memory base driver for 6280 1/3

自前の資料を基に作りました. 対象はネイティブの IO port 経由です. 別の組み込み MCU を使う場合は設定条件をよくみて SPI 機能を使うとよいでしょう.

こういうシフトレジスタの処理だけならアセンブラで書くのはとても楽しいです.

送信ルーチン

serial_send:
send_bit_next:
	lsr	<$02
	ror	<$01
	ror	<$00
	cla
	rol	a
	sta	joypad
	ora	#1<<1
 rept 0
	nop
 endm
	sta	joypad
	dex
	bne	send_bit_next
	rts

解析したサブルーチンは最大 8 bits まで送れる作りでしたが、zero page を使って最大 24 bits送れるようにしました. また Carry flag を活用して分岐命令を減らしています.

rept 0 について. 送信に関しては nowait で送ってかまわないようです.

初期化ルーチン+簡単な受信

	ldx	#9
	lda	#$A8
	sta	<$00
	stz	<$01
	bsr	serial_send
	lda	joypad
	sta	<$00
	and	#$0f
	php
	ldx	#1
	stx	<$00
	bsr	serial_send
	plp
	bne	init_ng
	lda	joypad
	and	#$0f
	cmp	#1<<2
	bne	init_ng
	sec
	rts

既存のルーチンは 8bits 送信, 1 bit 送信+受信, 1bit 送信+受信でしたが、 9bits 送信+受信, 1bit 送信+受信にわけました. 送信後, IO port から受信するときは CLOCK 立ち上がりから出力まで遅いみたいです. この場合は dex; bne; rts と十分に時間が経過しているので待ち時間の挿入はいりません.

先日の解析でプログラムを読み間違えたので、間違いに従い 10bits の初期化コードを3度送ってしまいました. この場合はエラーがでるにもかかわらず、実際には初期化が成功していて input bit 0 の出力が memory data となるので上キーとIボタンが送信されなくなりました.

メモリデータの受信

	ldx	#1<<1
	cly
read_byte_next:
	lda	#1<<7
	sta	<$00
read_bit_next:
	stz	joypad
 rept 0
	nop
 endm
	stx	joypad
 rept 2 ;do not remove
	nop
 endm
	lda	joypad
	lsr	a
	ror	<$00
	bcc	read_bit_next
	lda	<$00
	sta	(<mb128_destsrc),y
;dest or src += 1; length -= val; length == 0
	bsr	rw_ptr_length_update
	bne	read_byte_next
	stz	joypad
	rts
  • zero page $00 を 8bits シフトレジスタにします.
  • bit7 = 1, bit6:0 = 7'h00 にすることでカウンタも兼ねます.
  • stz joypad (clock=0); stx joypad (clock=1)のあとの nop は必要でそのあと lda joypad です.
  • lda joypad; lsr a で取り込んだデータを Carry にいれ、シフトレジスタにいれます. lsr joypad だと予期しない書き込み処理も入るので lda 命令を使います.

最近の MCU でやる場合

  • 最近のシフトレジスタバイスは MSB->LSB がほとんどで LSB->MSB なのに注意が必要です.
  • メモリベースからのデータの更新条件はクロックの立ち下がりではなく立ち上がりのために立ち上がり直後に読み込むとデータが安定しません.
  • どこまで早いクロックでいけるのかを調べると楽しいと思います.

KOCD2001 MB128 driver

こちらのが前回よりプログラムがきれいです. ただしやってることはほとんど同じでした.

ec1b8:
	jsr	lc08e ;send 10x3+3 bits, check detection bits
	bcc	lc1c0
	lda	#$FF
	rts
lc1c0:
;operation = read
	ldx	#$01
	ldy	#$01
	jsr	lc070
;send address
	ldx	<$8d
	ldy	#$08
	jsr	lc070
	ldx	<$8e
	ldy	#$02
	jsr	lc070
