続き
スプライト実装をする前に、Nestestを通すために、PAD入力をサクッと実装する。
NES on FPGA PAD
キー入力
Piston windowでキー入力をとるのはEvent::Inputが来たとき。
本当はInputでパターンマッチした引数でうまくやれるかもしれんが、逆に長くなったので、やめた。
while let Some(event) = window.next() { match event { Event::Loop(Loop::Render(_)) => { //略 } Event::Input(_, _) => { if let Some(Button::Keyboard(key)) = event.press_args() { match key { Key::J => nes.pad.press(pad::PadButton::A), Key::G => { let tmp = !nes.ppu.grid_on.get(); nes.ppu.grid_on.set(tmp); } _ => {} } } if let Some(Button::Keyboard(key)) = event.release_args() { match key { Key::J => nes.pad.release(pad::PadButton::A), _ => {} } } } _ => {} }
press/releaseで一旦NES内のPADステータス値を更新する。
PAD読み込み
NESプログラムがPADの状態を得るためには1Pなら0x4016、2Pなら0x4017に一定の操作を行う。
0x01書き込み→0x00書き込みで読み込み可能状態になるのでPADか、NESの内部に状態(strobe)を持っておく。
pub fn write8(&self, addr: u16, data: u8) { match addr { ... 0x4016 => { let strobe = (data & 0x01) != 0; //data == 1 -> true if strobe { self.pad.strobe_enable.set(true); } else { self.pad.shift_index.set(0); self.pad.strobe_enable.set(false); } } .... } }
その後、0x4016(0x4017)をリードするたびに下から順にビットを判定し、1か0を返す。
bitflags! { #[derive(Default)] pub struct PadButton:u8{ const A = 1 << 0; const B = 1 << 1; const START = 1 << 2; const SELECT = 1 << 3; const UP = 1 << 4; const DOWN = 1 << 5; const LEFT = 1 << 6; const RIGHT = 1 << 7; } }
pub fn read8(&self, addr: u16) -> u8 { match addr { .... 0x4016 => { let s = self.pad.shift_index.get(); if s > 7 { return 1; } self.pad.shift_index.set(s + 1); let btn = if !self.pad.strobe_enable.get() { let scanbtn = pad::PadButton::from_bits_truncate(0x01 << s); self.pad.status.get().contains(scanbtn) } else { true }; btn as u8 } 0x4017 => 0, .... } }
2Pコントローラー(0x4017)を実装しないときは、しっかり0を返すべきで、ここで0以外の値を返していると、ずっと2P入力されていることになってスーパーマリオブラザーズでタイトル画面を操作できない。
nestest
前回の背景描写と合わせて、PAD操作ができれば、nestestを実行することができる。