Z80アセンブラでFizzBuzz

レジスタ内容をダンプするシステムコール云々などと言ってましたが、もうちょっと複雑なアセンブラの動作確認がどうしてもしたくなって、BUSRQしてRAMを見にいく方法をさくっと試してみました。

今回のプログラムは遂にLチカを卒業して、FizzBuzzをやってみます。RAMに1からカウントアップする数字を書いていって、

  • 3の倍数の時は 0xff (Fizz)
  • 5の倍数の時は 0xbb (Buzz)
  • 3の倍数かつ5の倍数の時は 0xfb (FizzBuzz)

としつつ100(0x64)まで数えます。

STACK:  equ     0xf000
        org     0x0000
        ld      sp,STACK

        ; Turn off LED
        ld      a,0
        out     0,a

        ; Do FizzBuzz
        ld      b,100
        ld      hl,0xc000
        call    FIZBUZ

        ; Turn on LED
        ld      a,1
        out     0,a
        halt

        ; FizzBuzz counter
        ; [parameters]
        ;  B  -- a number upto which you want to play FizzBuzz
        ;  HL -- starting address to record the result
        ; [result]
        ;  The counted numbers are recorded in RAM from HL.
FIZBUZ: ld      a,0
_LOOP:  inc     a
        ; Is A divisible by 15?
        ld      d,0x0f
        call    CANDIV
        jp      nz,_DIV3
        ld      c,0xfb  ; Let's say FIZZBUZZ!
        jp      _DUMP
        ; Is A divisible by 3?
_DIV3:  ld      d,0x03
        call    CANDIV
        jp      nz,_DIV5
        ld      c,0xff  ; Let's say FIZZ!
        jp      _DUMP
        ; Is A divisible by 5?
_DIV5:  ld      d,0x05
        call    CANDIV
        jp      nz,_NOP
        ld      c,0xbb  ; Let's say BUZZ!
        jp      _DUMP
_NOP:   ld      c,a     ; Say it as it is.
_DUMP:  ld      (hl),c
        inc     hl
        djnz    _LOOP
        ret

        ; CANDIV tests if A is divisible by D.
        ; [parameters]
        ;  A -- dividend
        ;  D -- divisor
        ; [result]
        ;  F -- Z == 1 if divisible
CANDIV: ld      e,a
_SUB:   sub     d
        jr      z,_EXIT
        jp      nc,_SUB
_EXIT:  ld      a,e
        ret

どうってことない処理ですが、CANDIV(割り切れるか?)の実装はちょっと時間かかりました。 はじめは % (mod演算)を実装しようとしてたんですね。 すると余りを出すのに、ボローが出たら除数を足すか、計算過程を1回分キャッシュするかというわちゃっとした解法しかすぐに浮かばなくて悩みました。 でもよく考えたら「割り切れるかどうか」だけ分かればいいので、Aがゼロになるかボローが出るかを見るだけでいいですよね。 これならまぁ、正解に近い感じがします。

さて、上記コードをアセンブルして実行すると0xc000番地からFizzBuzzの結果が書き込まれるはずです。 前回までは「アセンブルした機械語Arduinoファームウェアのソースにuint8の配列として埋め込む」という原始的な方法でプログラミングしていましたが、 今回はCUIソフトも作ったので、転送→実行→停止→確認がPC側から全部行えるようになりました。

以下、その操作ログです。

$ ./loader.rb 
requesting connection..
connected!
MAX_BLOCK_SIZE: 96
erasing RAM data...done in 0.10248114200021519 sec.
cmd?> wfile fizzbuzz.rom
programming the romfile...done in 0.012214564000259998 sec.
reading back the romfile to verify...done in 0.020541404000141483 sec.

SUCCEEDED!!

dumped the readback image to "_fizzbuzz.rom"
cmd?> reset
let the board...GO!
cmd?> busrq
requesting bus control...accepted!
cmd?> dump c000 100
01 02 ff 04 bb ff 07 08 ff bb 0b ff 0d 0e fb 10 
11 ff 13 bb ff 16 17 ff bb 1a ff 1c 1d fb 1f 20 
ff 22 bb ff 25 26 ff bb 29 ff 2b 2c fb 2e 2f ff 
31 bb ff 34 35 ff bb 38 ff 3a 3b fb 3d 3e ff 40 
bb ff 43 44 ff bb 47 ff 49 4a fb 4c 4d ff 4f bb 
ff 52 53 ff bb 56 ff 58 59 fb 5b 5c ff 5e bb ff 
61 62 ff bb 
cmd?> exit
$

GOOD!