6502CPUでマイコンボード(4)

6502

6502CPUマイコンボードでSBC-IOのパラレルIC(PIA)を使ってLチカができたので、つぎはSBC-IOのMC6840を使ってドレミ..と演奏してみたい。そのための圧電スピーカーは接続済みでした。

タイマーIC HD6840をゲット

前回、手持ちのMC6840タイマーICをSBC-IOに装着したらマイコン自体うまく動かなかったので(その石は、SBC6800とつないでもNGでした)別のICをゲットすることに。

たまたま国内の部品店でHD6840PタイマーICとHD6321パラレルIC(PIA)があったので通販でゲットしました。

日立製ICで揃っていい感じ。あ、74LS173も日立製だと完璧でしたね。

稼働確認 〜 まずはLED点灯はOK

上の写真にでてる74LS173も、HD6840、HD6321と一緒に通販でゲットしたもので、こちらは4つのLEDを$8000番地にぶら下げるICです。動作確認に最適。さっそく、Universal MonitorのSコマンドで$8000番地を書き換えて点灯確認します。

Tomi9さんのBLOGにも出てますが、LEDは$8000番地の下位4bitに対応していて、ビット1で点灯、ビット0で消灯します。$01だと右端のLEDが点灯という感じ。

前回パラレルICのPIAもうまく動いたので、アドレスデコーダー74HCT138は問題なしの模様。次はタイマーIC HD6840のデータシートとか、アプリケーションノートをみながら「ドレミ…」演奏をしてみたいと思います。

演奏プログラムの検討

ちょっと長いですけど、最初に作った6502CPUのアセンブラプログラムがこちらです。
Universal Monitor for 6502で動きます。

;
; MC6840 PLAY
;  Universal Minitor for 6502 
;

	CPU	6502

TARGET:	EQU	"6502"

;
;;; Functions
low	function	x,(x & 255)
high	function	x,(x >> 8)
;
STROUT  EQU        $FF98
PT0     EQU        $28
;
; MC6840 PTM Address
;
        ORG $8030
PTM_CR13  RMB 1
PTM_CR2   RMB 1
PTM_MSBT1 RMB 1
PTM_T1LSB RMB 1
PTM_MSBT2 RMB 1
PTM_T2LSB RMB 1
PTM_MSBT3 RMB 1
PTM_T3LSB RMB 1
;
        org $0200
MAIN:
        JSR     PRINT_START
;
        JSR     INIT_6840
        JSR     PLAY
;
        LDA     #$00
        STA     PTM_MSBT1
        STA     PTM_T1LSB
        STA     PTM_MSBT2
        STA     PTM_T2LSB
        STA     PTM_MSBT3
        STA     PTM_T3LSB
;
        JSR     PRINT_END
        BRK
;
PLAY:
        LDA     #8
        STA     PLAY_CNT
        LDX     #$00
;
PLAY_LOOP:
        JSR     SOUND_OUT
        DEC     PLAY_CNT
        BNE     PLAY_LOOP
;
        RTS
;
SOUND_OUT:
        LDA     S_DO1,X
        STA     PTM_T2LSB
        INX
        LDA     S_DO1,X
        STA     PTM_MSBT2
        INX
        JSR     WAIT
        RTS
;
INIT_6840:
        LDA     #$01
        STA     PTM_CR2
        STA     PTM_CR13
;
        LDA     #$00
        STA     PTM_MSBT1
        STA     PTM_T1LSB
        STA     PTM_MSBT2
        STA     PTM_T2LSB
        STA     PTM_MSBT3
        STA     PTM_T3LSB
;
        LDA     #$82
        STA     PTM_CR2
        LDA     PTM_CR13
        LDA     #$83
        STA     PTM_CR2
        LDA     #$82
        STA     PTM_CR13
;
        RTS
;
WAIT:
        LDA     #$FF
        STA     TIMER1
W_LOOP1:
        LDA     #$FF
        STA     TIMER2
W_LOOP2:
        NOP
        NOP
        DEC     TIMER2
        BNE     W_LOOP2
;
        DEC     TIMER1
        BNE     W_LOOP1
;
        RTS
;
PRINT_START:
        LDA	#low(START_MSG)
        STA	PT0
        LDA	#high(START_MSG)
        STA	PT0+1
        JSR     STROUT
        RTS
;
PRINT_END:
        LDA	#low(END_MSG)
        STA	PT0
        LDA	#high(END_MSG)
        STA	PT0+1
        JSR     STROUT
        RTS
;
        BRK   ; PROGRAM END
