More Emulator Fixes. Player 2 Controller now always Works. Mega Man Tas runs up until NMI Glitch

This commit is contained in:
Sarah Jamie Lewis 2021-11-23 00:09:32 -08:00
parent f7110dcd50
commit bcd34e519c
9 changed files with 100 additions and 99 deletions

View File

@ -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),
}
}

View File

@ -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) {

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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;
}
}
_ => {}
};
}
}
}

View File

@ -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];
}
}
}

View File

@ -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();

View File

@ -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;
}