MSX Datapack wiki化計画

7章 新しいハードウェア

 この章では、MSX turbo Rに新しく内蔵されているハードウェアについて説明します。

 7.1 システムタイマー

 R800は、命令の実行時間が一定ではないので、CPUの命令実行時間から、ウェイトのタイミングなどをとることができません。そこで、MSX turbo Rでは、数10μ秒〜数10m秒程度の短い時間の検出ができるように、システムタイマーが追加されました。
 システムタイマーは、16ビットのクリア・データ読み出しが可能なカウンタで、10.738635MHzのシステムクロックを42分周(10.738635MHz/42=255.68KHz)したクロックでカウントアップされます。つまり、最下位ビットは3.911μ秒毎にカウントアップされることになります。なお、カウンタをクリアしてからカウントが1になるまでの時間は、0〜3.911μ秒の範囲です。
 カウンタの読み出し時に、データのラッチ機能はありません。カウンタの上位・下位読み出しの間にクロックが入ると、正しいカウント値が読み出されないので注意して下さい。

表1.6 システムタイマーのI/Oポート

I/Oポート番地R/W機能
0E6H W 任意のデータを書き込むと、カウンタがクリアされる。
0E6H R カウンタデータの下位8ビットの読み出し。
0E7H R カウンタデータの上位8ビットの読み出し。

 割り込みルーチンなどから呼ばれるプログラムは、カウンタをクリアすると、フォアグラウンドで動作しているプログラムに影響を与えるので、クリアしないで下さい。
 具体的には、次のサンプルプログラムのようにします。

countlow        equ      0e6h         ; カウンタ下位8ビット
counthigh       equ      0e7h         ; カウンタ上位8ビット
        .z80
;
; Bレジスタで指定された時間待つ。
; 待つ時間は、B*3.911μ秒
; 誤差は、-3.911μ秒〜+0μ秒
; 0を指定してはいけない。
; 割り込みは禁止されていなければならない。
; C,A,Fの各レジスタは破壊される。
;
wait:
        in      a,(countlow)          ; カウンタの現在値を得る
        ld      c,a                   ; それを保存する
wait_loop:                            
        in      a,(countlow)          ; カウンタの現在値を得る
        sub     c                     ; 経過時間を算出する
        cp      b                     ; 指定された時間経過したか?
        jr      c,wait_loop           ; 経過していなければループする
        ret

 7.2 PCM

 ここでは、PCMのI/Oポートや録音・再生の手順などを解説します。

7.2.1 PCMのI/Oポート

 PCM録音・再生のためのI/Oポートは以下のとおりです。

表1.7 PCMのI/Oポート

番地 bit7bit6bit5bit4bit3bit2bit1bit0
0A5H(Write) 0 0 0 SMPLSEL FILTMUTEADDA
0A5H(Read) COMP0 0 SMPLSEL FILTMUTEBUFF
0A4H(Write) DA7 DA6 DA5 DA4 DA3 DA2 DA1 DA0
0A4H(Read) 0 0 0 0 0 0 CT1 CT0

ADDA(BUFF)バッファモード
D/Aコンバータの出力をシングルバッファにするか、ダブルバッファにするかを指定します。D/A時にはダブルバッファに、A/D時にはシングルバッファにして下さい。
0シングルバッファ(A/D時、リセット時)
1ダブルバッファ(D/A時)
MUTE ミューティング制御
システム全体の音声出力をON/OFFします。
0音声出力OFF(リセット時)
1音声出力ON
FILT サンプル・ホールド回路入力信号の選択
A/D時にサンプル・ホールド回路に入力する信号を、フィルタの出力信号にするか、基準信号(ground level)にするかを選択します。
0基準信号(リセット時)
1フィルタ出力信号
SEL フィルタ入力信号の選択
ローパスフィルタに入力する信号を、D/Aコンバータの出力信号にするか、マイクアンプの出力信号にするかを選択します。
0D/Aコンバータ出力信号(リセット時)
1マイクアンプ出力信号
SMPL サンプルホールド信号
入力信号をサンプルするか、ホールドするかを選択します。
0サンプル時(リセット時)
1ホールド時
A/D時には、信号をホールドする前に、最低7μ秒はサンプルしなければなりません。
COMP コンパレータの出力信号
サンプル・ホールドの出力信号と、D/Aコンバータの出力信号とをくらべて、どちらが大きいかを読み出すことができます。
0D/A出力>サンプル・ホールド出力
1D/A出力<サンプル・ホールド出力
DA7〜DA0 D/A出力データ
CT1、CT0 カウンタデータ
63.5μ秒ごとにカウントアップされます。
D/A時(ADDAが1)には、カウントアップに同期して、0A4H番地に書かれたデータが繰り返し出力されます。0A4H番地にデータを書き込むと、カウンタはクリアされます。
A/D時(ADDAが0)には、0A4H番地に書かれたデータはすぐに出力されます。0A4H番地にデータを書き込んでもカウンタはクリアされません。

7.2.2 PCM再生

 PCMは次の手順で再生します。

  1. ADDAを1に、MUTEを1に、SELを0にします(I/Oポートの0A5H番地に00000011Bを出力する)。
  2. CT1、CT0を読んで、サンプリング周期を検出します。
  3. PCMデータを出力します。このとき、カウンタは自動的にクリアされます。
  4. データの個数回、2と3とを繰り返します。
            .z80