;send length
	lda	<$8b
	pha
	tax
	lda	<$8c
	pha
	sta	<$8b
	stz	<$8c
	txa
;(snip)

;---- initialize ----
lc08e:
	stz	$1000
	ldy	#$03
lc093:
	phy
	ldx	#$A8
	ldy	#$08
	bsr	lc070
	clx
	ldy	#$01
	bsr	lc070
	nop
	lda	$1000
	and	#$0F
	pha
	ldx	#$01
	ldy	#$01
	bsr	lc070
	nop
	lda	$1000
	and	#$0F
	plx
	ply
	cpx	#$00
	bne	lc0bd
	cmp	#$04
	clc
	beq	lc0c7
lc0bd:
	dey
	bne	lc093
	clx
	ldy	#$03
	jsr	lc070
	sec
lc0c7:
	rts

;---- send 1 bit into MB128 ----
lc070:
	txa
	and	#$01
	sta	$1000
	nop
	nop
	nop
	ora	#$02
	sta	$1000
	pha
	txa
	lsr	a
	tax
	pla
	and	#$FD
	dey
	bne	lc070
	nop
	nop
	sta	$1000
	rts

ADCD3001 A.III memory base 128 driver

解析しました.
(2020年1月28日更新: コードの読み間違いが原因のactivation * 3 とその後の不明の 3 bits を削除)

;ADCD3001 A.III memory base 128 driver
;bits |send data   |meaning
;10   |0001_0101_01|activation
;1    |p           |set operation read or write
;10   |aaaa_aaaa_aa|memory address bit 16:7; address bit6:0 = 7'h00
;20   |l *20       |memory data bit length
;lengt|x *length   |(read) memory read data
;lengt|d *length   |(write) memory write data
;(legend)  0:low, 1:high, x:don't care, p:operation, a:address, l:length, d:write data
;---- main routine ----
;initialize -> send read command -> check memory signature
ed7ff:
	jsr	ld789 ;rts only (?)
	jsr	ld7b8 ;get activation code (A=0:ng, A=1:ok)
	cmp	#$00
	bne	ld81d
	lda	#$01
	jsr	ld885 ;send R/W(=A.1, 1:read, 0:write), address (10 bits), length (20 bits)
ld80e:
	jsr	ld863 ;read data
	lda	$36e5
	sta	($21)
	jsr	ld6f9
	bcc	ld80e
	cla
	clc
ld81d:
	jsr	ld78a
	jsr	ld901
	rts

;address 0x1ff000 assignments for memory base 128
;w0 serial write data
;w1 serial clock
;r3 always 0
;r2 memory base detection serial data
;r1 always 0
;r0 memory data

;I'm not sure how to reset serial clock count???
ld7b8:
	clx
ld7b9:
;   old -> new
;sd 0001_0101_01
;sr xxxx_xxxx_01
;sd:serial data (when serial clock sets 0->1, mb128 latch 0x1ff000.w0)
;sr:r2 = detection data, bit3, 1 and 0 must be 0
	lda	#$A8 ;serial data lsb->msb
	ldy	#$08 ;bitcount
	jsr	ld8d4
	cla
	jsr	ld8b7 ;send 1bit (A.0)
	lda	$1000 ;get activation reply
	and	#$0F
	tay
	lda	#$01
	jsr	ld8b7
	lda	$1000
	and	#$0F
	cmp	#$04
	bne	ld7e1
	tya
	cmp	#$00
	bne	ld7e1
;activation ok
	clc
	cla
	bra	ld7ec
ld7e1: ;retry max 3 times
	inx
	cpx	#$03
	bne	ld7b9
;error!
	jsr	ld7ed
	sec
	lda	#$01
ld7ec:
	rts
ld7ed:
;sd 000
	cla
	ldy	#$03
	jsr	ld8d4
	jsr	ld901 ;wait some clocks
	lda	#$01
	sta	$1000 
	jsr	ld901
	rts

