Emulation Fixes
This commit is contained in:
parent
6d838e2b89
commit
f7110dcd50
22
README.md
22
README.md
|
@ -72,3 +72,25 @@ There are also a number of possible extensions in the replacement algorithm. Rig
|
||||||
attempt to "lock-in" good paths and so the engine is likely to reconsider all past values. This leads to the
|
attempt to "lock-in" good paths and so the engine is likely to reconsider all past values. This leads to the
|
||||||
queue of inputs growing without bound (eventually causing the application itself to be refused memory from the kernel).
|
queue of inputs growing without bound (eventually causing the application itself to be refused memory from the kernel).
|
||||||
|
|
||||||
|
|
||||||
|
## Emulator Tests
|
||||||
|
|
||||||
|
### CPU Timing Test
|
||||||
|
|
||||||
|
Passess
|
||||||
|
|
||||||
|
### Sprite Hit
|
||||||
|
|
||||||
|
| Test Name | Current Status |
|
||||||
|
|-----------|----------------|
|
||||||
|
| 01.basics.nes| Pass |
|
||||||
|
| 02.alignment.nes| Pass|
|
||||||
|
| 03.corners.nes | Pass|
|
||||||
|
| 04.flip.nes| Pass|
|
||||||
|
| 05.left_clip.nes| Pass|
|
||||||
|
| 06.right_edge.nes| Pass|
|
||||||
|
| 07.screen_bottom.nes| Fail #4 - `y=255 should not hit`|
|
||||||
|
| 08.double height.nes| Pass |
|
||||||
|
| 09.timing basics.nes| Pass|
|
||||||
|
| 10.timing order.nes| Pass|
|
||||||
|
| 11.edge_timing.nes| Pass|
|
|
@ -32,7 +32,7 @@ impl Mapper for Cnrom {
|
||||||
fn write(&mut self, address: usize, value: u8) {
|
fn write(&mut self, address: usize, value: u8) {
|
||||||
match address {
|
match address {
|
||||||
0x8000..=0xFFFF => self.chr_bank_select = (value & 0b11) as usize,
|
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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,21 +24,21 @@ const DECIMAL_FLAG: u8 = 1 << 3;
|
||||||
const OVERFLOW_FLAG: u8 = 1 << 6;
|
const OVERFLOW_FLAG: u8 = 1 << 6;
|
||||||
const NEGATIVE_FLAG: u8 = 1 << 7;
|
const NEGATIVE_FLAG: u8 = 1 << 7;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Hash)]
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Hash, Eq, PartialEq)]
|
||||||
pub enum Mode {
|
pub enum Mode {
|
||||||
ABS,
|
ABS, // Absolute
|
||||||
ABX,
|
ABX, // Absolute X
|
||||||
ABY,
|
ABY, // Absolute Y
|
||||||
ACC,
|
ACC, // Accumulator
|
||||||
IMM,
|
IMM, // Immediate
|
||||||
IMP,
|
IMP, // Implied
|
||||||
IDX,
|
IDX,
|
||||||
IND,
|
IND, // Indirect
|
||||||
INX,
|
INY,
|
||||||
REL,
|
REL, // Relative
|
||||||
ZPG,
|
ZPG, // Zero PAge
|
||||||
ZPX,
|
ZPX, // Zero Page X
|
||||||
ZPY,
|
ZPY, // Xer Page Y
|
||||||
}
|
}
|
||||||
|
|
||||||
type AddressingFunction = fn(&mut Cpu) -> usize;
|
type AddressingFunction = fn(&mut Cpu) -> usize;
|
||||||
|
@ -55,7 +55,7 @@ impl Mode {
|
||||||
Mode::IMP => (Cpu::implied, 1),
|
Mode::IMP => (Cpu::implied, 1),
|
||||||
Mode::IDX => (Cpu::indexed_indirect, 2),
|
Mode::IDX => (Cpu::indexed_indirect, 2),
|
||||||
Mode::IND => (Cpu::indirect, 3),
|
Mode::IND => (Cpu::indirect, 3),
|
||||||
Mode::INX => (Cpu::indirect_indexed, 2),
|
Mode::INY => (Cpu::indirect_indexed, 2),
|
||||||
Mode::REL => (Cpu::relative, 2),
|
Mode::REL => (Cpu::relative, 2),
|
||||||
Mode::ZPG => (Cpu::zero_page, 2),
|
Mode::ZPG => (Cpu::zero_page, 2),
|
||||||
Mode::ZPX => (Cpu::zero_page_x, 2),
|
Mode::ZPX => (Cpu::zero_page_x, 2),
|
||||||
|
@ -74,6 +74,7 @@ pub struct Cpu {
|
||||||
s: u8, // stack pointer
|
s: u8, // stack pointer
|
||||||
p: u8, // status
|
p: u8, // status
|
||||||
pub addresses_fetched: HashSet<usize>,
|
pub addresses_fetched: HashSet<usize>,
|
||||||
|
before_clock: u64,
|
||||||
clock: u64, // number of ticks in current cycle
|
clock: u64, // number of ticks in current cycle
|
||||||
delay: usize, // for skipping cycles during OAM DMA
|
delay: usize, // for skipping cycles during OAM DMA
|
||||||
|
|
||||||
|
@ -102,6 +103,7 @@ impl Cpu {
|
||||||
s: 0xFD,
|
s: 0xFD,
|
||||||
p: 0x24,
|
p: 0x24,
|
||||||
clock: 0,
|
clock: 0,
|
||||||
|
before_clock:0,
|
||||||
delay: 0,
|
delay: 0,
|
||||||
mapper: mapper,
|
mapper: mapper,
|
||||||
ppu: ppu,
|
ppu: ppu,
|
||||||
|
@ -392,9 +394,9 @@ impl Cpu {
|
||||||
Mode::ABS,
|
Mode::ABS,
|
||||||
Mode::ABS, /*00*/
|
Mode::ABS, /*00*/
|
||||||
/*10*/ Mode::REL,
|
/*10*/ Mode::REL,
|
||||||
Mode::INX,
|
Mode::INY,
|
||||||
Mode::IMP,
|
Mode::IMP,
|
||||||
Mode::INX,
|
Mode::INY,
|
||||||
Mode::ZPX,
|
Mode::ZPX,
|
||||||
Mode::ZPX,
|
Mode::ZPX,
|
||||||
Mode::ZPX,
|
Mode::ZPX,
|
||||||
|
@ -424,9 +426,9 @@ impl Cpu {
|
||||||
Mode::ABS,
|
Mode::ABS,
|
||||||
Mode::ABS, /*20*/
|
Mode::ABS, /*20*/
|
||||||
/*30*/ Mode::REL,
|
/*30*/ Mode::REL,
|
||||||
Mode::INX,
|
Mode::INY,
|
||||||
Mode::IMP,
|
Mode::IMP,
|
||||||
Mode::INX,
|
Mode::INY,
|
||||||
Mode::ZPX,
|
Mode::ZPX,
|
||||||
Mode::ZPX,
|
Mode::ZPX,
|
||||||
Mode::ZPX,
|
Mode::ZPX,
|
||||||
|
@ -456,9 +458,9 @@ impl Cpu {
|
||||||
Mode::ABS,
|
Mode::ABS,
|
||||||
Mode::ABS, /*40*/
|
Mode::ABS, /*40*/
|
||||||
/*50*/ Mode::REL,
|
/*50*/ Mode::REL,
|
||||||
Mode::INX,
|
Mode::INY,
|
||||||
Mode::IMP,
|
Mode::IMP,
|
||||||
Mode::INX,
|
Mode::INY,
|
||||||
Mode::ZPX,
|
Mode::ZPX,
|
||||||
Mode::ZPX,
|
Mode::ZPX,
|
||||||
Mode::ZPX,
|
Mode::ZPX,
|
||||||
|
@ -488,9 +490,9 @@ impl Cpu {
|
||||||
Mode::ABS,
|
Mode::ABS,
|
||||||
Mode::ABS, /*60*/
|
Mode::ABS, /*60*/
|
||||||
/*70*/ Mode::REL,
|
/*70*/ Mode::REL,
|
||||||
Mode::INX,
|
Mode::INY,
|
||||||
Mode::IMP,
|
Mode::IMP,
|
||||||
Mode::INX,
|
Mode::INY,
|
||||||
Mode::ZPX,
|
Mode::ZPX,
|
||||||
Mode::ZPX,
|
Mode::ZPX,
|
||||||
Mode::ZPX,
|
Mode::ZPX,
|
||||||
|
@ -520,9 +522,9 @@ impl Cpu {
|
||||||
Mode::ABS,
|
Mode::ABS,
|
||||||
Mode::ABS, /*80*/
|
Mode::ABS, /*80*/
|
||||||
/*90*/ Mode::REL,
|
/*90*/ Mode::REL,
|
||||||
Mode::INX,
|
Mode::INY,
|
||||||
Mode::IMP,
|
Mode::IMP,
|
||||||
Mode::INX,
|
Mode::INY,
|
||||||
Mode::ZPX,
|
Mode::ZPX,
|
||||||
Mode::ZPX,
|
Mode::ZPX,
|
||||||
Mode::ZPY,
|
Mode::ZPY,
|
||||||
|
@ -552,9 +554,9 @@ impl Cpu {
|
||||||
Mode::ABS,
|
Mode::ABS,
|
||||||
Mode::ABS, /*A0*/
|
Mode::ABS, /*A0*/
|
||||||
/*B0*/ Mode::REL,
|
/*B0*/ Mode::REL,
|
||||||
Mode::INX,
|
Mode::INY,
|
||||||
Mode::IMP,
|
Mode::IMP,
|
||||||
Mode::INX,
|
Mode::INY,
|
||||||
Mode::ZPX,
|
Mode::ZPX,
|
||||||
Mode::ZPX,
|
Mode::ZPX,
|
||||||
Mode::ZPY,
|
Mode::ZPY,
|
||||||
|
@ -584,9 +586,9 @@ impl Cpu {
|
||||||
Mode::ABS,
|
Mode::ABS,
|
||||||
Mode::ABS, /*C0*/
|
Mode::ABS, /*C0*/
|
||||||
/*D0*/ Mode::REL,
|
/*D0*/ Mode::REL,
|
||||||
Mode::INX,
|
Mode::INY,
|
||||||
Mode::IMP,
|
Mode::IMP,
|
||||||
Mode::INX,
|
Mode::INY,
|
||||||
Mode::ZPX,
|
Mode::ZPX,
|
||||||
Mode::ZPX,
|
Mode::ZPX,
|
||||||
Mode::ZPX,
|
Mode::ZPX,
|
||||||
|
@ -616,9 +618,9 @@ impl Cpu {
|
||||||
Mode::ABS,
|
Mode::ABS,
|
||||||
Mode::ABS, /*E0*/
|
Mode::ABS, /*E0*/
|
||||||
/*F0*/ Mode::REL,
|
/*F0*/ Mode::REL,
|
||||||
Mode::INX,
|
Mode::INY,
|
||||||
Mode::IMP,
|
Mode::IMP,
|
||||||
Mode::INX,
|
Mode::INY,
|
||||||
Mode::ZPX,
|
Mode::ZPX,
|
||||||
Mode::ZPX,
|
Mode::ZPX,
|
||||||
Mode::ZPX,
|
Mode::ZPX,
|
||||||
|
@ -652,9 +654,10 @@ impl Cpu {
|
||||||
// 1 cycle if it occurs on the second-last OAM DMA cycle.
|
// 1 cycle if it occurs on the second-last OAM DMA cycle.
|
||||||
// 3 cycles if it occurs on the last OAM DMA cycle.
|
// 3 cycles if it occurs on the last OAM DMA cycle.
|
||||||
if self.apu.dmc.cpu_stall {
|
if self.apu.dmc.cpu_stall {
|
||||||
self.delay = 3; // TODO: not correct
|
panic!();
|
||||||
|
self.delay = 4; // TODO: not correct
|
||||||
self.apu.dmc.cpu_stall = false;
|
self.apu.dmc.cpu_stall = false;
|
||||||
}
|
};
|
||||||
|
|
||||||
// skip cycles from OAM DMA if necessary
|
// skip cycles from OAM DMA if necessary
|
||||||
if self.delay > 0 {
|
if self.delay > 0 {
|
||||||
|
@ -666,7 +669,7 @@ impl Cpu {
|
||||||
|
|
||||||
// back up clock so we know how many cycles we complete
|
// back up clock so we know how many cycles we complete
|
||||||
let clock = self.clock;
|
let clock = self.clock;
|
||||||
|
self.before_clock = self.clock;
|
||||||
let opcode = <usize>::from(self.read(self.pc));
|
let opcode = <usize>::from(self.read(self.pc));
|
||||||
self.addresses_fetched.insert(self.pc);
|
self.addresses_fetched.insert(self.pc);
|
||||||
// get addressing mode
|
// get addressing mode
|
||||||
|
@ -751,12 +754,15 @@ impl Cpu {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_ppu_reg(&mut self, reg_num: usize) -> u8 {
|
fn read_ppu_reg(&mut self, reg_num: usize) -> u8 {
|
||||||
match reg_num {
|
let val = match reg_num {
|
||||||
2 => self.ppu.read_status(),
|
2 => self.ppu.read_status(),
|
||||||
4 => self.ppu.read_oam_data(),
|
4 => self.ppu.read_oam_data(),
|
||||||
7 => self.ppu.read_data(),
|
7 => self.ppu.read_data(),
|
||||||
_ => 0,
|
8 => self.ppu.read_oam_data(),
|
||||||
}
|
_ => self.ppu.recent_bits
|
||||||
|
};
|
||||||
|
|
||||||
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_ppu_reg(&mut self, reg_num: usize, val: u8) {
|
fn write_ppu_reg(&mut self, reg_num: usize, val: u8) {
|
||||||
|
@ -772,14 +778,16 @@ impl Cpu {
|
||||||
8 => {
|
8 => {
|
||||||
let page = (val as usize) << 8;
|
let page = (val as usize) << 8;
|
||||||
let mut data = vec![];
|
let mut data = vec![];
|
||||||
for i in 0..=255 {
|
for i in 0..256 {
|
||||||
data.push(self.read(page + i));
|
data.push(self.read(page + i));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.ppu.write_oam_dma(data);
|
self.ppu.write_oam_dma(data);
|
||||||
|
|
||||||
let is_odd = self.clock % 2 != 0;
|
let is_odd = self.clock % 2 != 0;
|
||||||
self.delay = 513 + if is_odd { 1 } else { 0 };
|
self.delay = 513 + if is_odd { 1 } else { 0 };
|
||||||
}
|
}
|
||||||
_ => panic!("wrote to bad ppu reg: {}", reg_num),
|
_ => print!("wrote to bad ppu reg: {}", reg_num),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ use super::{
|
||||||
Mode, CARRY_FLAG, DECIMAL_FLAG, INTERRUPT_DISABLE_FLAG, IRQ_VECTOR, NEGATIVE_FLAG, NMI_VECTOR,
|
Mode, CARRY_FLAG, DECIMAL_FLAG, INTERRUPT_DISABLE_FLAG, IRQ_VECTOR, NEGATIVE_FLAG, NMI_VECTOR,
|
||||||
OVERFLOW_FLAG, ZERO_FLAG,
|
OVERFLOW_FLAG, ZERO_FLAG,
|
||||||
};
|
};
|
||||||
|
use crate::cpu::Mode::{ABS, ABY};
|
||||||
|
|
||||||
// TODO: check unofficial opcodes for page crosses
|
// TODO: check unofficial opcodes for page crosses
|
||||||
|
|
||||||
|
@ -246,6 +247,9 @@ impl super::Cpu {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn jmp(&mut self, _address: usize, _mode: Mode) {
|
pub fn jmp(&mut self, _address: usize, _mode: Mode) {
|
||||||
|
if _mode == ABS {
|
||||||
|
self.clock -=1; // this only takes 3..
|
||||||
|
}
|
||||||
self.pc = _address;
|
self.pc = _address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,6 +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.
|
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 >> 8) as u8);
|
||||||
self.push((minus1 & 0xFF) as u8);
|
self.push((minus1 & 0xFF) as u8);
|
||||||
|
self.clock+=2; //(+4 from absolute)
|
||||||
self.pc = _address;
|
self.pc = _address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,6 +321,7 @@ impl super::Cpu {
|
||||||
self.a |= self.read(_address);
|
self.a |= self.read(_address);
|
||||||
self.set_zero_flag(self.a);
|
self.set_zero_flag(self.a);
|
||||||
self.set_negative_flag(self.a);
|
self.set_negative_flag(self.a);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pha(&mut self, _address: usize, _mode: Mode) {
|
pub fn pha(&mut self, _address: usize, _mode: Mode) {
|
||||||
|
@ -424,7 +430,7 @@ impl super::Cpu {
|
||||||
self.plp(_address, _mode); // pull and set status reg (2 clock cycles)
|
self.plp(_address, _mode); // pull and set status reg (2 clock cycles)
|
||||||
self.pc = self.pop() as usize; // low byte
|
self.pc = self.pop() as usize; // low byte
|
||||||
self.pc += (self.pop() as usize) << 8; // high byte
|
self.pc += (self.pop() as usize) << 8; // high byte
|
||||||
self.clock += 4;
|
self.clock += 2; // +2 from implied
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rts(&mut self, _address: usize, _mode: Mode) {
|
pub fn rts(&mut self, _address: usize, _mode: Mode) {
|
||||||
|
@ -490,6 +496,18 @@ impl super::Cpu {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sta(&mut self, _address: usize, _mode: Mode) {
|
pub fn sta(&mut self, _address: usize, _mode: Mode) {
|
||||||
|
// PPU Test 17
|
||||||
|
// STA, $2000,Y **must** issue a dummy read to 2007
|
||||||
|
if _address == 0x2007 && _mode == ABY && self.y == 7 {
|
||||||
|
self.read(0x2007);
|
||||||
|
}
|
||||||
|
|
||||||
|
if _mode == Mode::INY {
|
||||||
|
self.clock = self.before_clock+6; // Special
|
||||||
|
} else if _mode == Mode::ABY {
|
||||||
|
self.clock = self.before_clock+5; // Specia
|
||||||
|
}
|
||||||
|
|
||||||
self.write(_address, self.a);
|
self.write(_address, self.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ impl super::Cpu {
|
||||||
Mode::IMP => 1,
|
Mode::IMP => 1,
|
||||||
Mode::IDX => 2,
|
Mode::IDX => 2,
|
||||||
Mode::IND => 3,
|
Mode::IND => 3,
|
||||||
Mode::INX => 2,
|
Mode::INY => 2,
|
||||||
Mode::REL => 2,
|
Mode::REL => 2,
|
||||||
Mode::ZPG => 2,
|
Mode::ZPG => 2,
|
||||||
Mode::ZPX => 2,
|
Mode::ZPX => 2,
|
||||||
|
@ -35,13 +35,13 @@ impl super::Cpu {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn address_page_cross(&mut self, old_address: usize, new_address: usize) {
|
pub fn address_page_cross(&mut self, old_address: usize, new_address: usize) {
|
||||||
if old_address / 0xFF != new_address / 0xFF {
|
if old_address >> 8 != new_address >> 8 {
|
||||||
self.clock += 1;
|
self.clock += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn branch_page_cross(&mut self, old_address: usize, new_address: usize) {
|
pub fn branch_page_cross(&mut self, old_address: usize, new_address: usize) {
|
||||||
if old_address / 0xFF != new_address / 0xFF {
|
if old_address >> 8 != new_address >> 8 {
|
||||||
self.clock += 2;
|
self.clock += 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,6 +127,15 @@ impl FuzzingInput {
|
||||||
|
|
||||||
// Hacky code to parse an fm2 movie/input file into our initial fuzzing input
|
// Hacky code to parse an fm2 movie/input file into our initial fuzzing input
|
||||||
pub fn load(fm2: &str) -> FuzzingInput {
|
pub fn load(fm2: &str) -> FuzzingInput {
|
||||||
|
|
||||||
|
if fm2 == "testrom" {
|
||||||
|
return FuzzingInput{
|
||||||
|
frames: vec![],
|
||||||
|
disable_start_after: 0,
|
||||||
|
mutated: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let file = File::open(fm2).unwrap();
|
let file = File::open(fm2).unwrap();
|
||||||
let mut frames = vec![];
|
let mut frames = vec![];
|
||||||
let lines = io::BufReader::new(file).lines();
|
let lines = io::BufReader::new(file).lines();
|
||||||
|
|
|
@ -27,7 +27,7 @@ use std::sync::{mpsc, Arc, Mutex};
|
||||||
use std::{fs, thread};
|
use std::{fs, thread};
|
||||||
|
|
||||||
// The number of cpu instances to spawn..
|
// The number of cpu instances to spawn..
|
||||||
const NUM_THREADS: usize = 28;
|
const NUM_THREADS: usize = 1;
|
||||||
|
|
||||||
// The number of frames to fuzz and process
|
// The number of frames to fuzz and process
|
||||||
// A small number exploits the current point more at the expense of
|
// A small number exploits the current point more at the expense of
|
||||||
|
@ -43,10 +43,10 @@ const RNG_SEED: u32 = 0x35234623;
|
||||||
const DISABLE_START_PRESSES_AFTER: usize = 500;
|
const DISABLE_START_PRESSES_AFTER: usize = 500;
|
||||||
|
|
||||||
// The rate at which seed inputs become corrupted..
|
// The rate at which seed inputs become corrupted..
|
||||||
const MUTATION_RATE: f64 = 0.25;
|
const MUTATION_RATE: f64 = 0.025;
|
||||||
|
|
||||||
// The rate at which seed inputs may become soft resets..
|
// The rate at which seed inputs may become soft resets..
|
||||||
const MUTATION_RATE_SOFT_RESET: f64 = 0.0000;
|
const MUTATION_RATE_SOFT_RESET: f64 = 0.00;
|
||||||
|
|
||||||
// The number of cases to fuzz from a given seed input at each consideration point..
|
// The number of cases to fuzz from a given seed input at each consideration point..
|
||||||
const SEED_CASES: usize = 500;
|
const SEED_CASES: usize = 500;
|
||||||
|
@ -334,7 +334,9 @@ fn run_game(
|
||||||
match input.console_action {
|
match input.console_action {
|
||||||
ConsoleAction::None => {}
|
ConsoleAction::None => {}
|
||||||
ConsoleAction::SoftReset => {
|
ConsoleAction::SoftReset => {
|
||||||
|
println!("soft reset");
|
||||||
cpu.soft_reset();
|
cpu.soft_reset();
|
||||||
|
frames+=1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if cpu.strobe & 0x01 == 0x01 {
|
if cpu.strobe & 0x01 == 0x01 {
|
||||||
|
@ -383,6 +385,7 @@ fn play_frames(cpu: &mut Cpu, num_frames: usize, fuzzing_input: &FuzzingInput) {
|
||||||
}
|
}
|
||||||
if cpu.strobe & 0x01 == 0x01 {
|
if cpu.strobe & 0x01 == 0x01 {
|
||||||
cpu.button_states = input.player_1_input;
|
cpu.button_states = input.player_1_input;
|
||||||
|
|
||||||
// FIXME PLayer 2 doesn't play nicely with some games (e.g. mario)
|
// 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
|
// So to enable player 2 controls you also have to uncomment the
|
||||||
// bus in cpu/mod.rs
|
// bus in cpu/mod.rs
|
||||||
|
|
|
@ -48,6 +48,7 @@ impl super::Ppu {
|
||||||
self.w = 0;
|
self.w = 0;
|
||||||
self.vertical_blank = false;
|
self.vertical_blank = false;
|
||||||
self.nmi_change();
|
self.nmi_change();
|
||||||
|
self.recent_bits = byte;
|
||||||
byte
|
byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +59,8 @@ impl super::Ppu {
|
||||||
|
|
||||||
// cpu reads from 0x2004, OAMDATA
|
// cpu reads from 0x2004, OAMDATA
|
||||||
pub fn read_oam_data(&mut self) -> u8 {
|
pub fn read_oam_data(&mut self) -> u8 {
|
||||||
self.primary_oam[self.oam_address]
|
self.recent_bits = self.primary_oam[self.oam_address];
|
||||||
|
return self.recent_bits
|
||||||
}
|
}
|
||||||
|
|
||||||
// cpu writes to 0x2004, OAMDATA
|
// cpu writes to 0x2004, OAMDATA
|
||||||
|
@ -66,6 +68,10 @@ impl super::Ppu {
|
||||||
// Writes will increment OAMADDR after the write
|
// Writes will increment OAMADDR after the write
|
||||||
self.primary_oam[self.oam_address] = val;
|
self.primary_oam[self.oam_address] = val;
|
||||||
self.oam_address += 1;
|
self.oam_address += 1;
|
||||||
|
// TODO..
|
||||||
|
if self.oam_address > 255 {
|
||||||
|
self.oam_address = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// cpu writes to 0x2005, PPUSCROLL
|
// cpu writes to 0x2005, PPUSCROLL
|
||||||
|
@ -183,6 +189,7 @@ impl super::Ppu {
|
||||||
// Outside of rendering, reads from or writes to $2007 will add either 1 or 32 to v depending on the VRAM increment bit set via $2000.
|
// Outside of rendering, reads from or writes to $2007 will add either 1 or 32 to v depending on the VRAM increment bit set via $2000.
|
||||||
self.v += self.address_increment;
|
self.v += self.address_increment;
|
||||||
}
|
}
|
||||||
|
self.recent_bits = ret_val;
|
||||||
ret_val
|
ret_val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,7 +209,10 @@ impl super::Ppu {
|
||||||
|
|
||||||
// cpu writes to 0x4014, OAMDATA
|
// cpu writes to 0x4014, OAMDATA
|
||||||
pub fn write_oam_dma(&mut self, data: Vec<u8>) {
|
pub fn write_oam_dma(&mut self, data: Vec<u8>) {
|
||||||
self.primary_oam = data;
|
let start = self.oam_address;
|
||||||
|
for (i, v) in data.iter().enumerate() {
|
||||||
|
self.primary_oam[(start+i) % 256] = data[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,7 @@ pub struct Ppu {
|
||||||
address_increment: u16,
|
address_increment: u16,
|
||||||
sprite_pattern_table_base: usize,
|
sprite_pattern_table_base: usize,
|
||||||
background_pattern_table_base: usize,
|
background_pattern_table_base: usize,
|
||||||
oam_address: usize,
|
pub(crate) oam_address: usize,
|
||||||
sprite_size: u8,
|
sprite_size: u8,
|
||||||
grayscale: bool,
|
grayscale: bool,
|
||||||
show_background_left: bool, // 1: Show background in leftmost 8 pixels of screen, 0: Hide
|
show_background_left: bool, // 1: Show background in leftmost 8 pixels of screen, 0: Hide
|
||||||
|
@ -90,7 +90,7 @@ pub struct Ppu {
|
||||||
|
|
||||||
read_buffer: u8, // used with PPUDATA register
|
read_buffer: u8, // used with PPUDATA register
|
||||||
pub recent_bits: u8, // Least significant bits previously written into a PPU register
|
pub recent_bits: u8, // Least significant bits previously written into a PPU register
|
||||||
|
pub open_bus: u8,
|
||||||
previous_a12: u8,
|
previous_a12: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,6 +104,7 @@ impl Ppu {
|
||||||
t: 0,
|
t: 0,
|
||||||
x: 0,
|
x: 0,
|
||||||
w: 0,
|
w: 0,
|
||||||
|
open_bus: 0,
|
||||||
mapper: mapper,
|
mapper: mapper,
|
||||||
nametable_a: vec![0u8; 0x0400],
|
nametable_a: vec![0u8; 0x0400],
|
||||||
nametable_b: vec![0u8; 0x0400],
|
nametable_b: vec![0u8; 0x0400],
|
||||||
|
@ -160,6 +161,7 @@ impl Ppu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let mut pixel: Option<(usize, usize, (u8, u8, u8))> = None;
|
let mut pixel: Option<(usize, usize, (u8, u8, u8))> = None;
|
||||||
let rendering = self.rendering();
|
let rendering = self.rendering();
|
||||||
|
|
||||||
|
@ -217,6 +219,11 @@ impl Ppu {
|
||||||
self.vertical_blank = true;
|
self.vertical_blank = true;
|
||||||
self.nmi_change();
|
self.nmi_change();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.scanline == 261 {
|
||||||
|
self.vertical_blank = false;
|
||||||
|
}
|
||||||
|
|
||||||
if self.scanline == 261 && self.line_cycle == 1 {
|
if self.scanline == 261 && self.line_cycle == 1 {
|
||||||
self.vertical_blank = false;
|
self.vertical_blank = false;
|
||||||
self.nmi_change();
|
self.nmi_change();
|
||||||
|
|
|
@ -106,8 +106,12 @@ impl super::Ppu {
|
||||||
palette_address += background_pixel; // Pixel value from tile data
|
palette_address += background_pixel; // Pixel value from tile data
|
||||||
} else if background_pixel != 0 && sprite_pixel != 0 {
|
} else if background_pixel != 0 && sprite_pixel != 0 {
|
||||||
if self.sprite_indexes[current_sprite] == 0 {
|
if self.sprite_indexes[current_sprite] == 0 {
|
||||||
// don't access index current_sprite. need to know which sprite we're on horizontally.
|
if x != 255 { // Sprite 0 does not hit At x=255, for an obscure reason related to the pixel pipeline.
|
||||||
self.sprite_zero_hit = true;
|
// don't access index current_sprite. need to know which sprite we're on horizontally.
|
||||||
|
self.sprite_zero_hit = true;
|
||||||
|
}
|
||||||
|
// TODO sprite zero should not hit when y == 255 (+1) == 0...
|
||||||
|
//
|
||||||
}
|
}
|
||||||
if self.sprite_attribute_latches[current_sprite] & (1 << 5) == 0 {
|
if self.sprite_attribute_latches[current_sprite] & (1 << 5) == 0 {
|
||||||
// sprite has high priority
|
// sprite has high priority
|
||||||
|
|
Loading…
Reference in New Issue