読者です 読者をやめる 読者になる 読者になる

RAMが仲間になった!

電子工作 Z80

f:id:marlesan:20161106002630p:plain:w400

東芝SRAM、TC55257DPL-85Lです。これ1個で32kBあり、アクセスタイムも85nsとおそらく速い部類で、やはりZ80基準だと未来のデバイスなのでしょう。まぁ、気にせず組み込んでいきます。

ROMとRAMでチップが1個ずつだけあって、それぞれに32kBのアドレス空間を持たせたいので、アドレスデコードは単に最上位bitのA15を両者のCEに振り分ければよいですね。CEというのはチップイネーブル、あるいはCSでチップセレクトと呼ばれる信号で、メモリチップはこの信号がアクティブ(普通はLレベル)の時だけアクセスが可能です。

ROMのCE# =  A15 | MREQ#
RAMのCE# = !A15 | MREQ#

これでアドレスが0000H~7FFFH(A15が0)ならROM、8000H~FFFFH(A15が1)ならRAMへのアクセスとなります。MREQはZ80CPUがメモリアクセスを行うときにアクティブにする信号で、メモリがROM1個の時はこれとCEを直結するだけでOKでした。逆にメモリチップが多くなる場合、CPUから出るMREQとアドレスを元にどうやって各チップのCEを作り出すか(これをアドレスデコードと呼ぶ)がシステム設計の重要なポイントになるのだと思います。ここにさらに踏み込むと、Z80アドレス空間を超える容量のメモリを扱ったり(バンク切り替え)、I/Oポートをメモリのアドレス空間マッピングしたりなどが可能のようです。

プログラムは、RAMを無理やり使ってみる処理と、RAMがないと不可能な処理を試してみます。

PIOAD:  equ     0
PIOAC:  equ     1

RAM:    equ     0x8000
STACK:  equ     0xf000

        org     0
;
;       CPU初期設定
        ld      sp,STACK
;
;       PIO初期設定
        ld      a,0b11001111 ;ビットモード
        out     PIOAC,a
        ld      a,0b00001111 ;出力4bit/入力4bit
        out     PIOAC,a
;
;       サブルーチンをRAMに転送
        ld      hl,_DELAY ; 転送元アドレス
        ld      de,RAM    ; 転送先アドレス
        ld      bc,0xff   ; 転送サイズ
        ldir
;
;       Lチカ速度設定(入力ポートから読み込む)
SPEED:  equ     RAM + 0x100
        in      a,PIOAD
        sla     a
        sla     a
        sla     a
        sla     a
        or      0b00001111
        ld      (SPEED),a
        ld      a,0
;
;       LEDチカチカ本体
BLINK:  out     PIOAD,a
        inc     a
        call    DELAY
        jp      BLINK
;
;       遅延サブルーチン(RAMに置く)
DELAY:  equ     RAM
_DELAY: push    af
        ld      a,(SPEED)
        ld      b,a
_LOOP:  djnz    _LOOP
        pop     af
        ret

ちょっと長くなってきました。やってることは変わらずLチカです。

  • RAMを無理やり使ってみる処理
    • 点滅を遅延させる処理をRAM領域にコピーし、そこで実行されるようにした
  • RAMがないと不可能な処理
    • サブルーチン(スタックが使えないとリターンができない。また、コール元のレジスタ退避にもスタックを使う。スタックは当然、RAMが必要)
    • メモリへのデータ保存(起動時にPIO入力ポートの信号を読み、点滅速度設定としてRAM領域に保存)

アセンブルして、ROMに焼いて、電源投入。無事にチカチカしていますが、果たして想定通りの動作になっているのでしょうか。また実行速度を調べて確認してみます。

                        ;
                        ;       LEDチカチカ本体
0027: D300     [11]     BLINK:  out     PIOAD,a
0029: 3C       [15]             inc     a
002A: CD0080   [32]             call    DELAY
002D: C32700   [42]             jp      BLINK
                        ;
                        ;       遅延サブルーチン(RAMに置く)
8000:                   DELAY:  equ     RAM
0030: F5       [11]     _DELAY: push    af
0031: 3A0081   [24]             ld      a,(SPEED)
0034: 47       [28]             ld      b,a
0035: 10FE     [ 8|13]  _LOOP:  djnz    _LOOP
0037: F1       [18]             pop     af
0038: C9       [28]             ret

djnzがちょっとややこしいですね。これはBレジスタをデクリメントして0なら次の行(8クロック)、0以外なら相対ジャンプ(13クロック)という命令です。(SPEED)に保存されている設定値を 0xff だとすると、DELAYサブルーチン1回のコールは 28+13*254+8+20=3358クロック。明滅1回が (42+3358)*256=870400クロック。1クロック=250nsから秒に直して逆数を取ると秒間約4.6チカの点滅です。プログラムが正しく動いていれば、この速度でLチカしてるはず。

f:id:marlesan:20161106022932p:plain:w400

お見事!
楽しすぎますね、これ。

もちろんプログラムは1発動作なんてことはなく、ROMが10回くらいライタとボードを往復しました。RAMが加わって本格的にコンピューターっぽいものが出来上がってきたので、ソフトウェアの開発環境をどうするかという問題が浮上してきたように思います。ブートローダーを作ってトライ&エラーの時間を短縮するか、もう割り切ってエミュレーターで動作確認してしまうか…悩みどころです。