UbuntuでZ80アセンブラを書く
選択肢は色々あるようですが、次のサイトの zasm というZ80専用アセンブラを使うことにしました。
オプションでCPUサイクル付きのリストが出せるのが面白そうです。導入方法は、Archiveから実行バイナリをダウンロードして動くならそれが一番簡単です。私の環境(Ubuntu 16.04.1 LTS 64bit)は zasm-4.0.15-Linux64.zip が問題なく使えそうですが、一応ビルドも試したので、手順をメモしておきます。
$ cd ~/workshed/gitprojects /* 適当な作業フォルダ */ $ git clone http://k1.spdns.de/Git/zasm-4.0.git $ git clone http://k1.spdns.de/Git/Libraries.git $ cd zasm-4.0 $ ln -nfs ../Libraries Libraries $ cd Linux $ make $ sudo cp zasm /usr/local/bin
早速、簡単なプログラムをアセンブルしてみます。
org 0000h ld a,0 INC: inc a jp nz,INC LOOP: jp LOOP
これを test.asm として保存して…
$ zasm -uwy test.asm assemble: 6 lines time: 0.0202 sec. zasm: no errors $
問題なさそうです。test.lst と test.rom ができています。.rom が機械語のバイナリで、これをROMに焼いてZ80に実行させます。使ったオプションの意味は次の通り(作者のオススメ組み合わせっぽい)
- -u ... オブジェクトコード(機械語を16進数表記したもの)を .lst に含める
- -w ... ラベルのリストを .lst に含める
- -y ... CPUのクロックサイクルを .lst に含める
では test.lst がどうなっているか見てみましょう。
$ cat test.lst ; -------------------------------------- ; zasm: assemble "test.asm" ; date: 2016-11-03 03:26:01 ; -------------------------------------- 0000: org 0000h 0000: 3E00 [ 7] ld a,0 0002: 3C [ 4] INC: inc a 0003: C20200 [14|14] jp nz,INC 0006: C30600 [10] LOOP: jp LOOP ; +++ segments +++ #CODE : start=0 len=9 ; +++ global symbols +++ INC = $0002 = 2 test.asm:3 LOOP = $0006 = 6 test.asm:5 total time: 0.0199 sec. no errors $
例えば ld a,0
は 3E 00
という機械語にアセンブルされています。間にある数字がその行の命令の実行時間(クロック数)のようです。これ、Z80を手動クロックで動かす実験をしたおかげでよく理解できます。まず、各命令は必ずM1サイクルから始まります。ここでオペコード(命令の種類を示す)をメモリから読み出して命令の実行準備を整えるのですが、基本4クロックかかります。その後、命令によって次のどれかのサイクルに進みます。
- M1のうちに実行完了(5クロック以上かかることもある)
- もう1回M1サイクル(Z80で追加された新しい命令)
- メモリ・リード・サイクル(3クロック)
- メモリ・ライト・サイクル(3クロック)
- I/O・リード・サイクル(4クロック)
- I/O・ライト・サイクル(4クロック)
- メモリ・I/Oサイクルは必要数繰り返す
ld a,0
なら、3E
で「アキュムレータ(Aレジスタ)に値をロードする」という命令を判別するのがM1サイクルで4クロック、ロードする値 00
をメモリ・リード・サイクルで取ってくるのに3クロック使って計7クロック。inc a
は 3C
だけで「アキュムレータの値を1増やす」と判別して実行するのでM1サイクルの4クロックで終了、という具合です。
jp LOOP
ではジャンプ先アドレスを2バイトで指定するため、メモリ・リード・サイクルが2回必要です。つまり4+3+3=10クロック必要。残った jp nz,INC
の [14|14]
は、分岐ジャンプでは分岐する時としない時でクロック数が変わる場合があるためこのような表記になっているのだと思いますが、14という数字自体が不思議です。手元の本でもググってみても jp nz,nn
は [10|10]
となっています。なにか必要な設定が抜けてるのだろうか…。とりあえず、吐かれるバイナリ自体は間違ってないようなのでよしとしますか。この点も確認しときましょう。
$ od -t x1 test.rom 0000000 3e 00 3c c2 02 00 c3 06 00 0000011 $
オッケー!
というわけで、遂にZ80のシステムを実際に組んでいきます! まずは、CPU・ROM・PIOの最小構成を試す予定。
追記
jp nz,INC
のクロックサイクルは [10|10]
じゃないの?問題について。test.asm にちょっと手を加えてアセンブルしてみたら [14|14]
の理由がわかりました。以下、リストファイルの抜粋
0000: org 0000h 0000: 3E00 [ 7] ld a,0 0002: 3C [ 4] INC: inc a 0003: 3C [ 8] inc a 0004: 3C [12] inc a 0005: 3C [16] inc a 0006: 3C [20] inc a 0007: 3C [24] inc a 0008: 3C [28] inc a 0009: 3C [32] inc a 000A: C20200 [42|42] jp nz,INC 000D: C30D00 [10] LOOP: jp LOOP
カッコ内の数字はその行の実行サイクルではなくて、ラベル間の実行サイクルを足し合わせながら出力される値のようです。