Coding Memorandum

プログラミングに関する備忘録

PC更新の構想

特に速度が遅いなどで困っているわけではないのでけど、今のPCを組んだのが2013年8月だったようで、気づけば7年も経過していた。もう変えた方がいい頃だよねとのことで、新しいPCの構成をざっと考えてみた。

CPU

AMD Ryzen 5 5600X

流行りのzen3にしてみる ¥40,000
マザーボード ASUS TUF GAMING X570-PLUS zen3対応のチップセットは下記
  X570 ハイエンド
  B550 ミドルレンジ
  A520 ローエンド
¥20,000
メモリ cruciel CT2K16G4DFD832A DDR4 3200 16GB×2 ¥15,000
グラフィックボード   GeForce GTX 1660 Super ¥28,000
SSD cruciel P1 CT1000P1SSD8JP NVMe SSD 1TB ¥10,000
HDD

WD60EZAZ-RT

6TB SATA ¥11,000
電源

ANTEC NeoECO Gold NE650G

650W Gold ¥9,500

締めて13万円強。もう少しスペックを落とすか悩むなぁ。

ゲームプログラミングの思い出

この記事は MSX Advent Calendar 2015 の最終日の記事です。

MSX Advent Calendarの最終日の記事は人が集まらなかった反省会ネタの予定でしたが、それも不要な状況になりましたので、本日は私のMSXの思い出を綴ってみようと思います。

私はソフトウェアのエンジニアとして生計を立てていますが、ご多分に漏れずにMSXとの出会いがこの道に進むきっかけでした。

この時代のパソコンユーザーは、雑誌や本に載っているプログラムを打ち込んで遊ぶことが一般的でした。ある意味、プログラムの写経をやらされているようなもので自然とプログラマーとして養成されていく環境にあったと言えます。

私もMSXを使い始めて数年経つころには、BASICだけではなくZ80アセンブラまでマスターし、何かまとまったプログラムを作ってみたいと思うようになりました。そんな中で、初めて作った(ゲーム)プログラムが次の動画のものです。

これを作ったときは、スーパーファミコンが発売された頃で、スーファミのローンチタイトルであったスーパーマリオワールドの最終面のスポットライト処理に感銘を受けて、同じような画面効果を作りたいと思って作成したものです。

このときはアセンブラを持ってなく、紙にアセンブリコードを書いて、MSXマガジンに載っていたワンラインアセンブラを使ってマシン語コードを作成していました。ワンラインアセンブラは、ニーモニックを1行ずつ打ち込んでマシン語コードに変換するツールでした。このやり方は、プログラムの修正でコードサイズが変わると大変なことになりました。当時は、Z80の相対ジャンプ命令を駆使して無理やり修正コードの領域を確保したように記憶しています。(今ではとてもやる気にならない作業です。若いときの情熱がすごい!)

ゲームの内容は、警備員の視線とスポットライトを避けながらキーを集めるゲームで、キーはスポットライトが当たらないと見えないというものでした。

このプログラムの開発では、とてつもないバグに悩まされた思い出があります。キャラクタ(スプライト)が画面中を縦横無尽に飛び回ってしまうバグで、紙のアセンブリコードを何回も机上デバッグしても原因が見つからず、何日も悩みました。

判明した原因は、VRAMへの書き込みをVDPへのI/Oポートアクセスで直接行っていたことが原因で、VRAMへの書き込み速度が速すぎたため、VRAMに書き込んだスプライトの位置データの値が化けるというものでした。VRAMへの書き込みをBIOS経由に変える(CALL命令のオーバヘッドが増える)ことであっさり直りました。プログラムのロジックには何の問題もなかったのです。このバグはプログラマ人生の中で一番記憶に残るバグです。

次に作ったゲームは、次の動画のゲームです。

最初のタイトル画面だけはBASICで、その先のゲーム本編はオールアセンブラの力作です。Z80アセンブラのコードをこちらに置きました。鑑賞用にどうぞ。

このゲームを作ったときは、プレイステーションが発売されていた時期で、リッジレーサーをやりながらドリフト(らしい動き)のアルゴリズムを考えて作成したものです。

ドリフト中の動きは、車の曲がる動きは通常通りであるが、車の向きを変える角速度が2倍になって、向きだけが先行して変わっていきます。カウンターステアをあてるとそのときの車の向きに直進するという処理にしたと記憶しています(実際に動かしながら確認もしました)。

動画をとるために久しぶりに遊んでみましたが、慣れてくると爽快感があって結構面白かったです(動画の4,5週目はノーミスで疾走しています)。もう少しゲームバランスを調整すればイケてるゲームにできたんじゃないかなと思います。

MSXからは離れますが、これらのゲーム作成の経験を踏まえて作ったゲームがxrot(X-Window)です。

このゲームも試したいアルゴリズム(図形回転アルゴリズム)があって作成したゲームでした。このプログラムでは、2重レイヤの画面描画、加速表現、一定の動作速度を保つ処理などに前出の2つのプログラムの技術が活かされています。

振り返ると、私のゲームプログラムは何か試してみたいアルゴリズムがあって、その検証のためにゲームを作るというスタイルになっていました。ゲームというよりはDemoに近いような感じがします。


MSX Advent Calendar 2015に参加してくださった皆様、ありがとうございました。Merry Christmas !

ラスタースクロール

この記事はMSX Advent Calendar 2015の7日目の記事です。

8bit,16bit CPU時代のゲーム機で流行ったゲームエフェクトにラスタースクロールというものがありました。水平走査線(画面の横1ピクセルライン)毎にスクロール位置を変えることで、画面がうねるような動きを表現します。ハード性能が向上すると、スクロールだけでなく拡大縮小のスケールを変えるなど、より凝った表現が行われるようになりました。(Google画像検索

ラスタースクロールについては、Wikipediaで非常に詳しく説明されています。

このラスタースクロールをMSXで実装してみました。ラスタースクロールの基本ロジックは、Wikipediaにもある通り次のようになります。

  1. ビデオチップが1ライン分の描画(VRAM読み込み)を終え、水平帰線期間(水平ブランキング期間)に入る。このとき、水平同期(HSYNC)信号が出される。
  2. CPUは、HSYNC信号で割り込み発生させる。
  3. プログラムは、HSYNC割り込み処理で横方向のスクロール位置を設定する(次に描画するラインのスクロール位置) 。
  4. 1~3の処理をスクロール位置を変えながら全ラインで実行する。

MSX2のビデオチップV9938では、コントロールレジスタ#19にHSYNC割り込みを発生させる走査線番号を指定することができます。これを使えばラスタースクロールを実現できそうですが、残念ながらこの方法では上手く行きません。

CPU(Z80)がHSYNC割り込みを受けると、アドレス038Hに実行場所が移ります。 ここから、MSXのシステムプログラムが実行されるため、ユーザプログラムに制御が移るまでにかなりの時間を要してしまいます。ユーザプログラムに制御が移るころには数ラインの描画が進行してしまい、スクロール位置の変更が間に合わない状況となります。

step3でスクロール位置設定を行った後に次の割り込み位置をR#19へ設定しますが、上述の状況ですので1ラインごとに割り込み処理を行わせることができません。こちらで試した結果では6ライン程の間隔で設定すれば動作させることができました。

1ラインのHSYNC毎にスクロール位置を変えるには、HSYNCを自前で検出する方法があります。水平帰線期間中は、V9938のステータスレジスタ#2のbit 5が1になります。このbitを監視して、HSYNCを検出することができます。(ただし、CPUを占有します)

これを実装したプログラムは次のようです。

TITLE 	Raster Scroll
	.Z80
	ASEG

RG0SAV	EQU	0F3DFH
RG18SA	EQU	0FFF1H
RG19SA	EQU	0FFF2H
RG25SA	EQU	0FFFAH
VDPIO	EQU	099H
HKEYI	EQU	0FD9AH

	ORG	09200H

START:
; Set IE1(Interrupt Enble1)
	DI
	LD	A,(RG0SAV)
	OR	010H
	LD	(RG0SAV), A
	OUT	(VDPIO),A
	LD	A,080H
	OUT	(VDPIO),A
; Set R19(Interrupt line register)
	LD	A,0
	LD	(RG19SA),A
	OUT	(VDPIO),A
	LD	A,093H
	OUT	(VDPIO),A
; Set R25
	LD	A,(RG25SA)
	OR	03H
	LD	(RG25SA),A
	OUT	(VDPIO),A
	LD	A,099H
	OUT	(VDPIO),A
; Set H.KEYI
	LD	HL,HKEYI
	LD	DE,HKEYISV
	LD	BC,5
	LDIR
	LD	HL,HKEYISET
	LD	DE,HKEYI
	LD	BC,5
	LDIR
	EI
	RET

ONKEYI:
	DI
	LD	C,VDPIO
	LD	H,093H
	LD	A,(L0OFFSET)
	INC	A
	LD	(L0OFFSET),A
	LD	L,A
	LD	B,0
; Read S1
	LD	A,01H
	OUT	(C),A
	LD	A,08FH
	OUT	(C),A
	IN	A,(C)
; Check FH Flag
	RRCA
	JR	NC,ENDKEYI
; Read S2
	LD	A,02H
	OUT	(C),A
	LD	A,08FH
	OUT	(C),A
; Wait HSYNC(HR:1->0->1)
L1:	IN	A,(C)
	AND	020H
	JR	NZ, L1
L2:	IN	A,(C)
	AND	020H
	JR	Z, L2

; Set Scroll => R18(Display adjust register)
	LD	A,1
	ADD	A,L
	AND	03FH
	LD	L,A
	LD	A,(HL)
	OUT	(VDPIO),A
	LD	A,092H
	OUT	(VDPIO),A
;
	INC	B
	LD	A, 211
	CP	B
	JR	C, ENDKEYI
	JR	L1

ENDKEYI:
	XOR	A
	OUT	(C),A
	LD	A,08FH
	OUT	(C),A
HKEYISV:
	DS	5
HKEYISET:
	JP	ONKEYI
	DS	2
L0OFFSET:
	DB	0FFH

	ORG	09300H
HSCROLL:
	DB	00H,01H,01H,02H,03H,03H,04H,04H,05H,05H,06H,06H,06H,07H,07H,07H
	DB	07H,07H,07H,07H,06H,06H,06H,05H,05H,04H,04H,03H,03H,02H,01H,01H
	DB	00H,0FH,0FH,0EH,0DH,0DH,0CH,0CH,0BH,0BH,0AH,0AH,0AH,09H,09H,09H
	DB	09H,09H,09H,09H,0AH,0AH,0AH,0BH,0BH,0CH,0CH,0DH,0DH,0EH,0FH,0FH

	END START

横方向のスクロール位置は、画面のAdjust機能(テレビ出力用に画面の位置を-8~+7の範囲で調整する機能)を用いています。 R#18に調整値を設定します。

実行結果は、次のようです。

画面の上部がズレているのは次の理由からです。Line 0のHSYNC割り込みを契機にラスタースクロールの処理を開始していますが、実際に処理が開始されるまで数ライン分の遅延があるため、ラスタースクロールの開始ラインが下にズレてしまったと考えられます。上部の数ラインは、一つ前のフレームの設定が反映されています。

ラスタースクロール開始の契機をVSYNC(垂直帰線期間)にすれば、このズレは抑えられると思います。プログラムを試行錯誤で作った名残&時間切れで、このような状態になっています。


MSX2+からは、ビデオチップ(V9958)で横方向スクロールをサポートしました。 ライン毎のスクロール位置指定に、このハードウェアスクロールを使ってみました。

ハードウェアスクロールは、R#26に8ピクセル単位のスクロール位置、R#27に1ピクセル単位のスクロール位置(0~7)を指定します。 残念ながらR#27を指定すると、画面が乱れてしまいました。これは、今回使ったエミュレータの問題か、V9958の性能面での問題かは分かっていません。

R#26のみ設定した場合は、期待通りに動作しました。実行画面は次のようになります。

映像Codec屋から見たYJKカラー

この記事はMSX Advent Calendar 2015の3日目の記事です。

YJKカラーとは

MSX2規格では、画面解像度256x212, 表示色256色のグラフィックモードがある。カラーが256色であるため1ピクセルを1バイトで表わし、グラフィック1画面に必要なVRAMサイズは256x212x1byte(64KB弱)である。
1pixelの表現はRGB形式でG(3bit), R(3bit), B(2bit)の構成である。

Fig1

後継のMSX2+規格では、画面解像度は同じ256x212で表示色が19268色のグラフィックモードが用意された。このモードがすごいのは、表示色が増えたにも関わらずVRAMサイズはMSX2規格と同じ256x212byteに抑えていることである。

VRAMのサイズを変えずに表示色を増やす魔法のような方法がYJKカラー方式である。YJKカラーは光の3原色を用いたRGBとは異なり、輝度(Y)と色差(J,K)を使う方式である。

輝度,色差信号

RGB信号と輝度・色差信号の関係の一般形は次のように表わされる。色の輝度成分(Y)はR,G,Bの1次式で表わされる。

k1,k2,k3の値は規格(後述)で決められる。 ちなみに、輝度=明るさであり、輝度成分(Y)のみの画像を作るとモノクロ画像になる。コンピュータビジョンの前処理として、輝度成分の抽出処理(=モノクロ化)がよく行われる。

色差はR,G,Bの各値から輝度を引いた値である。

色差信号3つのうち1つは、輝度と2つの色差があれば求められるので色差信号は2信号で表現する。

色差の値域は輝度に比べて大きくなるため、色差の値を正規化する表現もある。Yが0.0~1.0の間の値を取るときに色差が-0.5~+0.5の範囲になるように調整するなどである。

輝度・色差信号のメジャーな規格にITU-R BT.601、HDTV用の ITU-R BT.709 勧告がある。JPEGやMPEG形式で用いられるカラー信号である。規格書は次で入手できる。また、最近4K/8K用にITU-R BT.2020が勧告された。

https://www.itu.int/rec/R-REC-BT.601/en
https://www.itu.int/rec/R-REC-BT.709/en
https://www.itu.int/rec/R-REC-BT.2020/en

ITU-R BT.601では、Yと色差(Cb, Cr)を次の式で定める。

ITU-R BT.709では、次の式である。

MSX規格で用いるYJKは次のように定義されている。

各式の係数の分母が2の累乗になっているあたりは、ビデオチップ(V9958)の回路が作りやすいという理由があるような気がします。割り算命令を持っていないCPU Z80にとっても負担の少ない計算式です。

色差の間引き

RGB信号と輝度・色差信号は相互に変換することができる。RGB信号ではなく、輝度・色差信号を用いるメリットは情報の圧縮にある。
人の視覚は、輝度変化には敏感であるが、色差の変化には鈍感であるという特性がある。この特性を利用して色差信号を削減する。

一般的な色差信号の間引きの方式には、次のものがある。下記では色差信号はU,Vと表現する。

■ YUV 4:2:0

Fig2

2x2ピクセルに対して、一つの色差信号(U,V)を割り当てる。デジタル放送でも使われている方式であり、皆さんが普段テレビで目にする映像はこの形式の画である。

■ YUV 4:1:1

Fig3

4x1ピクセルに対して、一つ色差信号(U,V)を割り当てる。DVカメラで使われる方式である。

■ YUV 4:2:2

Fig4

2x1ピクセルに対して、一つ色差信号(U,V)を割り当てる。業務用/放送制作用で使われる(XDCAM HD422,XAVC,P2 AVC-Intra100)。


■ YJK

YJKは、YUV 4:1:1と同じ形で4x1ピクセルに対して1つのJ、K値を割り当てる。

Fig5


ちなみにアナログ放送で使われていたNTSCは、色差を調整して視覚感度の高いI信号と感度の低いQ信号に分け、色差信号の間で信号量に差を持たせている。Y:I:Q = 4.2:1.3:0.4

量子化ビット数

色信号(連続値:例えば0.0~1.0)をコンピュータで扱うためには、量子化(=デジタル化、符号化)が行われる。例えば、8bit=1byteで信号を符号化すると連続値0.0~1.0の値は0~255にマッピングされる。

この例における”8bit”を量子化ビット数と呼ぶ。量子化ビット数が大きいほど信号の分解能が高くなるがデータ量が増える。色信号の場合は、量子化ビット数が増えると中間色の表現が増えることになる。量子化ビット数が小さいと信号の分解能が低くなり、元信号との誤差が大きくなる。この誤差は量子化誤差と呼ばれる。

MSX2+規格のYJKのVRAM上での表現は次のようである。

Fig6

Y信号は5bit(0~31),J,K信号は6bit(-32~31)で符号化される。4pixelは4byteで表現され、1pixelあたり1byteで表現できている。
[参考] JPEG,MPEG-2,H.264はYUVの各値を8bitで扱っている。(H.264は10bit,JPEGは12bitで扱うモードもある)

色数はJ,Kが64階調であり、Yは32階調である。これらの値を組み合わせると64x64x32=131072色が表現可能である。しかしながら、ビデオチップの中でビデオ信号出力のためにYJKからRGBへの変換が行われ、ここでRGBはそれぞれ5bitで扱われてしまう。このため、色数が32768色(32x32x32)となるが、さらに色の重複を除くと19268色となる。この辺りは下記の資料が詳しい。

http://marmsx.msxall.com/artigos/sc12en.pdf

YJKカラーを使う

YJKカラーを出力するプログラム(MSX-BASIC)を作成してみた。プログラムは、RGB 24bit(RGBの各値を8bitで表わす)のBitmapファイルを読み込んでMSX2+のSCREEN12に出力するものである。Bitmapファイルは256x212のサイズのものである。
行20にBitmapファイル名を指定する。

10 SCREEN 12
20 OPEN "mandrill.BMP" AS #1 LEN=6
30 FIELD #1, 1 AS B1$, 1 AS G1$, 1 AS R1$, 1 AS B2$, 1 AS G2$, 1 AS R2$
40 P = 0
50 FOR Y = 0 TO 211
60 FOR X = 1 TO 128
70 GET #1, 9+(211-Y)*128+X
80 R1 = ASC(R1$) : G1 = ASC(G1$) : B1 = ASC(B1$)
90 R2 = ASC(R2$) : G2 = ASC(G2$) : B2 = ASC(B2$)
100 X = X+1 : GET #1, 9+(211-Y)*128+X
110 R3 = ASC(R1$) : G3 = ASC(G1$) : B3 = ASC(B1$)
120 R4 = ASC(R2$) : G4 = ASC(G2$) : B4 = ASC(B2$)
130 Y1 = B1/2 + R1/4 + G1/8 : J1 = R1 - Y1 : K1 = G1 - Y1
140 Y2 = B2/2 + R2/4 + G2/8 : J2 = R2 - Y2 : K2 = G2 - Y2
150 Y3 = B3/2 + R3/4 + G3/8 : J3 = R3 - Y3 : K3 = G3 - Y3
160 Y4 = B4/2 + R4/4 + G4/8 : J4 = R4 - Y4 : K4 = G4 - Y4
170 Y1 = INT((Y1+4)/8)*8
180 Y2 = INT((Y2+4)/8)*8
190 Y3 = INT((Y3+4)/8)*8
200 Y4 = INT((Y4+4)/8)*8
210 J = (J1+J2+J3+J4)/4 : K = (K1+K2+K3+K4)/4
220 J = INT((J+4)/8) : K = INT((K+4)/8)
230 IF J > 31 THEN J = 31 ELSE IF J < -32 THEN J = -32
240 IF K > 31 THEN K = 31 ELSE IF K < -32 THEN K = -32
250 JL = J AND 7 : JH = INT(J / 8) AND 7
260 KL = K AND 7 : KH = INT(K / 8) AND 7
270 Y1 = Y1 OR KL : Y2 = Y2 OR KH : Y3 = Y3 OR JL : Y4 = Y4 OR JH
280 VPOKE P, Y1 : P = P + 1
290 VPOKE P, Y2 : P = P + 1
300 VPOKE P, Y3 : P = P + 1
310 VPOKE P, Y4 : P = P + 1
320 NEXT X
330 NEXT Y
340 CLOSE #1
350 A$ = INPUT$(1)

標準画像として良く使われるLennaとMandrillの画像の表示結果は次のようである。

元画像(RGB 24bit)

YJK画像

LennaLenna_SC12

元画像(RGB 24bit)

YJK画像

MandrillMandrill_SC12

YJK画像の方は、量子化ビット数が低いため量子化誤差が目立っている。特に輝度・色が大きく変わる物体の境界部分で誤差が目立つ。色の再現度は思っていた以上に高いと思う。

J,Kを0とし、Y成分のみを描画した画像は次のようである。

YJK画像(Yのみ)

Lenna_SC12_Y

YJKでは、Y成分に青(B)成分が入ってしまうため、少し青みがかった画像となっている。Y成分は1ピクセル毎に値を持つため、物体の境界部分ははっきりと表現できている。しかしながら、32階調であるため背景のグラデーション部分で量子化の誤差が目立ってしまっている。

Color cycling

この記事はMSX Advent Calendar 2015の2日目の記事です。

昨日紹介したDemo「Illusion」の中で、パレットアニメーションと思われるシーンがありました。3:22ぐらいのところです。

この手法は、あらかじめ異なるカラー値で図形・線を描画しておき、カラーパレットを変えることで動きを見せる技法です。少し調べてみると、この技法は「Color Cycling」と呼ばれているようです。

「Illusion」と同じような動きをするプログラムを作ってみました。次の動画をご覧ください。

このプログラムでは、敢えて下準備の描画処理を見せています。プログラムは次のようです。

10 SCREEN 5
20 COLOR ,,0 : LINE(0,0)-(255,211),0,BF
30 C=1
40 'Draw lines
50 FOR Y=105 TO 0 STEP -1
60 LINE (Y+22,Y)-(234-Y,Y),C
70 LINE (Y+22,211-Y)-(234-Y,211-Y),C
80 LINE (Y+22,Y)-(Y+22,211-Y),C
90 LINE (234-Y,Y)-(234-Y,211-Y),C
100 C=C+1 : IF C > 15 THEN C = 1
110 NEXT Y
120 A$ = INPUT$(1)
130 'Initialize color palette
140 FOR C=1 TO 8 : COLOR=(C,0,0,0) : NEXT
150 FOR C=9 TO 15 : COLOR=(C,7,7,7) : NEXT
160 'Color cycling
170 W=1 : B=9
180 FOR I=0 TO 209
190 COLOR=(B, 0, 0, 0)
200 COLOR=(W, 7, 7, 7)
210 W=W+1 : IF W > 15 THEN W = 1
220 B=B+1 : IF B > 15 THEN B = 1
230 NEXT I
240 A$ = INPUT$(1)

50-110行で、15色を使って線を書きます。ここでは標準のカラーパレットで線が描かれます。
140-150行で、カラー番号1-8を黒、カラー番号9-15を白にカラーパレットを変えています。

170-230行は、カラーパレットの白と黒の割り当てを変えています。
1回目のループ:黒2-9、白1,10-15
2回目のループ:黒3-10、白1-2,11-15
3回目のループ:黒4-11、白1-3,12-15

このようにすることで、白の帯が動いているように見えます。ピクセルの描画処理を伴わないため、CPU/ビデオチップへの負荷が軽く、BASICプログラムでも軽快に動作します。

余談ですが、「Color Cycling」で検索するとHTML5で作られた素晴らしい作品が見つかります。
Old School Color Cycling with HTML5

「Show Option」ボタンを押すとパレットの色の動きを見ることができ、Color Cyclingの仕組みがよく分かると思います。

次のページ