最近ツイッターで、ジョイスティックポートを使った、RS-232C通信が流行っている(?)ので、流行に乗ってみました。
P6のジョイスティック入出力ポートを使って、RS-232C通信を行うためのハードウエアなどは、MORIYAさんのページに詳しく書かれています。
http://p6ers.net/mm/pc-6001/dev/joy2rs232c/ また、MSXでは、TINY野郎さんのページにも書かれています。
http://www.tiny-yarou.com/joyjoy_fs.html 同じ事をやっても面白くないので、限界に挑戦してみることにしました。
PC -> P6 のデータレートは、57600bps で動いているのが最速なようですが、115200bps が動きました。
PC -> P6 への転送のみのプログラムです。プログラムは、MODE1-5、PAGE1-4で動作しますが、mk2SR/66SRではCPUクロックが違うために動作しません。
joy232c.zip ハードウエアは、上記のどちらのものを使っても動作します(RTS/CTSを使っていないため)。
PC側の送信の設定は、57600bps or 115200bps、ストップビット1ビット、パリティなし、データ8ビットにして下さい。
アセンブラソースは以下の通りです。
======================================================================
relaxed on
org $d000
jp WRITEDT_115K2 ; 0xD000:データ書き込み(115200bps)
jp WRITEDT_57K6 ; 0xD003:データ書き込み(57600bps)
org $d010
WRSTART: dw $0000 ; 書き込み先頭
WRSIZE: dw $0000 ; 書き込みサイズ
;
; データ書き込み
;
WRITEDT_115K2:
call INIT ; 初期化
ld hl,(WRSTART)
ld bc,(WRSIZE)
call RCVDT_115K2 ; データ受信&書き込み(115200bps)
call PS_PRO ; 後処理
ret
;
; データ書き込み
;
WRITEDT_57K6:
call INIT ; 初期化
ld hl,(WRSTART)
ld bc,(WRSIZE)
call RCVDT_57K6 ; データ受信&書き込み(57600bps)
call PS_PRO ; 後処理
ret
;
; データ受信&書き込み
; ・エラーチェックなし
; ・スタート、ストップ1ビット
; ・パリティなし
; ・ボーレート 115200bps(1ビット34.67clk@3.9936MHz)
; ・ボーレート 115200bps(1ビット34.72clk@4.0000MHz)
;
;
; 設計思想
; ・スタート、ストップとも1ビットのため、全体で10ビット
;
; ・1ビットのデータは、35クロック弱
;
; ・10ビット全体
; スタートビットをすぐに見つける
; 8ビットデータを取り込む
; データ格納、サイズチェックをして最初に戻る
; までを、346クロック以内に終了させる(10ビットでデータ全体が346.7/347.2clkのため)
;
; ・スタートビット検出と、最初のデータ取り込みのタイミングは
; 36クロック以上にする
; (35クロックにした場合、スタートビットがぎりぎり取れた時に
; 最初のビットを取り損なう可能性があるため)
;
;
; BC:データサイズ
; HL:書き込みアドレス
;
RCVDT_115K2:
ld (.sppt+1),sp ; ダミー命令を使うために、SPを退避する
.lp1:
in a,($a2) ; 11+2 / 13 受信ビット(bit0)(PSGは+1waitされる)
rrca ; 4+1 / 18
jp c,.lp1 ; 10+1 / 29 スタートビットを待つ
ld d,$00 ; 7+1 / 37
; 37 / 37
in a,($a2) ; 11+2 / 13 受信ビット(bit0)(PSGは+1waitされる)
rrca ; 4+1 / 18
rr d ; 8+2 / 28
ret c ; 5+1 / 34 時間調整用
in a,($a2) ; 11+2 / 13 受信ビット(bit0)(PSGは+1waitされる)
rrca ; 4+1 / 18
rr d ; 8+2 / 28
ld sp,hl ; 6+1 / 35 時間調整用
in a,($a2) ; 11+2 / 13 受信ビット(bit0)(PSGは+1waitされる)
rrca ; 4+1 / 18
rr d ; 8+2 / 28
ld sp,hl ; 6+1 / 35 時間調整用
in a,($a2) ; 11+2 / 13 受信ビット(bit0)(PSGは+1waitされる)
rrca ; 4+1 / 18
rr d ; 8+2 / 28
ret c ; 5+1 / 34 時間調整用
in a,($a2) ; 11+2 / 13 受信ビット(bit0)(PSGは+1waitされる)
rrca ; 4+1 / 18
rr d ; 8+2 / 28
ld sp,hl ; 6+1 / 35 時間調整用
in a,($a2) ; 11+2 / 13 受信ビット(bit0)(PSGは+1waitされる)
rrca ; 4+1 / 18
rr d ; 8+2 / 28
ld sp,hl ; 6+1 / 35 時間調整用
in a,($a2) ; 11+2 / 13 受信ビット(bit0)(PSGは+1waitされる)
rrca ; 4+1 / 18
rr d ; 8+2 / 28
ret c ; 5+1 / 34 時間調整用
; 242 / 279
in a,($a2) ; 11+2 / 13 受信ビット(bit0)(PSGは+1waitされる)
rrca ; 4+1 / 18
rr d ; 8+2 / 28
ld (hl),d ; 7+1 / 36
cpi ; 16+2 / 54 HL<=HL+1 / BC<=BC-1 で、BC=0の時、P/V=0
jp pe,.lp1 ; 10+1 / 65
;
; ↑ストップビットの間に処理を済ませる
; 65 / 344
.sppt: ld sp,$0000
ret
;
; データ受信&書き込み
; ・エラーチェックなし
; ・スタート、ストップ1ビット
; ・パリティなし
; ・ボーレート 57600bps(1ビット69.33clk@3.9936MHz)
; ・ボーレート 57600bps(1ビット69.44clk@4.0000MHz)
;
; BC:データサイズ
; HL:書き込みアドレス
;
RCVDT_57K6:
ld (.sppt+1),sp ; ダミー命令を使うために、SPを退避する
.lp1:
in a,($a2) ; 11+2 / 13 受信ビット(bit0)(PSGは+1waitされる)
rrca ; 4+1 / 18
jp c,.lp1 ; 10+1 / 29 スタートビットを待つ
; ここで、本来はデータの中心まで待つ必要があるのだが、
; データ幅(=69)に対して、サンプリング間隔(=29)が大きいので、
; 適当にウエイトを入れる
; ↓
ld a,b ; 4+1 / 34 時間調整用のダミー命令
; スタートビット分待つ
in a,($a2) ; 11+2 / 13 時間調整用のダミー命令
inc hl ; 6+1 / 20 時間調整用のダミー命令
dec hl ; 6+1 / 27 時間調整用のダミー命令
inc hl ; 6+1 / 34 時間調整用のダミー命令
dec hl ; 6+1 / 41 時間調整用のダミー命令
inc hl ; 6+1 / 48 時間調整用のダミー命令
dec hl ; 6+1 / 55 時間調整用のダミー命令
inc hl ; 6+1 / 62 時間調整用のダミー命令
dec hl ; 6+1 / 69 時間調整用のダミー命令
REPT 7
in a,($a2) ; 11+2 / 13 受信ビット(bit0)(PSGは+1waitされる)
rrca ; 4+1 / 18
rr d ; 8+2 / 28
in a,($a2) ; 11+2 / 41 時間調整用のダミー命令
inc hl ; 6+1 / 48 時間調整用のダミー命令
dec hl ; 6+1 / 55 時間調整用のダミー命令
inc hl ; 6+1 / 62 時間調整用のダミー命令
dec hl ; 6+1 / 69 時間調整用のダミー命令
ENDM
in a,($a2) ; 11+2 / 13 受信ビット(bit0)(PSGは+1waitされる)
rrca ; 4+1 / 18
rr d ; 8+2 / 28
ld (hl),d ; 7+1 / 8
cpi ; 16+2 / 26 HL<=HL+1 / BC<=BC-1 で、BC=0の時、P/V=0
jp pe,.lp1 ; 10+1 / 37
;
; ↑ストップビットの間に処理を済ませる
.sppt: ld sp,$0000
ret
;
; 初期化
;
INIT:
di
ld a,$02
out ($93),a ; BUSRQ停止
ld a,$07
out ($a0),a
ld a,$bf
out ($a1),a ; ミキサー初期化
ld a,$0f
out ($a0),a
ld a,$7f ; bit7=L:6,7pinを出力に設定(#1、#2とも)
; bit6=H:JOYSTICK#1を選択する(入力のみ)
; bit5=H:8pin(#1)に出力する値
; bit4=H:8pin(#2)に出力する値
; bit3=H:7pin(#1)に出力する値
; bit2=H:6pin(#1)に出力する値
; bit1=H:7pin(#2)に出力する値
; bit0=H:6pin(#2)に出力する値
out ($a1),a
ld a,$0e
out ($a0),a ; ポートAから入力する状態にしておく
ret
;
; 後処理
;
PS_PRO:
ld a,$03
out ($93),a ; BUSRQを戻す
ei
ret
end
======================================================================
マシンサイクルをいちいち書いている所が、タイミングを合わせる必要があって、なおかつ高速にする必要がある部分です。
今回コードを書いていての新しい発見は、「 cpi 」です。
cpi は、本来 Areg とメモリ内容の比較なのですが、ここでは
・HL ++
・BC --
・BC が 0かどうかの判定
の3つの事を1命令で済ませています。
これは他でも応用できます。
例えば、特定の範囲のメモリを0にする場合、
======================================================================
ld hl,$e200
ld bc,$1800
ld d,$00
.lp1:
ld (hl),d
inc hl
dec bc
ld a,b
or c
jr nz,.lp1
ret
======================================================================
とするのが普通だと思います(LDIRを使わない場合)。
これを
======================================================================
ld hl,$e200
ld bc,$1800
ld d,$00
.lp1:
ld (hl),d
cpi
jp pe,.lp1
ret
======================================================================
と、すっきり書けるのでなかなか便利だと思います。
また、マシンサイクルを調整するために、いろいろ命令を駆使する必要があります。
データブックを見ながら適切な命令を探しますが、以下を使う事が多いです。
NOP ; 5 (=4+1)
OR A ; 5 (=4+1)
RET C ; 6 (=5+1) (条件が不成立になるようにする)
RET NZ ; 6 (=5+1) (条件が不成立になるようにする)
INC HL ; 7 (=6+1)
LD SP,HL ; 7 (=6+1)
LD A,$00 ; 8 (=7+1)
AND $00 ; 8 (=7+1)
DJNZ $00 ; 9 (=8+1) (Breg=1にしておく)
マシンサイクルが10以上は、上の組み合わせで全部網羅できます。
もっとマシンサイクルを稼ぎたい場合は、PUSH-POP(11+1,10+1)や、EX (SP),HL を2回使う(19+1、19+1)と、バイト数が少なくてマシンサイクルを稼ぐ事ができます。