2020年03月31日

ジョイスティックポートでRS-232C通信

  最近ツイッターで、ジョイスティックポートを使った、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)と、バイト数が少なくてマシンサイクルを稼ぐ事ができます。


posted by えすび at 00:20| Comment(0) | P6解析:ジョイスティックポート | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前: [必須入力]

メールアドレス:

ホームページアドレス:

コメント: [必須入力]

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。