;
; SOUND DEFINE
;
S_DO1   FDB  $0DC2
S_RE1   FDB  $0C42
S_MI1   FDB  $0AEB
S_FA1   FDB  $0A4E
S_SO1   FDB  $092F
S_RA1   FDB  $082E
S_SI1   FDB  $074A
S_DO2   FDB  $06E1
S_RE2   FDB  $0621
S_MI2   FDB  $0575
S_FA2   FDB  $0527
S_SO2   FDB  $0497
S_RA2   FDB  $0417
S_SI2   FDB  $03A5
S_DO3   FDB  $0370
;
; MSG DEFINE
;
START_MSG FCB "PLAY START",$0D,$0A,$00
END_MSG FCB "PLAY END",$0D,$0A,$00
;
; WORK AREA
;
        org $30
;
TIMER1  RMB 1
TIMER2  RMB 1
;
PLAY_CNT  RMB 1
        END

残念ながら、色々な問題で正しく動かなかったのですが、基本的な考え方を説明します。

MC6840タイマーICで音程を出すしくみ

MC6840タイマーICで音程を出す場合、以前 i8254タイマーICで音程を出したときと一緒で方形波を出力したいと思います。

データシートによるとモードが何種類かあるうちの方形波を出すContinuous Modeというものがあり、そちらの動作は以下のようなかたちでした。

MC6840 Fundamental and Applications

MC6840タイマーICは16ビットタイマーを3つ持ってます。Continuous Modeでは、タイマーに値をセットしたのちカウントダウンしてタイムアウトしたタイミングでHレベル、Lレベルと変化する模様。つまり、音程を出すとしたら2回のタイムアウトで1周期なので、「クロック周波数(1.8432MHz)/音程(ドレミ)の周波数/2」でカウンター値が計算できます。

上記ソースの S_DO1SDO_3 のところに定義している2バイト数値はスプレッドシートで上記計算式を使って計算して出したものです。
ちなみに、6502CPUはMC6800と違ってインテルCPUみたいなリトルエンディアンですので2バイト数を定義すると下位1バイト→上位1バイトという順番にメモリーに格納されます。(ひっくり返った感じ)

MC6840の初期化

MC6840は3つの16ビットカウンターを持っています。ただ、制御用のアドレス線は3本しかありません。つまりコントロール用のアドレスは8つしかありません。

カウンターセット用 2バイトx3のレジスタを確保すると、制御用レジスタ指定で2つしか残らないのですが、なんとタイマー1とタイマー3の制御用レジスタを共用しています。電源投入時には制御レジスターはタイマー3が選択されていて、タイマー2の制御レジスタの$01ビットのOFF/ONでタイマー1とタイマー3を切り替える感じ。
なんともややこしい構成です。

MC6840 Fundamental and Applications

こういった事情なので、上記ソースの INIT_6840 ルーチンではタイマー2用の制御レジスタに何度もデータを書き込んでいるわけです。

音程出力ルーチン…正しくない?

初期化が正しく出来ていれば、あとは定義したタイマー値を、SBC-IOで圧電スピーカーをつないでいるタイマー2へセットして、ある程度WAITして…を繰り返すことでド・レ・ミ…と演奏できるはず。

PLAY ルーチンではひとまず8回ループさせて 「ドレミファソラシド」が演奏できることを目標としました。でもどうも正しくなかったようで….

演奏してみたら、音程が変?

The Macro assembler AS でアセンブリ(ASL,p2Hexを使用)してIntelHex形式のオブジェクトを作成して、Universal MonitorのLコマンドで流し込み、 G0200 で実行してみましたが音が変です。最初の「ド」がピーって音だし、途中もバラバラ。

また、プログラムを改造して、途中 途中の状況を文字列で表示(DO、RE、MIとか)しながら試してみるともっとヘンテコな状況になりました。

音程が変なだけではなく、SBC-IOを装着しているとループカウンターが8回で止まらなかったり….でもSBC-IOを外すと想定通り動いたり。メモリーが変なことになってるみたいです。メモリーマップドI/O恐るべし。

問題切り分けのため、SBC6800基板を使う

MC6800搭載のSBC6800

もともとTomi9さん作のSBC-IOはSBC6800 AdaptorとかSBC6809 AdaptorでSBC68系バスを搭載した環境で使うボードなので、SBC6800(MC6800 CPU搭載)+SBC6800 Adaptorと接続して動作確認してみました。

プログラムの方はアセンブラの表記がちょっと異なりますが(LDA → LDAAにするとか)、元々MC6800の考え方をベースとした6502CPUのプログラムをベースとしたので移植はかんたんでした。

SBC-IO_PLAY/SBC6800 at master · kuninet/SBC-IO_PLAY
SBC-IOと各種68系SBCシリーズなどを組み合わせたプログラム. Contribute to kuninet/SBC-IO_PLAY development by creating an account on GitHub.

