コンストラクタが継承されないのでサブクラスで全部書き並べる必要がありめんどい
解決策がなさそうなので面倒にならないようにうまいことやれ、くらいか
event の null チェックめんどい
public class EventTestClass { public event EventHandler NewEvent = delegate { }; protected void OnNewEvent() { NewEvent(this, EventArgs.Empty); } }http://www.dailycoding.com/Posts/avoiding_event__null_check.aspx
ふむふむ(あとで確かめる)
ファミコンとはいったい・・・うごごご
さてそろそろオリジナルのゲームをつくろうかなと思ったところで、ファミコンで作ろうとすると並大抵でなく苦労するであろうことに気付いてしまった
VBlank中に描け
VRAMのアクセスはVBlank中にしなくてはいけない。VBlank中かどうかを確認できたらいいと思うんだけど、そういうことはできるのだろうか。
renderLoop: ; VRAM に書き込むコード lda $2002 bmi renderLoop ; VBlank 中は $2002 の 7bit on
のようにしたいのだけど、$2002を read するとフラグがクリアされるので上のループは期待通り動かない。
クロック数を計るしかないのかな。29859 cycle/frame で VBlank が 22/262 scanline 分らしいので、VBlank中は2507cycle。またスプライトDMA転送は513cycleかかるらしい。
単純にバッファをネームテーブルに送る場合を考えると
ldx size ; 4 loop: lda buffer, x ; 4 sta $2007 ; 4 dex ; 2 bne loop ; 2
12cycle/byte なので、VBlank中に最大 200 byte 程度書き込める。1画面が 32*30 だから、それの 1/5 くらい。とこんな感じでしょうか。属性テーブルとの兼ね合いで1回に 32*4=128 を書こうとすると結構失敗する。
乱数生成
手軽に8bitの乱数っぽいものを作りたい。
N := (N xor $AA) + 73;
http://www.programmersheaven.com/mb/Console/100277/100277/nes-random-numbers/
試す。
irb(main):001:0> hash = {}; (0..0x100).inject(0) {|rnd, i| print rnd, ' '; print "\n#{i} / cycle\n" or break if hash[rnd]; hash[rnd] = ((rnd^0xaa) + 73) % 0x100 }
0 243 162 81 68 55 230 149 136 107 10 233 140 111 14 237 144 131 114 33 212 199 182 101 24 251 154 121 28 255 158 125 32 211 194 177 100 23 6 245 168 75 42 201 172 79 46 205 176 99 18 1 244 167 86 69 56 219 186 89 60 223 190 93 64 51 226 145 132 119 38 213 200 171 74 41 204 175 78 45 208 195 178 97 20 7 246 165 88 59 218 185 92 63 222 189 96 19 2 241 164 87 70 53 232 139 106 9 236 143 110 13 240 163 82 65 52 231 150 133 120 27 250 153 124 31 254 157 128 115 34 209 196 183 102 21 8 235 138 105 12 239 142 109 16 3 242 161 84 71 54 229 152 123 26 249 156 127 30 253 160 83 66 49 228 151 134 117 40 203 170 73 44 207 174 77 48 227 146 129 116 39 214 197 184 91 58 217 188 95 62 221 192 179 98 17 4 247 166 85 72 43 202 169 76 47 206 173 80 67 50 225 148 135 118 37 216 187 90 57 220 191 94 61 224 147 130 113 36 215 198 181 104 11 234 137 108 15 238 141 112 35 210 193 180 103 22 5 248 155 122 25 252 159 126 29 0
256 / cycle
おー1周した。いや偶奇があってればなんでも1周するっぽいな。LFSRっていうのを理解すればいいのかな。
線形帰還シフトレジスタ - Wikipedia
まあいいや。
http://codebase64.org/doku.php?id=base:6502_6510_maths#random_numbers
そんな感じのお手軽乱数を16bitなり32bitなりで作ってそのうちの8bitを使えば、という感じか。
MMC3(マッパー4)でラスタスクロール
ギコ猫でもわかるファミコンプログラミング - ラスタースクロール を MMC3 で書き換えたもの。画面走査のタイミングのプログラムだなんて、ファミコンはおっかない世界だねぇ。
参考文書
;;; MMC3 でラスタスクロールするテスト ;;; http://gikofami.fc2web.com/nes/nes015.html の改変 ;;; giko3.pal, giko3.bkg は上のページから拾ってね .inesprg 1 ; プログラムバンク数 .ineschr 1 ; CHR バンク数 .inesmir 0 ; 水平ミラーリング .inesmap 4 ; mapper #4 (MMC3) .bank 0 .org $C000 SIZE: .equ 3 ; スクロールする領域の数 lines: .byte 120, 50 ; スクロールする領域の高さ。スキャンライン数 speeds: .byte 0, 1, 2 ; スクロールスピード target: .equ $00 ; 次の IRQ でこのインデックスのスクロール領域を設定する scrolls: .equ $01 ; 現在のスクロールX位置. ここから SIZE bytes ;;; スタート/リセット時に呼ばれる start: ;; 初期化中は割り込み禁止 sei ; IRQ lda #%00001000 ; NMI sta $2000 cld ; デシマルモードクリア ldx #$ff ; スタック初期化 txs jsr vwait ; VBlank待ち lda #%00000110 ; 初期化中はスプライトとBGをOFF sta $2001 lda #$40 ; frame counter off sta $4017 ; (mapper のみ IRQ 発行) lda #$01 sta $a000 ; MMC3 水平ミラーリング lda #$00 sta $a001 ; これなに? ;; ゼロページ初期化 lda #$00 ldx #$00 .initZeroPage: sta <$00, x inx bne .initZeroPage ;; パレット初期化 ldx #$3F ; PPU $3F00 stx $2006 ldx #$00 stx $2006 .initPalette lda PALETTE_DATA, x sta $2007 inx cpx #$20 bne .initPalette ;; ネームテーブル生成 (空と地面を480こずつ) lda #$20 ; PPU $2000 sta $2006 lda #$00 sta $2006 lda #$00 ; 空 ldx #250 jsr writeVRAM ldx #230 jsr writeVRAM lda #$01 ; 地面 ldx #250 jsr writeVRAM ldx #230 jsr writeVRAM lda #%00011110 ; スプライトとBGの表示をONにする sta $2001 lda #%10001000 ; NMI割り込み許可 sta $2000 cli ; IRQ割り込み許可 .loop jmp .loop ;;; VBlank のタイミングで呼ばれる nmi: lda #$00 sta target jsr scroll rti ;;; scroll でセットしたスキャンラインのタイミングで呼ばれる irq: ;; レジスタをスタックに退避 pha txa pha tya pha jsr scroll ;; レジスタを IRQ 前の状態に復帰 pla tay pla tax pla rti ;;; target 番目のスクロールをセット ;;; target のインクリメントと IRQ のセットもする scroll: lda $2002 ; スクロールクリア ldx target lda scrolls, x sta $2005 ; set scroll X lda #$00 sta $2005 ; Y スクロールは固定 lda scrolls, x clc adc speeds, x sta scrolls, x ; increment scroll ;; target をインクリメント inx cpx #SIZE beq .quit ;; 次の IRQ セット dex lda lines, x ; IRQ を呼ぶまでのスキャンライン sta $c000 sta $c001 lda #$01 sta $e000 ; acknowledge the IRQ sta $e001 ; enable the IRQ inx stx target rts .quit ;; 最後なら IRQ 終わり lda #$01 sta $e000 ldx #$00 stx target rts ;;; VBlank 待ち vwait: .wait: lda $2002 bpl .wait rts ;;; a を $2007 に x 回書き込む writeVRAM: .write: sta $2007 dex bne .write rts PALETTE_DATA: .incbin "giko3.pal" .bank 1 .org $fffa .dw nmi, start, irq .bank 2 .incbin "giko3.bkg"