ld885:
	jsr	ld730 ;tai $D91D,$36E1,$0004; rts
	jsr	ld8b7 ;send 1 bit; data=A.0
	jsr	ld738
	lda	$36d9
	ldy	#$08
	jsr	ld8d4 ;send 8bits
	lda	$36da
	ldy	#$02
	jsr	ld8d4 ;send 2bits
	lda	$36dd
	ldy	#$08
	jsr	ld8d4 ;send 8bits
	lda	$36de
	ldy	#$08
	jsr	ld8d4 ;send 8bits
	lda	$36df
	ldy	#$04
	jsr	ld8d4 ;send 4bits
	rts

PCEのCDの型番の採番ルール

リストをみていると2通りあるようです.

発売社名+CD+年+用途+連番

TJCD9001

  • TJ: 発売社名. 基本2文字だが、ハドソンだけ H の1文字を使っている.
  • CD
  • 9: 年. 198x か 199x の1桁目を示す. 1988 から 1996 までで 7 は存在していないようだ.
  • 0: おそらく用途. 0 はゲーム. 詳しくは後述する.
  • 01: シリアル番号. 01 から順番に振られる番号.

年やシリアル番号は採番時の順番であるので、発売日の順番とは異なる可能性がある.

発売社名+PR+不明+用途?+連番

NAPR-1032

  • NA: 社名. NA か NI.
  • PR
  • 1: 不明. ボナンザブラザーズだけ2. ほかは1.
  • 0: おそらく用途であると思われる
  • 32: シリアル番号.

NEC Avenue, NEC Interchannel だけがこれを使った理由は不明. NEC Home Electronics は HECD で慣例を使っている.

おそらく用途

ゲーム(0)ではないものをリストアップする.

HRCD-9101 ロムロムカラオケ ボリューム1
HRCD-9102 ロムロムカラオケ ボリューム2
HRCD-9103 ロムロムカラオケ ボリューム3
HRCD-9104 ロムロムカラオケ ボリューム4
HRCD-9105 ロムロムカラオケ ボリューム5
JCCD9501 ロムロムカラオケ VOL1 すてきに スタンダード
JCCD9502 ロムロムカラオケ VOL2 なっとく アイドル
JCCD9503 ロムロムカラオケ VOL3 やっぱし バンド
JCCD9504 ロムロムカラオケ VOL4 ちょいと おとな!?
JCCD9505 ロムロムカラオケ VOL5 カラオケ 幕の内
JCCD0601 ウルトラBOX創刊号
JCCD0602 ウルトラBOX 2号
JCCD0603 ウルトラBOX 3号
JCCD1604 ウルトラBOX 4号
JCCD1605 ウルトラBOX 5号
JCCD2606 ウルトラBOX 6号

1,5,6 が振られている. 1 と 5 はカラオケで別に振られている理由は不明.
シリアルナンバーは用途別に 01 から順番に振られるので JCCD から始まり 01 で終わるソフトは3つある.

ユナ1作目の型番

MAME の pcecd.xml で謎だったものを実物で確認しました.

HCD5078 銀河お嬢様伝説ユナ HuVIDEO同梱再販版

銀河お嬢様伝説ユナの(1作目, PCE)は 1992 年に発売された DISC 1枚のパッケージが HCD2031 で、 1995 年に発売された DISC 2 枚のパッケージが HCD5078 です.
またパッケージの型番は DISC 自体の型番も流用されており、両方のパッケージともにゲーム本編のディスクが HCD2031 で HuVideo のディスクが HCD5078 と印字されています.

この場合はパッケージとしての型番かディスクとしての型番かで話が変わってきまして、私はディスクとしての型番を優先したい立場にあります. 似たような例がうる星やつらにもあるみたいです.

データベースをみるにこのゲームディスクはバージョン違いもあるらしいのですが、ディスクへの印刷で見分けることはできないようです. このソフトではないですがときめきメモリアルはバージョン違いが多いのか見分け方が詳しくかかれているのをインターネット上で(いまのところ)見つけることができます.