6502CPUだとアキュームレターが1つだったりインデックスレジスタが8bitですが、MC6800はアキュムレーターがA/Bと2つあったりインデックスレジスタXが16bitだったりでちょっと豪奢な気分。その分、インデックスレジスタが1つしかないとか一長一短はありますが。

クロック周波数が違いますのでタイマー値は調整する必要がありましたが、ループ8回を突き抜けることもなく無事プログラムは完走。音もちゃんと出ました。今回は音程も問題なし!! プログラムの考え方は間違っていなかったのと、SBC-IOの部品組付けなどに問題はなかったことが確認できました。(部品を半端に搭載していたので….)

SBC6800改 6502マイコンで挑戦

続いて問題切り分けのため、ほうめいさん案6502用改造をほどこしたSBC6800基板(Tomi9さんのSBC6800アダプターつき)とSBC-IOを接続してMC6840でのド・レ・ミ…演奏をしてみました。

SBC6800(MC6800 CPU版)と違って音程は変ですが、8回のカウンターがおかしくなることもなくプログラムが動作することが確認できました。これは私が作った6502CPUマイコン側のハードウェアが原因の可能性が高い!!

原因は2つ

で……結論から言いますと、ソフトウェアとハードウェアの2つの原因がありました。

音程用タイマーセットルーチン

MC6800 CPUでは正しい音程が出力されて、6502CPUではヘンテコな音が出るのが不思議だったのですが、両者のプログラムを見比べていて気がつきました。MC6840のタイマーレジスタへのセット順が違っていました。

MC6840タイマーICへカウンター値をセットするレジスタは2つづつ別々のアドレスにあります。

;
; MC6840 PTM Address
;
        ORG $8030
PTM_CR13  RMB 1
PTM_CR2   RMB 1
PTM_MSBT1 RMB 1
PTM_T1LSB RMB 1
PTM_MSBT2 RMB 1
PTM_T2LSB RMB 1
PTM_MSBT3 RMB 1
PTM_T3LSB RMB 1

今回はタイマー2なので、 PTM_MSBT2PTM_T2LSB へそれぞれ上位、下位の1バイトづつを出力します。MC6840はMC6800 CPUで使うことを想定していまして、アプリケーションノートを見ると 16bitのXレジスタに値を入れておいて 一気にストアするというプログラム例が出てました。

SOUND_OUT:
        LDX     SOUND_PTR
        LDAA    0,X
        STAA    PTM_MSBT2
        LDAA    1,X
        STAA    PTM_T2LSB
        INX
        INX
        STX     SOUND_PTR
        JSR     WAIT
        RTS

わたしが今回SBC6800(MC6800 CPU)で演奏プログラムを実装した際は6502CPU用のプログラムと同様 、上記のように 1バイトづつセットすることにしました。ただ、エンディアンが違いますのでMSB(上位)→LSB(下位)の順でセットしています。6502CPUの場合は定数定義したメモリー上 LSB(下位)→MSB(上位)とセットしました。

これが間違いで、MC6840タイマーはカウンター値のLSB(下位)がセットされたことをトリガーに動作するようです。この順番を逆にする必要がありました。

6502CPUマイコンのIRQ(割り込みリクエスト)配線

音程の問題は解決しましたので、6502CPUユニバーサル基板マイコンの配線の問題と思い配線チェックを実施しました。すると、変な配線が1つ….

6502マイコン側に搭載しているシリアルIC(ACIA)の#IRQ端子を外部バスに直結していました。ただし、6502CPUの#IRQ端子にはつないでいません。SBC-IO側もタイマーIC(MC6840)の#IRQ端子を外部バスにつないでいます。直結されちゃっている感じ。

今回、シリアルIC(ACIA)の読み書きはポーリングで実施していて割り込みを使用していません。元々のGrantさんの回路ではACIAの#IRQはMAX232側につないでフロー制御に使っていたので接続する必要はありませんでしたが、回路図を書いたときに勘違いしたようです。(せめて、CPU側でプルアップ済みの#IRQにつないでいたら良かったかも)

こちらの緑のケーブルです。大半の手配線が終わったあと朦朧としながら回路図どおり結線したものでした。こちらをひとまず切断しました。

ユニバーサル基板版の6502マイコンでも無事 MC6840でド・レ・ミ演奏ができました。良かった!

でも考えてみると、SBC-IO側でもMC6840タイマーICとMC6850シリアルICの#IRQを直結してSBC68系バスに出してきているので、ただしく6502CPUのIRQ(プルアップ済み)につなぐのが正しいのかもしれないです。
そこはまた今後研究します。(-_-)>

コメント