FF1 のクリスタルを獲得するシーンの解析

やる気が出てきましたので調べました。

このシーンの初期化命令は $da42 から始まり、 $da7f から vblank 割り込みを経由したループになっています。今回の逆アセンブルはコメントの冒頭に数値がついたら CPU cycle となります。

loop のメイン処理

DA7F: 
	jsr  $DAD2 ;sequece end, wait vblank interrupt

;wait renderring start line
	ldx  $10 ;3, skip line count
DA84: ;uses 114 cycle (last 113 cycle) / loop
	jsr  $DAF4 ;6 + 103 cycle
	dex  ;2
	bne  $DA84 ;3 or 2

;color -> monochrome renderring line
	ldx  $11 ;3, effective line count
DA8C: ;uses 116 cycle (last 115 cycle) / loop
	jsr  $DAFF; 6+ 57 + 42 (monochorme) + 6 cycle
	dex  ;2
	bne  $DA8C ;3 or 2

loop 開始

$dad2 は renderring frame の終了処理, vblank interrupt の割り込み待ちが入っています。スクロールレジスタの bit0 を毎フレーム 0 か 1 に切り換えているのは、画面をぶるぶるさせているものと思われます。

;renderring squence end, wait vblank
DAD2: 
;generate noice sound?
	lda  $11
	lsr  a
	lsr  a
	lsr  a
	ora  #$30
	sta  $400C
	sta  $4004
;update scroll register
	and  #$01
	eor  $1F
	sta  $2005 ;scroll register port
;wait next vblank interrupt
	jsr  $FE00
	lda  #$02
	sta  $4014 ;sprite dma register
	ldy  #$06
DAF0: 
	dey  
	bne  $DAF0
	rts  

割り込み処理

jsr $fe00 の先は、無限ループ -> 割り込み発生 -> jsr $fe00 から戻ってくるとなります。3回の pla は割り込み発生アドレス ($fe00) を破棄するために pop を 3回やってから、 rti ではなく rts を使っています。

FEEE: jmp  $FEEE
FECF: lda  $FF
FED1: sta  $2000
FED4: lda  $2002
FED7: pla  
FED8: pla  
FED9: pla  
FEDA: rts  

表示調整待ち

jsr $DAF4 を伴う最初の小ループはライン単位の時間つぶしです。$da84 から次の $da84 までの消費サイクル数は私が数えたら 114 でした。解析文書によると1 line 当たりの消費サイクル数は 113.66 (1364/12) らしいので、CPU レベルの精度では適切な設定値だと思われます。

;2+ (2+3) * 0x11 + 2+2 = 91
DAF4: 
	ldy  #$12 ;2
DAF6: 
	dey  ;2
	bne  $DAF6 ;3 or 2
;2 + 2 + 2 + 6
	nop  ;2
	nop  ;2
	nop  ;2
	rts  ;6

$2005 への頻繁な書き込み

jsr $DAFF を伴う2つ目の小ループは color/monochrome レジスタを切り換えるループで、モノクロになってる期間は 36 cycle です。この小ループの消費サイクル数は 116 で、ライン単位待ちより 2 cycle 多いです。このため、発生する時間が1ラインあたり 2 cycle ずれるので、1ラインあたりで右に約 1 pixel ずれたモノクロ領域が出せるものと思われます。

このループで消費サイクルを 114 にしたら長方形のモノクロ領域が表示できるかもしれません。CPU から pixel 単位の計測は精度が粗いので、途中でずれる可能も多々あり、斜めにする方が違和感がないのかもしれません。

PAL 版の FF1 が存在するかは知りませんが、仮にあった場合は消費サイクル数を 106.57 (1598/15) に合わせて調整されていると予想されます。

DAFD:
	nop
	nop

;switch monochrome/color register
;2+ (2+3) * 9 + 2+2 = 51
DAFF: 
	ldy  #$0A; 2
DB01: 
	dey  ;2
	bne  $DB01 ;3 or 2
;2+4 = 6
	lda  #$1F ;2
	sta  $2001 ;4, monochrome, display sprite and tile layer
;2*3 = 6
	ldy  #$1E ;2
	nop  ;2
	nop  ;2
;6*4+2*3+6 = 36
	jsr  $DB19 ;6+6
	jsr  $DB19 ;6+6
	nop  ;2
	nop  ;2
	nop  ;2
	sty  $2001 ;6 color, display sprite and tile layer
DB19:
	rts ;6 

6502 のブランチ命令 (ここでは bne) は分岐発生時に 3 cycle, 分岐なしで 2 cycle となりますが、分岐先のアドレス bit15:8 が異なると 4 cycle かかります。
$DAFD での2つの nop はこれが発生しないように調整しています。これがないと 分岐先が $DAFF, 分岐元が $DB01 になりますから、ここまで気を利かせてるんですね。すごいや Nasir.

というわけで、0番スプライトも使わずに計算してました。Vblank 割り込みの実装も無茶ですな。