;
; PCM再生
;
; 入力   HL = PCMデータの開始アドレス
;        BC = PCMデータの長さ
;        E  = 再生サンプリング周期
;              1   15.75KHz
;              2   7.875KHz
;              3   5.25KHz
; 戻り値  なし

pcmdac      equ    0a4h                  ; D/A コンバータ (Write)
pcmcnt      equ    0a4h                  ; カウンタ       (Read )
pcmcntl     equ    0a5h                  ; PCMコントロール(Write)
pcmstat     equ    0a5h                  ; PCMステータス  (Read )
pcmplay:
            ld     a,00000011b           
            out    (pcmcntl),a           ; D/Aモードに設定
            di                           ; タイミングの正確さのために、
                                         ; 割り込みを禁止する
pcmplay_loop:                            
            in     a,(pcmcnt)            ; カウンタを読み取る
            sub    e                     ; 希望の値になったか?
            jr     c,pcmplay_loop        ; そうでないならループする
            ld     a,(hl)                ; データを読み取る
            out    (pcmdac),a            ; DACに出力する
            inc    hl                    ; 次にデータを指し示す
            dec    bc                    ; カウンタを減らす
            ld     a,c                   ; カウンタは0か?
            or     b                     
            jr     nz,pcmplay_loop       ; そうでないならループする
            ei                           ; 割り込み禁止を解除
            ret

7.2.3 PCM録音

 PCM録音は次の手順で行います。

  1. ADDAを0に、MUTEを0に、FILTを1に、SELを1に、SMPLを0にします(I/Oポートの0A5H番地に00001100Bを出力して、入力アナログ信号をサンプルする)。
  2. CT1、CT0を読んで、サンプリング周期を検出します。
  3. 最低7μ秒待ちます。この7μ秒が2に含まれていれば待つ必要はありません。
  4. SMPLを1にして(I/Oポートの0A5H番地に00011100Bを出力する)、入力アナログ信号をホールドします。
  5. 逐次変換のシーケンスによって、D/Aコンバータのデータを上位ビットから変化させながら、D/Aコンバータ出力と入力アナログ信号との比較結果をCOMPから読み込んで、各ビットを決定し、データを格納します。
  6. SMPLを0にします(I/Oポートの0A5H番地に00001100Bを出力する)。
  7. 2から6を繰り返します。
            .z80
;
; PCM録音
;
; 入力   HL = PCMデータの開始アドレス
;        BC = PCMデータの長さ
;        E  = 再生サンプリング周期
;              1   15.75KHz
;              2   7.875KHz
;              3   5.25KHz
; 戻り値  なし

pcmdac      equ    0a4h                  ; D/A コンバータ 
pcmcnt      equ    0a4h                  ; カウンタ       
pcmcntl     equ    0a5h                  ; PCMコントロール
pcmstat     equ    0a5h                  ; PCMステータス  
; 1ビットA/D変換マクロの定義
adconv      macro  next_bit,strip
            local  adconv_not_change
            out    (pcmdac),a            ; データを出力
            db     0edh,70h              ; PCMSTATからデータを読み込み、
                                         ; フラグにのみ反映させる
            jp     m,adconv_not_change   ; 入力アナログ信号の方が大きければ、
                                         ; データはそのまま
            and    strip                 ; 入力アナログ信号の方が小さいので、
                                         ; ビットを0にする
adconv_not_change:                       
            or     next_bit              ; 次のビットを1にする
            endm                         

pcmrec:
            ld     a,00001100b
            out    (pcmcntl),a           ; A/Dモードに設定
            ld     d,0                   ; カウンタ検出の初期値を設定する
            di                           ; タイミングの正確さのために
                                         ; 割り込みを禁止する
pcmrec_loop:                             
            in     a,(pcmcnt)            ; カウンタを読み取る
            cp     d                     ; 希望の値になったか?
            jr     nz,pcmrec_loop        ; そうでないならループする
            add    a,e                   ; 次のカウンタの値を作る
            and    11b                   
            ld     d,a                   ; それを保存する
            ; 上記ループにより7μ秒を満足する
            exx
            ld     a,00011100b           
            out    (pcmcntl),a           ; データをホールドする
            ld     c,pcmstat             ; COMPビットのポートアドレスを設定する
            ld     a,80h                 ; 出荷データを設定する
            adconv 01000000b,01111111b   ; 順次ビットを変換する
            adconv 00100000b,00111111b
            adconv 00010000b,01011111b
            adconv 00001000b,01101111b
            adconv 00000100b,01110111b
            adconv 00000010b,01111011b
            adconv 00000001b,01111101b
            adconv 00000000b,01111110b
            exx
            ld     (hl),a                ; データを格納する
            ld     a,00001100b           
            out    (pcmcntl),a           ; データのホールドを解除する
            inc    hl                    ; 次のデータを指し示す
            dec    bc                    ; カウンタを減らす
            ld     a,c                   ; カウンタは0か?
            or     b                     
            jr     nz,pcmrec_loop        ; そうでないならループする
            ld     a,00000011b           
            out    (pcmcntl),a           ; D/Aモードに設定。MUTEを解除する
            ei                           ; 割り込み禁止を解除
            ret
注意このプログラムは、Z80では速度が間に合わない場合があります。