Hatena::ブログ(Diary)

デゼニランドでつかまえて このページをアンテナに追加

2016-12-08 FP-1100サブCPU制御参考プログラム・リスト

[][] FP-1100サブCPU制御参考プログラム・リスト   FP-1100サブCPU制御参考プログラム・リストを含むブックマーク

ごくごく一部で求められていたような気がしたので、FP-1100のサブCPU直接制御サンプルプログラムをまとめてみました。実記でもエミュレータでも動くように調整しました。ご自由にお使い、または参考にしてください。


冒頭はサンプルです。実際必要なのはSUBINIT以下になります。んで使い方。まずSUBINITをCALLしてください。SUBINITでは、画面を40字モードに切り替えた後、サブCPUのFF80h〜にプログラムを転送します。

サブCPUのプログラムは後半のsubdata〜になります。


サブ側のハックが成功すると、サブCPUはメインからの1byteを待ち続けます。

そして1byteを受け取るとその値を下位8ビットとして&HFFxx番地にジャンプします。


サブ側には

・HL受け取り(FF95h

・1バイト受け取って(HL)に書き込み、HL++(FFA0h

・beep on(FFA7h

・beep off(FFAEh

・ポートBの内容をメインに送る(FFB5h

・8バイトを連続で受け取り、(HL)に書き込み(FFC6h

のルーチンを作ってあります。どれも読みやすさを優先したもので、まだまだ最適化できると思います。メモリもまだちょっと余裕あります。


たとえばFPのキースキャンは「サブCPUの&HE400にラインNoを書き込んでから少し待つと、ポートBにスキャン結果が帰ってくる」ので、HLに&HE400をセットしてあげた後にラインNoを1バイト送信してあげて、さらにポートBの内容を受け取っています。


FP-1100の基礎知識としてこちらのエントリ(http://d.hatena.ne.jp/tanzi/20150623/p2)も参考にしてください。


更なる高速化への夢:

・せっかくCPUが二つあるのに全くと言っていいほど並列化されていないので、いかに並列化させるかがポイント?

・キャラクタ消去専用のルーチンを作っておけばちょっと並列化できる

・メインからグラフィックデータを1バイトずつ送るとつきっきりなので並列化できない。VRAMの見えてない範囲にデータを置いてサブ側で転送させるのはどうか? VRAMアクセスでウェイトがかかるのがネックになりそうだが、CPUが並列で動く恩恵のほうが大きいのでは?

・キーボード読み取りにも時間がかかっている。複数ラインの読み取りが必要な場合、もっと処理を考えないといけないような気がする。

・複数のデータをやりとりする場合、一度同期すればあとはクロック数をみてサブとメインのタイミングを合わせるとこができそうだけど・・・エミュと実機で再現性が違ったりいろいろ問題もありそう。


とはいえ、このまま使っても無印88のゲームくらいだったら動かせそうな予感。まぁ、パレットがないのでアルフォスなどは無理ですが・・・・

ちゅーわけで、以下!


ORG 0A800h

;********************
	CALL	SUBINIT

SOUND:
	LD	B,0FFh
BEEPON:
	LD	A,0A7h
	CALL	MtoS
BEEPOFF:
	LD	A,0AEh
	CALL	MtoS
	
	LD	A,B
SLOOP:	DEC	A
	JR	NZ,SLOOP
	
	DJNZ	BEEPON

KEYSCAN:
	LD	HL,0E400h
	CALL	setHL
	LD	A,0A0h
	CALL	MtoS
	LD	A,026h
	CALL	MtoS
	CALL	StoM
	
PUTCHR:	
	PUSH	AF
	LD	HL,0A000h
	CALL	setHL
	LD	A,0C6h
	CALL	MtoS
	POP	AF
	LD	B,08h
PCLOOP:	CALL	MtoS
	DJNZ	PCLOOP
	
	JP	KEYSCAN
	
;********************
SUBINIT:
	LD	A,33
	LD	B,0
	CALL	0B00H		;width40
	LD	A,19
	LD	BC,07001H
	CALL	0B09H		;COLOR 1,0
	LD	HL,00617h
	LD	(09FD7h),HL
	CALL	007A4h		;locate 6,23
	LD	HL,subdata-7
	LD	B,017h		;23chr
loop1:	CALL	0077Eh		;print defchr$
	DJNZ	loop1
	LD	A,052h
	CALL	00AFBh		;Int.SubCPU  / code = $52
	RET

;********************
MtoS:
	PUSH	BC
	EX	AF,AF'
	LD	BC,0FF80h
p1:	IN	A,(C)
	OR	A
	JR	NZ,p1		;wait code 0 from sub CPU
	EX	AF,AF'
	PUSH	AF
	LD	C,0C0h
	OUT	(C),A		;Areg <- (FFC0h)
	LD	C,080h
	LD	A,0C0h
	OUT	(C),A		;INTS ON
	LD	A,020h
	OUT	(C),A		;INTS OFF
p2:	IN	A,(C)
	INC	A
	JR	NZ,p2		;wait code ffh from sub CPU
	POP	AF
	POP	BC
	RET
	
;********************
StoM:
	PUSH	BC
	LD	A,0B5h
	CALL	MtoS
	LD	B,028h
sml1:	DJNZ	sml1
	LD	BC,0FF80h
	IN	A,(C)
	PUSH	AF
sml2:	IN	A,(C)
	INC	A
	JR	NZ,sml2
	POP	AF
	POP	BC
	RET

;********************
setHL:
	PUSH	AF
	LD	A,095h
	CALL	MtoS
	LD	A,L
	CALL	MtoS
	LD	A,H
	CALL	MtoS
	POP	AF
	RET
	
;********************
subdata:
					;VRAMからSUBCPUへコード転送
	DB	048h,024h		;DI
	DB	069h,0FFh		;MVI	A,FFh
	DB	070h,079h,000h,0E8h	;MOV	E800h,A
	DB	004h,000h,000h		;LXI	SP,0000h
	DB	034h,061h,028h		;LXI	HL,2861h		転送元
	DB	024h,080h,0FFh		;LXI	DE,FF80h		転送先
	DB	06Bh,07Fh		;MVI	C,7Fh		転送バイト数
	DB	031h			;BLOCK
	DB	024h,000h,0FFh		;LXI	DE,FF00h
	DB 	014h,000h,0FFh		;LXI	BC,FF00h
	DB	011h			;EXX
	DB	024h,000h,0E4h		;LXI	DE,E400h
	DB	011h			;EXX
	DB	054h,080h,0FFh		;JMP	FF80h
	
	
;ORG	sub - FF80h
;MAIN
	DB	044h,085h,0FFh		;CALL	MtoS
	DB	01Bh			;MOV	C,A
	DB	073h			;JB
;FF85h
;MtoS
	DB	070h,07Dh,000h,0E8h	;MOV	E800h,E
	DB	048h,003h		;SKIT	F2		:L1
	DB	0FDh			;JR	L1
	DB	070h,069h,000h,0E8h	;MOV	A,E800h
	DB	070h,07Ch,000h,0E8h	;MOV	E800h,D
	DB	008h			;RET
;FF95h
;getHL
	DB	044h,085h,0FFh		;CALL	MtoS
	DB	01Fh			;MOV	L,A
	DB	044h,085h,0FFh		;CALL	MtoS
	DB	01Eh			;MOV	H,A
	DB	054h,080h,0FFh		;JMP	MAIN
;FFA0h
;get&write1byte				
	DB	044h,085h,0FFh		;CALL	MtoS
	DB	03Dh			;STAX	H+ (HL)
	DB	054h,080h,0FFh		;JMP	MAIN
;FFA7h
;beepOn					
	DB	011h			;EXX
	DB	04Ah,030h		;MVIX (DE),30h  		:BEEP ON
	DB	011h			;EXX
	DB	054h,080h,0FFh		;JMP	MAIN
;FFAEh
;beepOff				
	DB	011h			;EXX
	DB	04Ah,020h		;MVIX (DE),20h  		:BEEP OFF
	DB	011h			;EXX
	DB	054h,080h,0FFh		;JMP	MAIN
;FFB5h
;sendPortB				
	DB	04Ch,0C1h		;MOV	A,PortB
	DB	070h,079h,000h,0E8h	;MOV	E800h,A
	DB	06Bh,00Dh		;MVI	C,0Dh
	DB	053h			;DCR	C		:L2
	DB	0FEh			;JR	L2
	DB	070h,07Ch,000h,0E8h	;MOV	E800h,D
	DB	054h,080h,0FFh		;JMP	MAIN
;FFC6h
;get&write8byte				
	DB	06Bh,007h		;MVI	C,07h
	DB	044h,085h,0FFh		;CALL	MtoS		:L3
	DB	03Dh			;STAX	H+ (HL)
	DB	053h			;DCR	C
	DB	0FAh			;JR	L3
	DB	054h,080h,0FFh		;JMP	MAIN
	
END

参考:FP-1100 100%活用法(技術評論社) / ベジタブルクラッシュ(ハドソン)

トラックバック - http://d.hatena.ne.jp/tanzi/20161208

2015-06-23 FP-1100のサブCPU乗っ取りについて

[][]FP-1100のサブCPU乗っ取りについて FP-1100のサブCPU乗っ取りについてを含むブックマーク

f:id:tanzi:20150619164503j:image

>>>>>>>こちらにサンプルコードをまとめてみました。


FP-1100は画面表示・キー入力・BEEP音その他の入出力関係がサブCPU側に切り離されていて、本来メインCPUからはBASICルーチンを介してしかアクセス出来ない。用意されたサブCPUとのやりとりルーチンは、送るパラメータの数によって4種類ある。

 実行アドレス内容
タイプ1&H0AFBパラメータなし。Aregにコマンドコードを入れてコール。
タイプ2&H0B00パラメータがひとつ。Aregにコマンドコード、Bregにパラメータを入れてコール。
タイプ3&H0B09パラメータがふたつ。Aregにコマンドコード、BCregにパラメータを入れてコール。
タイプ4&H0B16パラメータが三つ以上。Aregにコマンドコード、Bregにパラメータ個数、HLregにパラメータ先頭アドレスを入れてコール。

コマンドコードは&H01〜&H33まで用意されていて、画面表示やキー入力などの入出力関係にマシン語からアクセスすることが出来る。が、結局BASICで使っているルーチンそのままなのでBASIC以上の表現は無理。これは初心者が使いやすいシステムではあるのだが、スピードがとてつもなく遅い上、各ルーチンのチューンも不可能でリアルタイムゲームには不向き。また、BASICルーチンからではリアルタイムキー入力は出来ないしBEEP音も決められた2種類の長さしか出せない。上記以外にも直接実行番地をコールしてBASICルーチンを利用する手段もある。サブCPUを直接制御する場合、最低限以下のものを覚えておけば事足りるかと。

■主なコマンドコードとBASICルーチン

コマンドコード内容タイプ
&H21画面サイズ変更 / Breg=1で80字モード、0だと40字モード2
&H12LOCATE / BCregにXY座標3
&H13COLOR / BCregにカラーコード3
直接コールDEFCHR$ / HLregにデータ先頭アドレスを入れて&H077Eをコール×

さて、このサブとメインのやり取りだが、サブCPUはコマンドコードを元にテーブルを参照してそれぞれの処理先にジャンプするという仕組み。では、&H33を越えた想定外のコマンドコードをサブ側に送るとどうなるか? サブCPUはメモリ内のでたらめな数値を参照してジャンプしてしまう。つまり大半は暴走してしまうのだが、偶然(?)にもジャンプ先がVRAM内を指すコマンドコードが存在する。ということは、画面上にグラフィックとしてサブCPU用のコードを表示させておき、うまく処理をそこに移せばサブCPUを乗っ取ることが出来る。

以上がFP-1100におけるサブCPU乗っ取りの大まかなストーリー。なかなか感動的。

具体的な例として、&H6Bをコマンドコードとして送ると、サブCPUは&H3A60番地をコールする。このアドレスは、40字モードでLOCATE 4,21(80字だとlocate 44,10)のキャラクタ位置、青のVRAMに相当する。なのでその位置にサブCPUプログラムを書き込んでおけばよい。

10 SCREEN 0:WIDTH 40
20 LOCATE 4,21:COLOR 1,0
30 DEFCHR$(255)="2400E44A304A2008"
40 PRINT CHR$(255);
50 'HACK SUB CPU
60 CALL &H0AFB,&H6B
70 GOTO 60

↑BASICによる単純なサブCPU乗っ取りの例。高速でBEEPのオンオフを繰り返すだけ。プチプチ音がする。サブCPU側のコードは

24 00 E4  LVI   DE ,$E400
4A 30     MVIX (DE),$30  ;BEEP ON
4A 20     MVIX (DE),$20  ;BEEP OFF
08        RET

こんな感じである(BEEPのポートについては後述)。

簡単な検証やユーティリティプログラムではVRAM上にプログラムを置きっぱなしにして毎回コールしても良いが、リアルタイムゲームに応用した過去の例を見るとサブCPUのワークエリアにプログラムを転送してそこで実行させるのが一般的だったようである。

しかしサブCPUのワークは&HFF80〜のみ。とても小さい。VRAMはRGB三枚分あるし、40字モードにすればまるまる半分が画面外(スクロールエリア)になるのでここを使わない手はない! と思いきや、サブCPUに積まれたVRAMは世にも奇妙な反転型RAM(&H00 を書き込むと&HFFになる)であり、さらに水平同期中にVRAMにアクセスがあると次の水平同期までWAITがかかる。つまり1byte書き込むのに約64μSecかかる。ちなみに並び方もキャラクタ単位に8byte縦に並んで次は横に移るという、ちょっと変則的な並び。(確かパソピア7もそんな並びだった?)

■サブCPUメモリマップ

0000H〜CPU内部ROM
1000H〜外部ROM
2000H〜青VRAM
6000H〜赤VRAM
A000H〜緑VRAM
E000H〜I/O
F400H〜外部ROM/CPU内部RAM
F800H〜フリーエリア

サブCPUはμPD7801G。約2MHz。こちらのリンク(http://www.st.rim.or.jp/~nkomatsu/nec/uPD7807.html)はuPD7807に関するものだが、これは7801の上位互換なので参考になる。こちらのリンク(http://www.disgruntleddesigner.com/chrisc/GamePokekon/files/uPD78c06_Instruction_Set.txt)はインスタラクションセット。8bitプログラムの経験者なら上の2つの資料で問題なくコードのほうは書けるだろう。

メイン<>サブのやりとりポートは以下の通り。

アドレス内容詳細
サブCPU &HE800メインCPUとのやりとりメインCPUポート&HFFC0にwriteしたデータがセットされる。書き込むとメンCPUの&HFF80からreadできる。メモリマップドIOなので単に読み書きすればOK
サブCPU INT2メインCPUからの割り込み信号1で割り込みあり SKIT F2で待てる
メインCPU ポート&HFFC0(write)サブCPUとのやりとりポートサブCPUの&HE800へ。使用例 : LD BC,&HFFC0 / OUT(C),A
メインCPU ポート&HFF80(write)割り込み制御ポート最上位ビットがサブCPUへの割り込み信号INTS
メインCPU ポート&HFF80(read)サブCPUとのやりとりポートサブCPUの&HE800の内容がセットされる。

具体的には

LD BC,0xFF80
OUT (C),C       ;INTS ON

↑これでサブCPUに割り込み信号を送る。FPユーザーのバイブル100%活用法によれば、INTSは4μs以上1にしてから0に戻さねば上手く割り込みがかからないことがあるとのこと。4μsはメインCPUの16クロックサイクル相当。でもハドソンのゲームのソースを読んでみると

LD BC,0xFF80
OUT (C),A	;INTS ON
LD A,0x00
OUT (C),A	;INTS OFF

こんな感じでちゃんと動いているようなのでよくわからない。

また、サブCPU側では

SKIT F2     ;LOOP
JR   LOOP

こんな感じで割り込み信号を待てばよい。

結局、グラフィックを表示させようとすればメインCPUから1byteづつデータを送ってあげるしかない。(少なくとも当時雑誌に発表されたものはそうしていた)そのたびにメインとサブはREADYフラグを立てたりコマンド待ちをしたりしなくてはならず、さらにVRAM書き込み時のWAITもあるため思ったようにスピードは出せない。それでもサブCPUを乗っ取ったゲームプログラムはそうでないものに比べて格段に速かった。

ちなみにFPは完全なグラフィックマシン。テキストRAMは持っていなくてすべてグラフィック画面に描かれる。FM-7と同じような構成。BASIC上から見るとPCGがあるように見えるが、これは単にVRAMに送る文字データを書き換えてるだけ。PCG搭載機種でよく使われていたキャラパターン書き換えを使った擬似スクロールなどのテクニックは使えない。ちなみにメイン側&H9000〜のテキストRAMっぽく見える通称キャラクタバッファも単なる仮想テキストRAM。

■キーボード

サブCPU、&HE400の下位4ビットにラインNoを書き込んでから少し待つと、ポートBにKI1~KI8のビットがセットされる。KIが反転信号なのに注意。すこしというのがどのくらいなのかは未検証。&HE400の6bit目がキーボード読み込み許可。以下、キーボードマトリクス。

LINE 11LINE 10LINE 9LINE 8LINE 7LINE 6LINE 5LINE 4LINE 3LINE 2LINE 1LINE 0
KI1(neg):;LKJHGFDSASHIFT
KI2(neg)0987654321ESCCTRL
KI3(neg)^RETURN[7][4][5][8][9][6][3][+][-]GRAPH
KI4(neg)@POIUYTREWQCAPS
KI5(neg){/.,MNBVCXZKANA
KI6(neg)\RUBOUT[←][CLS][↑][↓][INS][→][DEL][/][*]
KI7(neg)-[][1][2][0]SPACE[000][.][,][ENTER]
KI8(neg)STOPPF9PF8PF7PF6PF5PF4PF3PF2PF1PF0BREAK

また、通常時サブCPUはキーボードスキャンで割り込みがかかっているため、スピードダウンしている。サブCPUを乗っ取る場合、まずDIで割り込みを禁止するのがセオリー?

■BEEP音

サブCPU、&HE400の5bit目でBEEPのオンオフ。

サブCPUの&HE400のOUTはこんな感じ

bit76543210
KEY ENABLEBEEP ON/OFFKEY LINE NOKEY LINE NOKEY LINE NOKEY LINE NO

以上、FP-1100でのサブCPU乗っ取りに関する情報をまとめてみたがひとつ問題が。FP-1100エミュレータのeFP-1100、とても良い出来で重宝しているのだが、サブCPUを乗っ取ったプログラムで動かないものがいくつかあるようだ(2015.6.23現在)。ちゃんと動くプログラムもあり、どこ原因がちょっと究明できていないためなんとも言えないのだが。まぁこんな複雑な構成のコンピュータをクロックタイミングまでバッチリ実機に合わせるのは相当大変だと思うので、どこかでサブとメインのタイミングがずれているんだとは思うけど。。。

エミュレータでもちゃんと動く汎用の入出力ルーチンを制作中なので、次回はソースコードつきでそれを解説できたらいいな。

2015-04-14

[][]MZ-80BにiPhoneを繋ぐ MZ-80BにiPhoneを繋ぐを含むブックマーク

こちらのページを参考にMZ-80Bのライン入出力ユニットを作ってみた。用途としては外部のデータレコーダを使うというよりはiPhoneを直接繋いでPCとデータをやりとりするのが主になると思ったので、コネクタはiPhoneの4極3.5mmプラグがそのまま刺さるようにした。8255の足を乗っ取っる形での実装なので、8255自体を交換すれば元に戻る、本体を傷つけない改造だ。装着時、本体カセットデッキの再生&録音は切り離されるが、モーター等は繋がっているのでダミーのテープを入れておけばイジェクトや巻き戻しのギミックもちゃんと楽しめる。


f:id:tanzi:20150414163602j:image

工作中。規模は小さいけどICの足を折るので緊張する。あと4極の配線が面倒。



f:id:tanzi:20150414163603j:image

本体に取り付けたところ。白く見えるICが8255。まだアキバで買えるぞ! とりあえず背面の拡張スロット部分からジャックを出しているがちゃんとマウントしたいところ。


f:id:tanzi:20150414164255j:image

無事iPhoneからデータを読み込めました。


MZ-80Bと2000はオプションのGRAMが無いと非常にショボショボで市販ゲームや雑誌掲載プログラムもほとんど動かない上、モニタとカセットデッキがどうしても故障しがち。かなり売れた機種なので中古市場に数はあるがまともなコンディション(しかもGRAM付き)となると探すのはけっこう難しい。しかしこの改造でとりあえずカセットデッキの問題はなんとかなるので、実機に興味のある人の選択肢が広がればなぁと思う。

トラックバック - http://d.hatena.ne.jp/tanzi/20150414

2015-02-07 FP-1100覚書いろいろ

[][] を含むブックマーク

FP-1100はマイファーストマイコン。未だに思い入れハンパない。当時理解しきれなかったサブCPUの直接制御関連を今こそマスターしたい。とりあえずおぼえ書き。後日まとめる。

■FP-1100 マシン語モニタ命令表

DMxxxx,yyyy メモリダンプ

CMxxxx メモリ書き換え

MMxxxx,yyyy>zzzz メモリ内容移動

CR レジスタの値表示

DM# 1 xxxx,yyyy プリンタ出力

STxxxx,yyyy テープセーブ

LT テープロード

GOxxxx 指定アドレスへジャンプ

BA BASICへ戻る

MON モニタモードへ入る

■FP-1100サブCPU関連覚書

サブCPU:uPD7801G 2MHz

□CRTCはHD46505。CRTCの制御:E000HにレジスタNoを書き込み、E001Hにデータを書き込む。

レジスタNo.内容
R0Horizontal Total
R1Horizontal Displayed
R2H. Sync Position
R3Sync Width
R4Vertical Total
R5V. Total Adjust
R6Vertical Displayed
R7V. Sync Position
R8Interlace Mode and Skew
R9Max Scan Line Address
R10Cursor Start
R11Cursor End
R12Start Address (H)
R13Start Address (L)
R14Cursor (H)
R15Cursor (L)
R16Light Pen (H)
R17ight Pen (L)

・画面表示のON/OFFはPAのビット0〜2

・キー入力はE400Hの下位4ビットにラインNoを書き込んでから少し待つ。PBにデータがセットされる。

・E400Hで音出し。

・PAのビット4:モノクロ画面

・F000Hのビット0〜3:ボーダーカラー

・メインへの割り込み:PCのビット3を一定の間1にすると割り込みがかかる。

・E800Hに書き込むとメインのFF80H(READ)に出力される

・INT2が1のとき、メインからの割り込みあり。

・E800HにメインのFFC0Hのデータがセットされる。

・VRAMアクセスでSUB-CPUが止まる。水平帰還中に1バイトだけ(64μsecに1バイト)。

・サブCPUメモリマップ

0000H〜CPU内部ROM
1000H〜外部ROM
2000H〜青VRAM
6000H〜赤VRAM
A000H〜緑VRAM
E000H〜I/O
F400H〜外部ROM/CPU内部RAM
F800H〜フリーエリア
トラックバック - http://d.hatena.ne.jp/tanzi/20150207

2014-12-16

[][]PIC32MXのMZ-80エミュレータ作ってみた PIC32MXのMZ-80エミュレータ作ってみたを含むブックマーク

ランニングエレクトロニクスでしばらく品切れだったPICボード、SBDBT32が入荷されたようなのでこちらのページにあるMZ-80エミュレータを作ってみた。内容も詳細に書かれていて非常に勉強になる。すばらしいページをありがとうございます。

このボード、USBとSDカードスロットが搭載されていてブートローダーのファームウェアも書き込み済みという至れり尽くせりの製品で(ちょっと値段はお高いけど)、電源とビデオ出力、それとサウンド出力部分を作るだけでMZ-80が丸々動いてしまう。電子工作としては簡単すぎて物足りないレベル。すごい時代だ。


f:id:tanzi:20141203181843j:image

部品買出し。SBDBT32とSDカード以外はぜんぶ秋月。


f:id:tanzi:20141203181915j:image

こいつがSBDBT32。小さい。


f:id:tanzi:20141203181941j:image

USBとマイクロSDスロットギリギリサイズ。


f:id:tanzi:20141203193504j:image

完成(笑) 電子工作に慣れたひとなら30分で出来るはず。

フォントデータはフォーマットコンバータをHSPで書いてサクっと変換。


f:id:tanzi:20141216105807j:image

動いた!!

MZ-80のキー配列はかなり独特なので専用のキートップを作りたくなるな……

最初、ディスプレイに繋いでもウンともスンとも言わなくて焦ったんだけど、コンポジット信号の同期がうまく取れてなかったみたいで何度かリセットしたらうまく引っかかってくれた。ほんと液晶TVのコンポジットは使えない。。。さて、どっぷりMZ-80で遊んでみるものいいし、これをベースにいろいろ改造していくのも楽しそう……

トラックバック - http://d.hatena.ne.jp/tanzi/20141216