作りかけの展示モードとシステムカードのパッチ

UperGrafx は何度か展示会に出しているのですが、ゲームの切り替えが手動であるために展示時間やタイトルに偏りがあるので自動で切り替えることにしました. またゲームを遊ばないが動かしたいというユーザーも一定数いる気がしますので有用な機能かもしれません.

ここで重要なのがシステムカードの起動画面で RUN button を押す操作です. これを省けば全て自動操作になります. しかしこれはオリジナルプログラムの改変が必要です.

展示モードは一般公開してもいいのですが、システムカードのパッチがいることや操作が煩雑なこと UGX-01 非対応ということでここに記載した非公式機能とする予定です.

対応方法 / UperGrafx 側

現在公開中の pac にはありませんので使えません. 対応した pac file を公開しましたらここに記載します.

システムカードのパッチ共通事項

  • システムカードは起動直後にボタンを何も押していない場合は自動的に RUN button を押した操作をすることになっています. 起動直後にボタンを押している場合は通常の動作をします.
  • CRC32 の算出方法は zlib の方法です.
  • 該当する ROM image へ IPS パッチを適用してください.

Super System Card

このファイルは CD 読み込み高速化パッチに RUN ボタン操作不要を追加したものです. UperGrafx に ROM を登録した後は ROM+RAM card の設定にしてください.

CD-ROM2 System Card version 2.0

CD 読み込み高速化パッチ非対応です. 単なる ROM card でいいので設定の変更の操作はありません.

Game Express CD Card (派手な方)

水着のおねーさんがでてくる派手な方です. CD 読み込み高速化パッチ非対応です. UperGrafx に ROM を登録した後は ROM+RAM card の設定にしてください.

Game Express CD Card (地味な方)

地味な画面のほうです. ほかの説明は派手な方と同じです.

Mingw系のgccで生成される exe ファイルの main() の argv の文字コード

タイトルが長い...
筆者は msys2 を常用し開発作業をしています. 昨日コマンドラインの引数 (main() の argv) にUTF-8の日本語文字列を渡しても正常に動かないケースがあり原因を調べました. そもそも日本語文字列をターミナルで使うということが10年間ぐらいなくてなんでいまさらと、いう話ですけど.

経緯

  • > echo.exe "お尻"
  • > hoge.exe "お尻"
  • main() の argv を調べたらなぜか SJIS になっている
  • shell の locale は関係ない
  • cmd.exe からでも関係ないし、その文字コードも関係ない

調べた

文字コードのことはかいてないのですが、Win32 ネイティブだとパス引数が変換され、互換レイヤだとパス引数が変換されないとあります. また「MSYSのコマンド群はPOSIX互換レイヤで動いています」と書かれているので echo は UTF-8 をいれても UTF-8 がでる理由もつじつまが合います.
http://7shi.hateblo.jp/entry/2012/05/05/220750

このパス引数の変換は /c/windows を c:/windows に変換してくれるというもので、msys2 の shell からは使う身としては大変便利だと思っています. しかしこれが誤動作して困るという記述は世界中にあります.

UTF-8 の文字列を SJIS に勝手に変換するのはコマンド実行から main() 実行の間にパス変換のついでに行われる根拠を見つけることはできませんでした. また変換先が SJIS になるというのも日本語設定の Windows を使っているからのはずで、別の言語だとまた別の文字コードに変換されるのかなど謎が多いです.

対策

そこにあるように msys 互換レイヤとか cygwin を使うなど exe の作成過程をかえることによって直せるものだと思われます. 本当のところはネイティブの exe リンク時に設定を変えられるのが最適なのですけど.

今回の機能は私だけがデバッグに必要なもので、仕方なく iconv を利用して変換された SJIS を main() の最初に UTF-8 に戻すということで回避しました.

高解像対応をやりたいその3

