More Emulator Fixes. Player 2 Controller now always Works. Mega Man Tas runs up until NMI Glitch
This commit is contained in:
parent
f7110dcd50
commit
bcd34e519c
|
@ -32,7 +32,7 @@ impl Mapper for Cnrom {
|
|||
fn write(&mut self, address: usize, value: u8) {
|
||||
match address {
|
||||
0x8000..=0xFFFF => self.chr_bank_select = (value & 0b11) as usize,
|
||||
_ => {}//println!("bad address written to CNROM mapper: 0x{:X}", address),
|
||||
_ => {} //println!("bad address written to CNROM mapper: 0x{:X}", address),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -83,13 +83,13 @@ pub struct Cpu {
|
|||
pub apu: super::Apu,
|
||||
|
||||
// controller
|
||||
pub strobe: u8, // signals to the controller that button inputs should be read
|
||||
pub strobe: bool, // signals to the controller that button inputs should be read
|
||||
pub button_states: u8, // Player 1 controller
|
||||
pub button_states2: u8, // Player 2 controller
|
||||
button_number: u8, // counter that scans the bits of the input register serially
|
||||
|
||||
button_number1: u8, // counter that scans the bits of the input register serially
|
||||
button_number2: u8, // counter that scans the bits of the input register serially
|
||||
opcode_table: Vec<fn(&mut Self, usize, Mode)>, // function table
|
||||
mode_table: Vec<Mode>, // address mode table
|
||||
mode_table: Vec<Mode>, // address mode table
|
||||
}
|
||||
|
||||
impl Cpu {
|
||||
|
@ -103,15 +103,16 @@ impl Cpu {
|
|||
s: 0xFD,
|
||||
p: 0x24,
|
||||
clock: 0,
|
||||
before_clock:0,
|
||||
before_clock: 0,
|
||||
delay: 0,
|
||||
mapper: mapper,
|
||||
ppu: ppu,
|
||||
apu: apu,
|
||||
strobe: 0,
|
||||
strobe: false,
|
||||
button_states: 0,
|
||||
button_states2: 0,
|
||||
button_number: 0,
|
||||
button_number1: 0,
|
||||
button_number2: 0,
|
||||
addresses_fetched: HashSet::new(),
|
||||
|
||||
opcode_table: vec![
|
||||
|
@ -695,8 +696,7 @@ impl Cpu {
|
|||
0x4014 => self.read_ppu_reg(8),
|
||||
0x4015 => self.apu.read_status(),
|
||||
0x4016 => self.read_controller(),
|
||||
// FIXME: Uncomment for player 2..
|
||||
// 0x4017 => self.read_controller2(),
|
||||
0x4017 => self.read_controller2(),
|
||||
0x4000..=0x4017 => 0, // can't read from these APU registers
|
||||
0x4018..=0x401F => 0, // APU and I/O functionality that is normally disabled. See CPU Test Mode.
|
||||
0x4020..=0xFFFF => self.mapper.borrow().read(address),
|
||||
|
@ -721,35 +721,32 @@ impl Cpu {
|
|||
}
|
||||
|
||||
fn read_controller(&mut self) -> u8 {
|
||||
let bit = match self.button_number < 8 {
|
||||
true => (self.button_states & (1 << self.button_number) != 0) as u8,
|
||||
false => 1,
|
||||
};
|
||||
if self.strobe & 1 != 0 {
|
||||
self.button_number = 0;
|
||||
} else {
|
||||
self.button_number += 1;
|
||||
if self.button_number1 > 7 {
|
||||
return 1;
|
||||
}
|
||||
bit
|
||||
let response = (self.button_states & (1 << self.button_number1)) >> self.button_number1;
|
||||
if !self.strobe && self.button_number1 <= 7 {
|
||||
self.button_number1 += 1;
|
||||
}
|
||||
response
|
||||
}
|
||||
|
||||
fn read_controller2(&mut self) -> u8 {
|
||||
let bit = match self.button_number < 8 {
|
||||
true => (self.button_states2 & (1 << self.button_number) != 0) as u8,
|
||||
false => 1,
|
||||
};
|
||||
if self.strobe & 1 != 0 {
|
||||
self.button_number = 0;
|
||||
} else {
|
||||
self.button_number += 1;
|
||||
if self.button_number2 > 7 {
|
||||
return 1;
|
||||
}
|
||||
bit
|
||||
let response = (self.button_states2 & (1 << self.button_number2)) >> self.button_number2;
|
||||
if !self.strobe && self.button_number2 <= 7 {
|
||||
self.button_number2 += 1;
|
||||
}
|
||||
response
|
||||
}
|
||||
|
||||
fn write_controller(&mut self, val: u8) {
|
||||
self.strobe = val;
|
||||
if self.strobe & 1 != 0 {
|
||||
self.button_number = 0;
|
||||
self.strobe = val & 1 == 1;
|
||||
if self.strobe {
|
||||
self.button_number1 = 0;
|
||||
self.button_number2 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -759,10 +756,10 @@ impl Cpu {
|
|||
4 => self.ppu.read_oam_data(),
|
||||
7 => self.ppu.read_data(),
|
||||
8 => self.ppu.read_oam_data(),
|
||||
_ => self.ppu.recent_bits
|
||||
_ => self.ppu.recent_bits,
|
||||
};
|
||||
|
||||
return val
|
||||
return val;
|
||||
}
|
||||
|
||||
fn write_ppu_reg(&mut self, reg_num: usize, val: u8) {
|
||||
|
|
|
@ -248,7 +248,7 @@ impl super::Cpu {
|
|||
|
||||
pub fn jmp(&mut self, _address: usize, _mode: Mode) {
|
||||
if _mode == ABS {
|
||||
self.clock -=1; // this only takes 3..
|
||||
self.clock -= 1; // this only takes 3..
|
||||
}
|
||||
self.pc = _address;
|
||||
}
|
||||
|
@ -258,7 +258,7 @@ impl super::Cpu {
|
|||
let minus1 = self.pc - 1; // so m1 is the last _byte of the jsr instruction. second _byte of the operand.
|
||||
self.push((minus1 >> 8) as u8);
|
||||
self.push((minus1 & 0xFF) as u8);
|
||||
self.clock+=2; //(+4 from absolute)
|
||||
self.clock += 2; //(+4 from absolute)
|
||||
self.pc = _address;
|
||||
}
|
||||
|
||||
|
@ -321,7 +321,6 @@ impl super::Cpu {
|
|||
self.a |= self.read(_address);
|
||||
self.set_zero_flag(self.a);
|
||||
self.set_negative_flag(self.a);
|
||||
|
||||
}
|
||||
|
||||
pub fn pha(&mut self, _address: usize, _mode: Mode) {
|
||||
|
@ -503,9 +502,9 @@ impl super::Cpu {
|
|||
}
|
||||
|
||||
if _mode == Mode::INY {
|
||||
self.clock = self.before_clock+6; // Special
|
||||
self.clock = self.before_clock + 6; // Special
|
||||
} else if _mode == Mode::ABY {
|
||||
self.clock = self.before_clock+5; // Specia
|
||||
self.clock = self.before_clock + 5; // Specia
|
||||
}
|
||||
|
||||
self.write(_address, self.a);
|
||||
|
|
|
@ -13,10 +13,11 @@ pub struct CpuData {
|
|||
p: u8,
|
||||
clock: u64,
|
||||
delay: usize,
|
||||
strobe: u8,
|
||||
strobe: bool,
|
||||
button_states: u8,
|
||||
button_states2: u8,
|
||||
button_number: u8,
|
||||
button_number1: u8,
|
||||
button_number2: u8,
|
||||
mode_table: Vec<Mode>,
|
||||
}
|
||||
|
||||
|
@ -35,7 +36,8 @@ impl super::Cpu {
|
|||
strobe: self.strobe,
|
||||
button_states: self.button_states,
|
||||
button_states2: self.button_states2,
|
||||
button_number: self.button_number,
|
||||
button_number1: self.button_number1,
|
||||
button_number2: self.button_number2,
|
||||
mode_table: self.mode_table.clone(),
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +55,8 @@ impl super::Cpu {
|
|||
self.strobe = data.strobe;
|
||||
self.button_states = data.button_states;
|
||||
self.button_states2 = data.button_states2;
|
||||
self.button_number = data.button_number;
|
||||
self.button_number1 = data.button_number1;
|
||||
self.button_number2 = data.button_number2;
|
||||
self.mode_table = data.mode_table;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -127,13 +127,12 @@ impl FuzzingInput {
|
|||
|
||||
// Hacky code to parse an fm2 movie/input file into our initial fuzzing input
|
||||
pub fn load(fm2: &str) -> FuzzingInput {
|
||||
|
||||
if fm2 == "testrom" {
|
||||
return FuzzingInput{
|
||||
return FuzzingInput {
|
||||
frames: vec![],
|
||||
disable_start_after: 0,
|
||||
mutated: false
|
||||
}
|
||||
mutated: false,
|
||||
};
|
||||
}
|
||||
|
||||
let file = File::open(fm2).unwrap();
|
||||
|
|
97
src/main.rs
97
src/main.rs
|
@ -40,7 +40,7 @@ const RNG_SEED: u32 = 0x35234623;
|
|||
|
||||
// If set to a low number, this disables start presses after the given frame
|
||||
// Useful for some games where pausing does nothing to advance the game...
|
||||
const DISABLE_START_PRESSES_AFTER: usize = 500;
|
||||
const DISABLE_START_PRESSES_AFTER: usize = usize::MAX;
|
||||
|
||||
// The rate at which seed inputs become corrupted..
|
||||
const MUTATION_RATE: f64 = 0.025;
|
||||
|
@ -296,12 +296,36 @@ fn run_game(
|
|||
Err(_x) => {}
|
||||
Ok((fuzzing_input, fuzzing_state)) => {
|
||||
let mut new_frames = 0;
|
||||
|
||||
// Initialize hardware components
|
||||
let (mut cpu, mut frames) = fuzzing_state.load_state(filename.clone());
|
||||
|
||||
let mut prev_frame = frames;
|
||||
while new_frames < FRAMES_TO_CONSIDER {
|
||||
// step CPU: perform 1 cpu instruction, getting back number of clock cycles it took
|
||||
|
||||
// A the start of each new frame set our input for that frame...
|
||||
if prev_frame == 0 || prev_frame != frames {
|
||||
match fuzzing_input.get_frame_input(frames) {
|
||||
Some(input) => {
|
||||
match input.console_action {
|
||||
ConsoleAction::None => {}
|
||||
ConsoleAction::SoftReset => {
|
||||
cpu.soft_reset();
|
||||
frames += 1;
|
||||
}
|
||||
}
|
||||
|
||||
cpu.button_states = input.player_1_input;
|
||||
cpu.button_states2 = input.player_2_input;
|
||||
}
|
||||
_ => {
|
||||
cpu.button_states = 0;
|
||||
cpu.button_states2 = 0;
|
||||
}
|
||||
};
|
||||
prev_frame = frames;
|
||||
}
|
||||
|
||||
let cpu_cycles = cpu.step();
|
||||
|
||||
// clock PPU three times for every CPU cycle
|
||||
|
@ -322,33 +346,13 @@ fn run_game(
|
|||
window
|
||||
.update_with_buffer(&screen.display, 256, 240)
|
||||
.unwrap();
|
||||
prev_frame = frames;
|
||||
frames += 1;
|
||||
new_frames += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Checking for Inputs...
|
||||
|
||||
match fuzzing_input.get_frame_input(frames) {
|
||||
Some(input) => {
|
||||
match input.console_action {
|
||||
ConsoleAction::None => {}
|
||||
ConsoleAction::SoftReset => {
|
||||
println!("soft reset");
|
||||
cpu.soft_reset();
|
||||
frames+=1;
|
||||
}
|
||||
}
|
||||
if cpu.strobe & 0x01 == 0x01 {
|
||||
cpu.button_states = input.player_1_input;
|
||||
// FIXME PLayer 2 doesn't play nicely with some games (e.g. mario)
|
||||
// So to enable player 2 controls you also have to uncomment the
|
||||
// bus in cpu/mod.rs
|
||||
cpu.button_states2 = input.player_2_input;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
send_results
|
||||
|
@ -361,39 +365,42 @@ fn run_game(
|
|||
|
||||
fn play_frames(cpu: &mut Cpu, num_frames: usize, fuzzing_input: &FuzzingInput) {
|
||||
let mut played_frames = 0;
|
||||
let mut prev_frame = 0;
|
||||
while played_frames < num_frames {
|
||||
// step CPU: perform 1 cpu instruction, getting back number of clock cycles it took
|
||||
|
||||
// A the start of each new frame set our input for that frame...
|
||||
if prev_frame == 0 || prev_frame != played_frames {
|
||||
match fuzzing_input.get_frame_input(played_frames) {
|
||||
Some(input) => {
|
||||
match input.console_action {
|
||||
ConsoleAction::None => {}
|
||||
ConsoleAction::SoftReset => {
|
||||
cpu.soft_reset();
|
||||
played_frames += 1;
|
||||
}
|
||||
}
|
||||
|
||||
cpu.button_states = input.player_1_input;
|
||||
cpu.button_states2 = input.player_2_input;
|
||||
}
|
||||
_ => {
|
||||
cpu.button_states = 0;
|
||||
cpu.button_states2 = 0;
|
||||
}
|
||||
};
|
||||
prev_frame = played_frames;
|
||||
}
|
||||
|
||||
let cpu_cycles = cpu.step();
|
||||
|
||||
// clock PPU three times for every CPU cycle
|
||||
for _ in 0..cpu_cycles * 3 {
|
||||
let (_, end_of_frame) = cpu.ppu.clock();
|
||||
if end_of_frame {
|
||||
prev_frame = played_frames;
|
||||
played_frames += 1;
|
||||
}
|
||||
|
||||
// Checking for Inputs...
|
||||
|
||||
match fuzzing_input.get_frame_input(played_frames) {
|
||||
Some(input) => {
|
||||
match input.console_action {
|
||||
ConsoleAction::None => {}
|
||||
ConsoleAction::SoftReset => {
|
||||
cpu.soft_reset();
|
||||
}
|
||||
}
|
||||
if cpu.strobe & 0x01 == 0x01 {
|
||||
cpu.button_states = input.player_1_input;
|
||||
|
||||
// FIXME PLayer 2 doesn't play nicely with some games (e.g. mario)
|
||||
// So to enable player 2 controls you also have to uncomment the
|
||||
// bus in cpu/mod.rs
|
||||
cpu.button_states2 = input.player_2_input;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ impl super::Ppu {
|
|||
// cpu reads from 0x2004, OAMDATA
|
||||
pub fn read_oam_data(&mut self) -> u8 {
|
||||
self.recent_bits = self.primary_oam[self.oam_address];
|
||||
return self.recent_bits
|
||||
return self.recent_bits;
|
||||
}
|
||||
|
||||
// cpu writes to 0x2004, OAMDATA
|
||||
|
@ -211,7 +211,7 @@ impl super::Ppu {
|
|||
pub fn write_oam_dma(&mut self, data: Vec<u8>) {
|
||||
let start = self.oam_address;
|
||||
for (i, v) in data.iter().enumerate() {
|
||||
self.primary_oam[(start+i) % 256] = data[i];
|
||||
self.primary_oam[(start + i) % 256] = data[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -161,7 +161,6 @@ impl Ppu {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
let mut pixel: Option<(usize, usize, (u8, u8, u8))> = None;
|
||||
let rendering = self.rendering();
|
||||
|
||||
|
@ -220,10 +219,6 @@ impl Ppu {
|
|||
self.nmi_change();
|
||||
}
|
||||
|
||||
if self.scanline == 261 {
|
||||
self.vertical_blank = false;
|
||||
}
|
||||
|
||||
if self.scanline == 261 && self.line_cycle == 1 {
|
||||
self.vertical_blank = false;
|
||||
self.nmi_change();
|
||||
|
|
|
@ -106,7 +106,8 @@ impl super::Ppu {
|
|||
palette_address += background_pixel; // Pixel value from tile data
|
||||
} else if background_pixel != 0 && sprite_pixel != 0 {
|
||||
if self.sprite_indexes[current_sprite] == 0 {
|
||||
if x != 255 { // Sprite 0 does not hit At x=255, for an obscure reason related to the pixel pipeline.
|
||||
if x != 255 {
|
||||
// Sprite 0 does not hit At x=255, for an obscure reason related to the pixel pipeline.
|
||||
// don't access index current_sprite. need to know which sprite we're on horizontally.
|
||||
self.sprite_zero_hit = true;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue