マシン語を使って画面に文字やグラフィックを表示する基本的な処理を解説します。
このスクショは、0xF300 から 0x40, 0x41, 0x42, 0x43, 0x44, 0x45 と Sコマンドを使って書き込んだ結果です。画面の一番左上から @ABCDE と表示されている事が見えるかと思います。PC-8001 はこのように VRAM エリアに文字コードを書き込むだけで、画面に文字が出ます。
これで 0xF300 から文字コードを書き込むと、何かしら文字が出る事が分かりましたので、続いてマシン語で試してみましょう。画面は事前に初期化してある前提とします。
左半分が緑色、右半分が赤色になったのが見えるかと思います。0xF350 が一番上の行のアトリビュートの開始アドレスですので、そこから 0x00,0x88 で 0文字目(左端)に属性コード 0x88 を、続いて 0x03, 0x48 で3文字目に赤色を設定しています。
左からの位置は必ず左に設定した値より大きくします。 00,88,03,48,02,88 と書くと動作は保証されません。書き込む属性コードは以下のような意味になっています。
※ アトリビュート制御は高速に処理しようとするとかなりの手間がかかります。
では、マシン語でアトリビュートを書き換えてみましょう。
- 画面はどこにある?
- 画面を初期化する
BASIC で、CONSOLE 0,25,0,1:WIDTH 80,25:COLOR 7,0,0として 80文字モードにするのとほぼ同じ初期化をマシン語で行う一番簡単な方法は BASIC ROM内ルーチンを呼び出す事です。私は、特によく使う一部の ROMルーチンアドレスを以下のように定義しています。
実際の画面の初期化は以下のようになります。ついでに SP も初期化してしまいます。私は SP = 0x0000 に設定する事が多いです。理由はだいたい空いているためです。
BIOS:
.NBASIC_VER equ 0x1850 ; N-BASIC Version番号
.FUNC_COLOR equ 0x08F7 ; Function Key On/Off、カラーモノクロ指定
.WIDTH equ 0x093A ; CRT 画面表示文字数の設定
.CURSOR_OFF equ 0x0BD2 ; カーソル消去
.CMT_OPEN_R equ 0x0BF3 ; CMT 読み込み開始
.CMT_READ equ 0x5F9E ; CMT 1バイト読み込み(or $0C88)
.CMT_CLOSE equ 0x0C2E ; CMT 終了
.MONITOR equ 0x5C66 ; モニターモードに戻る
ld sp, 0
call BIOS.CURSOR_OFF ; カーソル消去
ld bc, $00FF
call BIOS.FUNC_COLOR ; ファンクションキーを消してカラーに
ld bc, 80 * 256 + 25
call BIOS.WIDTH ; CRT 画面表示文字数の設定
- まずはともかく文字を出す
このスクショは、0xF300 から 0x40, 0x41, 0x42, 0x43, 0x44, 0x45 と Sコマンドを使って書き込んだ結果です。画面の一番左上から @ABCDE と表示されている事が見えるかと思います。PC-8001 はこのように VRAM エリアに文字コードを書き込むだけで、画面に文字が出ます。
これで 0xF300 から文字コードを書き込むと、何かしら文字が出る事が分かりましたので、続いてマシン語で試してみましょう。画面は事前に初期化してある前提とします。
ここで使っているのは、汎用レジスタ H,L,B とアキュムレーター Acc です。ld (hl),a は、HL が VRAMアドレスを指し示していますので、そこに Acc の値を書き込む動作をしています。inc が +1 の動作、dec が -1 の動作をします。そして、dec b の結果が 0 になれば ZF=1 とフラグが変化します。jr nz,.loop は「事前の計算結果がゼロじゃなければ.loopにジャンプ」という動作となります。これで1行分繰り返して動作するようになります。
org 0x9000 ; 実行開始アドレス
ld b, 80 ; 80文字だけ書き込む
ld hl, 0xF300 ; HL をVRAMアドレスに設定する
ld a, 0x00 ; Acc を 0x00 にする
.loop ld (hl), a ; HL を示す場所に Acc の内容を書き込む
inc hl ; VRAMアドレスを + 1 する
inc a ; 書き込む値を +1 する
dec b ; 描画カウンタを -1 する
jr nz, .loop ; ゼロじゃなければ繰り返す
ret ; 終了する(エラーで止まる)
- アトリビュート
左半分が緑色、右半分が赤色になったのが見えるかと思います。0xF350 が一番上の行のアトリビュートの開始アドレスですので、そこから 0x00,0x88 で 0文字目(左端)に属性コード 0x88 を、続いて 0x03, 0x48 で3文字目に赤色を設定しています。
左からの位置は必ず左に設定した値より大きくします。 00,88,03,48,02,88 と書くと動作は保証されません。書き込む属性コードは以下のような意味になっています。
先ほど手入力した 0x88 は、ATRB.GREEN なので、画面の色が緑色になったというわけです。途中で緑色の範囲が広がってから後方の赤色が表示されていますが、このようなタイミングが悪いと書き換え途中が見えてしまう事があります。これが目立つとゲーム中でちらつきとして見えて0しまいます。防止するには一気に書き換えるか、垂直帰線が裏に行っている時を狙って書き換えるかとなります。
ATRB:
.BLACK equ %00001000 ; 黒
.BLUE equ %00101000 ; 青
.RED equ %01001000 ; 赤
.MAGENTA equ %01101000 ; 紫
.GREEN equ %10001000 ; 緑
.CYAN equ %10101000 ; 水
.YELLOW equ %11001000 ; 黄
.WHITE equ %11101000 ; 白
※ アトリビュート制御は高速に処理しようとするとかなりの手間がかかります。
では、マシン語でアトリビュートを書き換えてみましょう。
今回は実行速度を気にしてブロック転送を使ってみました。実は4バイト転送であれば、BC に転送サイズを入れたりせず LDI 命令を並べて記述してしまう事が殆どです。理由は断然こちらの方が高速に動作するためです。
org 0x9100 ; 執行開始アドレス
ld hl, .atrb ; HL データの位置
ld de, 0xF350 ; DE 書き換えるアトリビュートアドレス
ld bc, 4 ; BC 書き換えるサイズ
ldir ; (HL)→(DE) に BC サイズ分ブロック転送
ret
.atrb db 0x00, 0x88, 0x03, 0x48
殆どのアセンブラには、マクロ展開が実装されています。それを使った記述は以下の通りとなります。※ 本内容は tools80 に準拠しています。
org 0x9100 ; 実行開始アドレス
ld hl, .atrb ; HL データの位置
ld de, 0xF350 ; DE 書き換えるアトリビュートアドレス
ldi ; 転送1回目
ldi ; 転送2回目
ldi ; 転送3回目
ldi ; 転送4回目
ret
.atrb db 0x00, 0x88, 0x03, 0x48
org 0x9100 ; 実行開始アドレス
ld hl, .atrb ; HL データの位置
ld de, 0xF350 ; DE 書き換えるアトリビュートアドレス
REPT 4 ; 4回展開する
ldi ; 1バイトブロック転送
ENDM ; ここまでを展開する
ret
.atrb db 0x00, 0x88, 0x03, 0x48
上記3つのプログラム全てで動作結果は同じとなります。
ANYCUBIC
コメント