1080p を対応した後に整数を可変に循環させて平均として小数倍をだすというアイディアを教えてもらったので実装しました. 理想的な pixel aspect ratio にそしてある程度近づけることができました. *1

各種欠点は消せてませんが下記の2解像度でそれぞれ2種類の pixel aspect ratio をだすことにしています.

      |div2       |div3       |div4
理想値|0.583  --- |0.875  --- |1.167  ---
720pA |0.667 1.143|1.000 1.143|1.333 1.143
720pB | ---   --- |0.875 1.000|1.167 1.000
1080pA|0.625 1.071|1.000 1.143|1.250 1.071
1080pB|0.588 1.008|0.941 1.076|1.176 1.008

* 720pB.div2 は正常に絵が出ない. 設計ミスと性能の限界で直せない.

  • divN = vce dotlock = pce master clock / N
  • 1つ目の値: pixel aspect ratio
  • 2つ目の値: 理想との比率(倍率, 理想値 / 1つ目の値)

pixel aspect ratio の計算方法

      |vertical   |horizonal.2|horizonal.3        |horizonal.4
720pA |3          |2          |3                  |4
720pB |3          |(1+2+2+2)/4|(2+3+2+3+2+3+3+3)/8|(3+4)/2
1080pA|4          |(2+3)/2    |4                  |5
1080pB|(4+4+4+5)/4|(2+3)/2    |4                  |5

理想値は256x224 pixel の領域を 4:3 という仮定*2で下記の計算する.
224/3*N/256

切り替え可能

720p の A と B, 1080p の A と B は設定メニューで切り替えができます.
この都合で pixel aspect ratio on 4x3 での 3x3 は廃止にします.

*1:もちろん平均として小数倍なので、スクロール時の波打ちなどの違和感はあります.

*2:CRTでは横幅をユーザーが調節できるので仮定

高解像度対応をやりたいその2

複数の解像度用のパラメータを切り替える仕組みを事前につくり、実験をしてみました. 実験内容はゲーム画面ではなく単純なテストパターンをだすだけです.

結果は 1600x1200 が映らない以外はわりと良好でした. 4:3 の画面でも音声の転送は(なぜか)うまくできていました. 1600x1200 が映らない理由は LCD 側の out of range だと思われますが、pixel clock が 162 MHz, TMDS encoding 後は10倍の 1.6 GHz になりますのでタイミング違反の可能性もあります.

気になる点: LCD 側の横引き延ばし設定

手元にある2つのモニタでは 4:3 の画面は横に引き延ばされてしまったので引き延ばしなしの設定 4:3 へ変えました. その後 16:9 の画面を写したところ今度は 4:3 を維持して 16:9 の画面を横に縮められてしまいました. 2つともです.

他のモニタでは引き延ばしをやめるだけにできた記憶があるんですが... 手動でこの設定を毎回やるのはふざけていると思いました.

気になる点: dotclock = master clock / 2 mode

縦に4倍に伸ばす場合にpixel aspect ratio は 3:4 (0.75) にするつもりでしたが、これは 2:3 (0.67) より大きくなりまして、画面に収まりません.

このモードは縦4倍モードでは対応しないつもりです.

内部処理を考えていたのですが 720p mode と比べて矛盾点が増えすぎていること、使っているソフトが少ないこと(知っている限り3本ぐらい)、真剣に対応すると時間がかかることが理由です.

気になる点: 1280x960 mode

実装をある程度やるつもりですが計算上やはり横幅が足りていないのが気になります. pixel aspect ratio 5:4 の場合は 256 pixel の場合は確かに画面に収まるのですがその左右にある backdrop 領域がまったくでません.
pixel aspect ratio 4:4 の場合は画面に収まらないのでいまのイメージ選択画面の左端のカーソルも表示されないと思われます.

ただし縦に余白が出ないことと、 pixel clock が 720p の次に遅いのも開発者としては利点と考えております.

高解像度対応をやりたいその1

