今回は特に描画負荷が高いこともあって、BASIC ではすぐに速度的な限界が見えてしまいました。
一方で「マシン語は BASIC の 100 倍速」などと言われており、なんでも 100 倍速になるわけではないですが、誇張でもありません。200~300 倍速になることもあります。
ただ、PC-8001 実機や PasocomMini PC-8001 上でマシン語開発するのは非常に大変です。Windows 上で動作するエミュレータの利用を強く推奨します(実機を持っていない場合、ROM データの著作権問題がありますが……)。
なお、用語としては「マシン語」「機械語」「アセンブリ言語」 (「アセンブラ」と言う人も多いですが)などが使われますが、 ここでは「マシン語」で統一します。
PC-8001 の CPU は Z80(厳密には互換 CPU)で、詳細は「Z80 講座」で解説しています。まずはそちらを熟読してください。
アセンブラは自作で、Windows のコマンドプロンプトから実行することを想定しています。
他のアセンブラと文法的に違う部分もある (ラベルやマクロなど)ので注意してください。
16
進数表記は「~H
」も「$
~」も通りますが、
今回は解説も含めて「$
~」で統一します
(特にデータを記述する際、こちらの方が文字数が揃って便利なので)。
今回作成したのは、短い BASIC プログラムと、マシン語ソース 2
つです。3D 描画部分が長いので分けました(メインから
INCLUDE
しています。
PC-8001 のメモリ配置は、だいたい以下の図のようになっています (システム領域の中には使える部分もありますが、推奨しません)。
マシン語を使う際は、このうち「BASIC 領域」の末尾を
CLEAR
命令で狭める必要があります。たとえば $8FFF
までにするなら、
?fre(0) 26786 Ok clear100,&h8fff Ok ?fre(0) 3946 Ok
といった感じです。これで $9000~$E9FF
をマシン語で使用できます。
なお CLEAR
の第 1
パラメータは文字列領域のサイズ(詳しくはこちらを参照)です。ちなみに起動直後は 300
バイト確保されています。
オールマシン語でもいいのですが、実行は BASIC から RUN
するのがわかりやすいので、最低限の BASIC プログラムを作ります。
10 CLEAR 100,&H8FFF:WIDTH 40,25:CONSOLE 0,25,0,1:COLOR 7,0,0:PRINT CHR$(12) 20 DEF USR=&H9000:A=USR(0)
画面の初期化をマシン語でやるのは面倒なので、ついでに BASIC でやっておきます。
マシン語は $9000
から配置することにします。くれぐれもマシン語部分を用意する前に RUN
しないでください。
何が起こるかわかりません。
まずは BASIC にすぐ戻るだけのプログラムを書いてみます。
なお自作アセンブラ z80asm
用なので、他のアセンブラでは通らないかもしれません。
ORG $9000 ENTRY: RET PROG_END: END
ラベルは左端に記述し、 命令はタブもしくはスペースをいくつか入れて記述します。
最後の END
は無くてもいいですが、
ここで終了ということをわかりやすくするために入れています。
このプログラムをファイル名 test.z80s
で保存し、z80asm
をコマンドプロンプトなどから実行します。
.\z80asm test.z80s test.cmt -m test.map Input file : test.z80s Pass 2... Output file : test.cmt Output map file : test.map Complete.
これでカセットテープイメージ test.cmt
と、マップファイル test.map
が出力されます。test.map
の中身は、
9000 ENTRY 9001 PROG_END
このようになります。試しに test.cmt
をロード対象にして(具体的な手順は省略)、
mon *L (読み込み) *(CTRL+B) Ok
とした後、先ほどの BASIC プログラムを
RUN
してみて、画面初期化後すぐ
Ok
が出ることを確認してみてください。
z80asm
がサポートしている疑似命令(Z80
のコードではない命令)などについて軽く解説しておきます。
ORG
アドレスEND
EQU
定数式DB
データ列
もしくは DEFB
データ列DB $12,$34,$56 DB "Test1",0 DB 'Test2',0
DW
データ列
もしくは DEFW
データ列
DW $1234,$5678
DB $34,$12,$78,$56 (等価)
DS
バイト数
もしくは DEFS
バイト数INCLUDE
ファイル名@@,@F,@B
@0~@9,@0F~@9F,@0B~@9B
JR @F (前方の一番近い @@ に分岐) @@: JR @B (後方の一番近い @@ に分岐) JR @1F (前方の一番近い @1 に分岐) @1: JR @1B (後方の一番近い @1 に分岐)
MACRO
~
MEND
%1~%9
は引数に置き換え。WAIT MACRO LD B,%1 @@: DJNZ @B MEND ENTRY: WAIT 10
ENTRY: LD B,10 @@: DJNZ @B
BASIC 版で使用している数値変数を、 マシン語版ではソースの最後の方でまとめて確保することにします。
WORK_START:
PL_X: ; PX
DS 1
PL_Y: ; PY
DS 1
PL_Z: ; PZ
DS 1
PL_DIR: ; PD
DS 1
(中略)
WORK_END:
BASIC の整数変数は 16 ビット(2 バイト)ですが、8 ビットの範囲しか使っていないものは 1 バイトだけ確保しています。
数値変数は初期値 0 なので、マシン語版でも最初に、
ENTRY: LD HL,WORK_START LD DE,WORK_START+1 LD BC,WORK_END-WORK_START-1 LD (HL),0 LDIR
として、一気に 0 で埋めています(ただしこのコードはワークサイズが全部で 2 バイト以上無いと暴走するので注意)。
BASIC 版ではマップ要素の値を毎回プログラムに直書きしていましたが、
マシン語では EQU
疑似命令で定数を定義しておくことで、
途中で値の変更がしやすくなり、意味もわかりやすくなります。
MP_SPACE EQU 1 MP_WALL EQU 2 MP_OGATE EQU 3 MP_DSTAIRS EQU 4 MP_USTAIRS EQU 5 MP_START EQU 6 MP_WARP EQU 7 MP_TRE1 EQU 8 MP_TRE2 EQU 9 MP_CGATE EQU 32 MP_KEY EQU 64
今回はこれくらいしか定義しませんでしたが、BASIC と違って実行速度や実行プログラムサイズへの影響は無いので、 積極的に利用することをおすすめします。
文字列データや DATA
文の内容の多くは、各ソースの後ろの方に固めてあります。
たとえばメッセージ類は「MSG_
」
で始まる名前で定義してあります。
MSG_MOVES:
DB "Moves:",0
MSG_KEY:
DB "Key:",0
MSG_TREASURE:
DB "Treasure:",0
(中略)
MSG_START:
DB $C0,$B6,$D7,$D3,$C9,$A6,$20,$D0,$C2,$B9,$C3,$20,$D3,$C1,$B6,$B4
DB $DB,$B3,$21,0
MSG_START
の内容は、開始時のメッセージ「タカラモノヲ ミツケテ モチカエロウ!」です。ASCII
コード($20~$7E
)の範囲外を含む文字列データは、
このように文字コードを並べています(文字コード一覧はこちら)。
ここまでは下準備といったところです。 必要なワークやデータを先にまとめて用意しておくと、 コードを書くことに専念しやすいかと思います。
→マシン語版(後編)へ続く