はじめに
こちらは鈴鹿高専 Advent Calendar 2021の4日目の記事です.
注意事項としては,ここで記載されているコードを他のプロダクトでそのまま使うのは禁止とします.
何かバグがあっても責任を取れないためです.
また,実際に何かを制作する際には必ず一次情報(データシートなど)を参考にしてください.
作ったもの
アセンブラでリングバッファを作りました.
シリアル通信の受信バッファとして適しています.
いきなり内容が薄くなりましたが,元々自分のホームページで公開していたものに加筆修正を施したものです.
概要
先端と終端が論理的に繋がっていて古いデータは上書きされていきます.
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だと思います.
みなさんも是非使ってみてください.