UperGrafx プロジェクトは 10月18日から20日に米国オレゴン州ポートランドで開催される Portland Video Game Expo に間借りして出展する予定です. → https://www.retrogamingexpo.com/
出展準備があまり進んでおらず出展内容は UGX-01/UGX-02 での高解像度対応をするつもりです. 理由は短時間で準備ができそうだからです.

高解像度対応の意義

720p では PCE の画面がすこし横に長い欠点があり、これを補いたいというのが目的です. こういったレトロゲーム機の映像は画質や応答速度ではアナログの CRT が最高なのは事実です. 一方 CRT の入手や維持が困難だったり、体積や重量が大きいというのも事実です.
こういった中ディジタル映像化を進めるには複数の要素をよくさせることはできますがいくつかの要素を犠牲にする必要があると開発者は考えております.

ドットバイドット

開発者はドットを整数倍で表示することを最優先にしております. 小数倍も理論上できるのですがアナログを介せばできることで、その欠点は英語で Jailbar と呼ばれているそうです. 整数倍である以上闇雲に解像度をあげてもよくなるということではありません.

720p の解像度は PCE 特有の 1 pixel の横幅を可変にできる仕様に対して下記の利点があります.

  • どの LCD でも対応している
  • 音声を TMDS によって転送できる
  • pixel clock が早くないので設計がわりと楽
  • scanline の本数が 240 の 3 の整数倍
  • 1365 master clock / scanline から 2,3,4 で除算して PCE の dotclock をだすことと表示の整数倍に矛盾が少ない.
    • 描画期間中に pixel aspect ratio を変更する2本のソフト(あすか120と竜虎の拳)で自然(だと思う)

欠点は先述の1点のみです.

  • 大半のソフトで使う 1365 / 4 mode のとき 1 pce pixel aspect ratio が 4:3 で横に長い

設計当初は CRT の横の長さはユーザーが決められるので正解はないと勝手に決めて気にしていなかったのですが、絵に詳しいひとから不評です.

縦4倍モード

ここからは 720p より高解像度の説明をいたします. それらの共通事項は下記となります.

1280x960 (screen aspect ratio 4:3)

利点

  • 縦方向は 240 の整数倍でモニターが映れば余白がない

欠点

  • 横方向の解像度が足りない (R-TYPE は 336x240 pixel でその 4x4 倍で 1344x960 pixel の領域が必要)
  • どの LCD でも対応しているのか不安
  • 音声の転送規格がないので調査が必要

1920x1080 (1080p, screen aspect ratio 16:9)

利点

  • 対応している LCD がとても多い
  • 音声を TMDS によって転送できる
  • 横方向の解像度が足りる

欠点

  • 縦方向の解像度が 240 の整数倍ではなく対応に困る
  • pixel clock が 148.5 MHz と結構早いので設計に困りそう

240 の倍数ではない点は縦4倍の場合は 120 line は余白になります. 縦5倍の場合は NTSC の 224 *5 であっても 40 line (PCE で 8 line) 足りません.

縦5倍モード

pixel aspect ratio は 6:5, 5:5, 4:5 になりますがこれは細いと思われます.

1600x1200 (screen aspect ratio 4:3)

利点欠点は 1280x960 と同じで欠点が1つ加わります.
欠点

  • pixel clock 162 MHz として早すぎる

1920x1200 (screen aspect ratio 16:10)

240 * 5 の値かつ、横に余裕があるこれが理想なのかもしれませんが... これは UGX-01, UGX-02 の仕様上、性能不足で動きません. pixel clock が 192.16 MHz となり利用している TMDS encoder IC の限界および DVI の規定の最大値の 165 MHz を超えています.
TMDS として転送できる DVI と HDMI の共通は pixel clock 165 MHz までで DVI なら Dual Channel を選択し, HDMI では上位規格を選ぶ必要があります. また Display Port などの別規格も選択候補となります.

よてい

いろいろ書いてみましたが候補となる3解像度を試してみて本当に使えるのかの確認をします.