LoginSignup
2
0

More than 1 year has passed since last update.

Z80でリングバッファの実装

Last updated at Posted at 2021-12-03

はじめに

こちらは鈴鹿高専 Advent Calendar 2021の4日目の記事です.

注意事項としては,ここで記載されているコードを他のプロダクトでそのまま使うのは禁止とします.

何かバグがあっても責任を取れないためです.

また,実際に何かを制作する際には必ず一次情報(データシートなど)を参考にしてください.

作ったもの

アセンブラでリングバッファを作りました.

シリアル通信の受信バッファとして適しています.

いきなり内容が薄くなりましたが,元々自分のホームページで公開していたものに加筆修正を施したものです.

概要

リングバッファは以下の図のようなリング状のバッファです.
circularbuffer.png

先端と終端が論理的に繋がっていて古いデータは上書きされていきます.

C言語などの高級言語での実装では先端と終端の結合に%演算子を使っていることが多いのですが,アセンブラ+Z80ではいろいろしんどい(除算器がない)ので大人しく条件分岐でインデックスを先端に戻す処理をかきます.

コード

BUFFER_SIZE     EQU     80H                     ;バッファーのサイズはここで指定
                ORG     5000H
;バッファーの初期化
;引数:  なし
;破壊:  なし
INIT_BUFFER:    PUSH    AF                      ;レジスタの退避
                PUSH    BC
                PUSH    DE
                PUSH    HL
                ;
                XOR     A                       ;ポインタの初期化
                LD      (BUFFER_READ),A
                LD      (BUFFER_WRITE),A
                LD      (BUFFER_LENGTH),A
                ;
                LD      HL,BUFFER               ;バッファー領域の初期化
                LD      DE,BUFFER+1
                LD      BC,BUFFER_SIZE-1
                LD      A,00H
                LD      (HL),A
                LDIR
                ;
                POP     HL                      ;レジスタの復帰
                POP     DE
                POP     BC
                POP     AF
                RET
;
;バッファーへのデータ追加
;引数:          D       追加するデータ
;破壊:          A,F
;返り値:        Z        T:失敗(空きがない)
;                       F:成功
SET_BUFFER:     PUSH    BC                      ;レジスタの退避
                PUSH    HL
                ;
                LD      A,(BUFFER_LENGTH)       ;バッファーに空きがあるか調べる
                LD      B,BUFFER_SIZE
                XOR     B
                JP      NZ,SET_BUFFER1
                POP     HL
                POP     BC
                RET
                ;
SET_BUFFER1:    LD      HL,BUFFER               ;アドレス算出
                LD      B,0
                LD      A,(BUFFER_WRITE)
                LD      C,A
                ADD     HL,BC
                ;
                LD      (HL),D                  ;データの追加
                ;
                LD      A,(BUFFER_LENGTH)       ;BUFFER_LENGTH++
                INC     A
                LD      (BUFFER_LENGTH),A
                ;
                INC     C                       ;BUFFER_WRITE++
                LD      A,C
                LD      (BUFFER_WRITE),A
                ;
                LD      B,BUFFER_SIZE           ;BUFFER_SIZE==BUFFER_WRITE?
                XOR     B
                POP     HL
                POP     BC
                RET     NZ                      ;BUFFER_SIZE!=BUFFER_WRITE
                XOR     A                       ;BUFFER_WRITE=0
                LD      (BUFFER_WRITE),A
                INC     A                       ;Zフラグを消す
                OR      A
                RET
;
;バッファーからのデータの取り出し
;引数:          なし
;破壊:          A,F
;返り値:        D       取り出したデータ
;              Z       T       失敗(空)
;                      F       成功
GET_BUFFER:     PUSH    BC                      ;レジスタの退避
                PUSH    HL
                ;
                LD      A,(BUFFER_LENGTH)       ;データが入っているか調べる
                LD      B,0
                XOR     B
                JP      NZ,GET_BUFFER1
                POP     HL
                POP     BC
                RET
                ;
GET_BUFFER1:    LD      HL,BUFFER               ;アドレスの算出
                LD      B,0
                LD      A,(BUFFER_READ)
                LD      C,A
                ADD     HL,BC
                ;
                LD      D,(HL)                  ;データの取り出し
                ;
                LD      A,(BUFFER_LENGTH)       ;BUFFER_LENGTH--
                DEC     A
                LD      (BUFFER_LENGTH),A
                ;
                INC     C                       ;BUFFER_READ++
                LD      A,C
                LD      (BUFFER_READ),A
                ;
                LD      B,BUFFER_SIZE           ;BUFFER_SIZE==BUFFER_READ?
                XOR     B
                POP     HL
                POP     BC
                RET     NZ                      ;BUFFER_SIZE!=BUFFER_READ
                XOR     A                       ;BUFFER_READ=0
                LD      (BUFFER_READ),A
                INC     A
                OR      A
                RET
;RAM AREA
                ORG     9000H
BUFFER_READ:    DEFS    01H
BUFFER_WRITE:   DEFS    01H
BUFFER_LENGTH:  DEFS    01H
BUFFER:         DEFS    80H

Z80のアセンブラは容易に読めるので,コメントと合わせれば理解は可能だと思います.

それなりに速度に気をつかってみたつもりですが最適化はまだまだできると思います.

実際の使用例

        ORG 0000H
MAIN:   CALL    INIT_BUFFER
        LD  B,4H
LOOP1:  LD  D,B
        CALL    SET_BUFFER
        DJNZ    LOOP1
        LD  B,5H
LOOP2:  LD  D,00H
        CALL    GET_BUFFER
        DJNZ    LOOP2
        HALT

こんな感じで所定のレジスタに引数を入れサブルーチンを呼び出すだけでデータの格納,取り出しが可能です.

MIDIの受信に使用するつもりで書きましたが,せっかくなので汎用性を持たせた設計にしました.

最後に

Z80は非常に優れたCPUだと思います.

みなさんも是非使ってみてください.

2
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0