not starting audio till buffer has data, fixed snake case warnings, loading save state by time modified, not filename. high-pitched ringing is gone from audio thanks to filters!

This commit is contained in:
Theron 2020-03-05 19:50:56 -06:00
parent 13d617059c
commit ede3545260
12 changed files with 277 additions and 270 deletions

View File

@ -31,7 +31,7 @@ If the game is called `mygame.nes`, the save state files will be called `mygame-
## Use ## Use
Double-click or run the executable from a terminal by itself to launch with instructions. Then click Ok and drag a (iNES/`.nes`) ROM file onto the window. Or, drag and drop a ROM file on to the executable to run it directly, or use the path to the ROM file as the first argument to the terminal command. Double-click or run the executable from a terminal by itself to launch with instructions. Then click Ok and drag a (iNES/`.nes`) ROM file onto the window. Or, drag and drop a ROM file onto the executable to run it directly, or use the path to the ROM file as the first argument to the terminal command.
If the game uses battery-backed RAM (if it can save data when the console is turned off), a save file like `rom_filename.sav` will be created in the same folder as the ROM when the program is exited. When Nestur is run again, it will look for a file matching the ROM name, with a `.sav` extension instead of `.nes`. If the game uses battery-backed RAM (if it can save data when the console is turned off), a save file like `rom_filename.sav` will be created in the same folder as the ROM when the program is exited. When Nestur is run again, it will look for a file matching the ROM name, with a `.sav` extension instead of `.nes`.

View File

@ -1,4 +1,3 @@
use std::time::{Duration, Instant};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use sdl2::Sdl; use sdl2::Sdl;
use sdl2::audio::{AudioCallback, AudioSpecDesired}; use sdl2::audio::{AudioCallback, AudioSpecDesired};
@ -80,7 +79,7 @@ impl AudioCallback for ApuSampler {
*b = b.split_off(target); *b = b.split_off(target);
} }
} else { } else {
// println!("buffer empty!"); // happens when the callback fires twice between video frames println!("buffer empty!"); // happens when the callback fires twice between video frames
} }
} }
} }

View File

@ -3,15 +3,15 @@ impl super::Cpu {
pub fn absolute(&mut self) -> usize { pub fn absolute(&mut self) -> usize {
self.clock += 4; self.clock += 4;
<usize>::from( <usize>::from(
((self.read(self.PC + 2) as usize) << 8) + // high byte, little endian ((self.read(self.pc + 2) as usize) << 8) + // high byte, little endian
(self.read(self.PC + 1)) as usize // low byte (self.read(self.pc + 1)) as usize // low byte
) )
} }
pub fn absolute_x(&mut self) -> usize { pub fn absolute_x(&mut self) -> usize {
let current_opcode = self.read(self.PC); let current_opcode = self.read(self.pc);
let old_address = self.absolute(); let old_address = self.absolute();
let new_address = old_address + self.X as usize; let new_address = old_address + self.x as usize;
match current_opcode { match current_opcode {
0x1C | 0x1D | 0x3C | 0x3D | 0x5C | 0x5D | 0x7C | 0x7D | 0xBC | 0xBD | 0xDC | 0xDD | 0xFC | 0xFD 0x1C | 0x1D | 0x3C | 0x3D | 0x5C | 0x5D | 0x7C | 0x7D | 0xBC | 0xBD | 0xDC | 0xDD | 0xFC | 0xFD
=> self.address_page_cross(old_address, new_address), => self.address_page_cross(old_address, new_address),
@ -23,9 +23,9 @@ impl super::Cpu {
} }
pub fn absolute_y(&mut self) -> usize { pub fn absolute_y(&mut self) -> usize {
let current_opcode = self.PC; let current_opcode = self.pc;
let old_address = self.absolute() as u16; // coerce to u16 for wrapping addition let old_address = self.absolute() as u16; // coerce to u16 for wrapping addition
let new_address = old_address.wrapping_add(self.Y as u16); let new_address = old_address.wrapping_add(self.y as u16);
let old_address = old_address as usize; // coerce back let old_address = old_address as usize; // coerce back
let new_address = new_address as usize; let new_address = new_address as usize;
if current_opcode == 0x99 { if current_opcode == 0x99 {
@ -43,7 +43,7 @@ impl super::Cpu {
pub fn immediate(&mut self) -> usize { pub fn immediate(&mut self) -> usize {
self.clock += 2; self.clock += 2;
self.PC + 1 self.pc + 1
} }
pub fn implied(&mut self) -> usize { pub fn implied(&mut self) -> usize {
@ -53,8 +53,8 @@ impl super::Cpu {
pub fn indexed_indirect(&mut self) -> usize { pub fn indexed_indirect(&mut self) -> usize {
self.clock += 6; self.clock += 6;
let operand = self.read(self.PC + 1); let operand = self.read(self.pc + 1);
let zp_low_addr = operand.wrapping_add(self.X); let zp_low_addr = operand.wrapping_add(self.x);
let zp_high_addr = zp_low_addr.wrapping_add(1); // take account of zero page wraparound let zp_high_addr = zp_low_addr.wrapping_add(1); // take account of zero page wraparound
let zp_low_byte = self.read(zp_low_addr as usize); let zp_low_byte = self.read(zp_low_addr as usize);
let zp_high_byte = self.read(zp_high_addr as usize); let zp_high_byte = self.read(zp_high_addr as usize);
@ -62,8 +62,8 @@ impl super::Cpu {
} }
pub fn indirect(&mut self) -> usize { pub fn indirect(&mut self) -> usize {
let operand_address = ((self.read(self.PC + 2) as usize) << 8) let operand_address = ((self.read(self.pc + 2) as usize) << 8)
+ (self.read(self.PC + 1) as usize); + (self.read(self.pc + 1) as usize);
let low_byte = self.read(operand_address) as usize; let low_byte = self.read(operand_address) as usize;
// BUG TIME! from https://wiki.nesdev.com/w/index.php/Errata // BUG TIME! from https://wiki.nesdev.com/w/index.php/Errata
// "JMP ($xxyy), or JMP indirect, does not advance pages if the lower eight bits // "JMP ($xxyy), or JMP indirect, does not advance pages if the lower eight bits
@ -80,14 +80,14 @@ impl super::Cpu {
} }
pub fn indirect_indexed(&mut self) -> usize { pub fn indirect_indexed(&mut self) -> usize {
let operand = self.read(self.PC + 1); let operand = self.read(self.pc + 1);
let zp_low_addr = operand; let zp_low_addr = operand;
let zp_high_addr = operand.wrapping_add(1); let zp_high_addr = operand.wrapping_add(1);
let zp_low_byte = self.read(zp_low_addr as usize); let zp_low_byte = self.read(zp_low_addr as usize);
let zp_high_byte = self.read(zp_high_addr as usize); let zp_high_byte = self.read(zp_high_addr as usize);
let old_address = ((zp_high_byte as u16) << 8) + zp_low_byte as u16; let old_address = ((zp_high_byte as u16) << 8) + zp_low_byte as u16;
let new_address = old_address.wrapping_add(self.Y as u16); let new_address = old_address.wrapping_add(self.y as u16);
if self.PC == 0xF1 { if self.pc == 0xF1 {
self.clock += 1; self.clock += 1;
} else { } else {
self.address_page_cross(old_address as usize, new_address as usize); self.address_page_cross(old_address as usize, new_address as usize);
@ -98,25 +98,25 @@ impl super::Cpu {
pub fn relative(&mut self) -> usize { pub fn relative(&mut self) -> usize {
self.clock += 2; self.clock += 2;
self.PC + 1 self.pc + 1
} }
pub fn zero_page(&mut self) -> usize { pub fn zero_page(&mut self) -> usize {
let operand = self.read(self.PC + 1); let operand = self.read(self.pc + 1);
self.clock += 3; self.clock += 3;
operand as usize operand as usize
} }
pub fn zero_page_x(&mut self) -> usize { pub fn zero_page_x(&mut self) -> usize {
let operand = self.read(self.PC + 1); let operand = self.read(self.pc + 1);
self.clock += 4; self.clock += 4;
operand.wrapping_add(self.X) as usize operand.wrapping_add(self.x) as usize
} }
pub fn zero_page_y(&mut self) -> usize { pub fn zero_page_y(&mut self) -> usize {
let operand = self.read(self.PC + 1); let operand = self.read(self.pc + 1);
self.clock += 4; self.clock += 4;
operand.wrapping_add(self.Y) as usize operand.wrapping_add(self.y) as usize
} }
} }

View File

@ -55,12 +55,12 @@ impl Mode {
pub struct Cpu { pub struct Cpu {
mem: Vec<u8>, // CPU's RAM, $0000-$1FFF mem: Vec<u8>, // CPU's RAM, $0000-$1FFF
A: u8, // accumulator a: u8, // accumulator
X: u8, // general purpose x: u8, // general purpose
Y: u8, // general purpose y: u8, // general purpose
PC: usize, // 16-bit program counter pc: usize, // 16-bit program counter
S: u8, // stack pointer s: u8, // stack pointer
P: u8, // status p: u8, // status
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
@ -82,10 +82,10 @@ impl Cpu {
pub fn new(mapper: Rc<RefCell<dyn Mapper>>, ppu: super::Ppu, apu: super::Apu) -> Self { pub fn new(mapper: Rc<RefCell<dyn Mapper>>, ppu: super::Ppu, apu: super::Apu) -> Self {
let mut cpu = Cpu{ let mut cpu = Cpu{
mem: vec![0; 0x2000], mem: vec![0; 0x2000],
A: 0, X: 0, Y: 0, a: 0, x: 0, y: 0,
PC: 0, pc: 0,
S: 0xFD, s: 0xFD,
P: 0x24, p: 0x24,
clock: 0, clock: 0,
delay: 0, delay: 0,
mapper: mapper, mapper: mapper,
@ -133,7 +133,7 @@ impl Cpu {
/*F0*/ Mode::REL, Mode::INX, Mode::IMP, Mode::INX, Mode::ZPX, Mode::ZPX, Mode::ZPX, Mode::ZPX, Mode::IMP, Mode::ABY, Mode::IMP, Mode::ABY, Mode::ABX, Mode::ABX, Mode::ABX, Mode::ABX, /*F0*/ /*F0*/ Mode::REL, Mode::INX, Mode::IMP, Mode::INX, Mode::ZPX, Mode::ZPX, Mode::ZPX, Mode::ZPX, Mode::IMP, Mode::ABY, Mode::IMP, Mode::ABY, Mode::ABX, Mode::ABX, Mode::ABX, Mode::ABX, /*F0*/
], ],
}; };
cpu.PC = ((cpu.read(RESET_VECTOR + 1) as usize) << 8) + cpu.read(RESET_VECTOR) as usize; cpu.pc = ((cpu.read(RESET_VECTOR + 1) as usize) << 8) + cpu.read(RESET_VECTOR) as usize;
cpu cpu
} }
@ -151,12 +151,12 @@ impl Cpu {
} }
self.ppu.trigger_nmi = false; self.ppu.trigger_nmi = false;
// and apu // and apu
if self.apu.trigger_irq && (self.P & INTERRUPT_DISABLE_FLAG == 0) { if self.apu.trigger_irq && (self.p & INTERRUPT_DISABLE_FLAG == 0) {
self.irq(); self.irq();
} }
self.apu.trigger_irq = false; self.apu.trigger_irq = false;
// and mapper MMC3 // and mapper MMC3
if self.mapper.borrow_mut().check_irq() && (self.P & INTERRUPT_DISABLE_FLAG == 0) { if self.mapper.borrow_mut().check_irq() && (self.p & INTERRUPT_DISABLE_FLAG == 0) {
self.irq(); self.irq();
} }
// TODO: should checks for APU and MMC3 IRQs be combined and acknowledged together? // TODO: should checks for APU and MMC3 IRQs be combined and acknowledged together?
@ -164,7 +164,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;
let opcode = <usize>::from(self.read(self.PC)); let opcode = <usize>::from(self.read(self.pc));
// get addressing mode // get addressing mode
let mode = self.mode_table[opcode].clone(); let mode = self.mode_table[opcode].clone();
@ -264,7 +264,7 @@ impl Cpu {
} }
fn _debug(&mut self, num_bytes: usize, opcode: usize) { fn _debug(&mut self, num_bytes: usize, opcode: usize) {
let pc = self.PC; let pc = self.pc;
let operands = match num_bytes { let operands = match num_bytes {
1 => " ".to_string(), 1 => " ".to_string(),
2 => format!("{:02X} ", self.read(pc + 1)), 2 => format!("{:02X} ", self.read(pc + 1)),
@ -273,7 +273,7 @@ impl Cpu {
}; };
println!("{:04X} {:02X} {} {} A:{:02X} X:{:02X} Y:{:02X} P:{:02X} SP:{:02X}", println!("{:04X} {:02X} {} {} A:{:02X} X:{:02X} Y:{:02X} P:{:02X} SP:{:02X}",
pc, self.read(pc), operands, _OPCODE_DISPLAY_NAMES[opcode], pc, self.read(pc), operands, _OPCODE_DISPLAY_NAMES[opcode],
self.A, self.X, self.Y, self.P, self.S, self.a, self.x, self.y, self.p, self.s,
); );
} }

View File

@ -6,14 +6,14 @@ impl super::Cpu {
pub fn adc(&mut self, _address: usize, _mode: Mode) { pub fn adc(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address); let byte = self.read(_address);
let carry_bit = if self.P & CARRY_FLAG == 0 {0} else {1}; let carry_bit = if self.p & CARRY_FLAG == 0 {0} else {1};
let mut new_val = self.A.wrapping_add(byte); // add the byte at the _address to accum let mut new_val = self.a.wrapping_add(byte); // add the byte at the _address to accum
new_val = new_val.wrapping_add(carry_bit); // add carry flag to accumulator new_val = new_val.wrapping_add(carry_bit); // add carry flag to accumulator
// set carry flag if we wrapped around and added something // set carry flag if we wrapped around and added something
if new_val <= self.A && (byte != 0 || carry_bit != 0) { if new_val <= self.a && (byte != 0 || carry_bit != 0) {
self.P |= CARRY_FLAG; self.p |= CARRY_FLAG;
} else { } else {
self.P &= 0xFF - CARRY_FLAG; self.p &= 0xFF - CARRY_FLAG;
} }
self.set_zero_flag(new_val); self.set_zero_flag(new_val);
self.set_negative_flag(new_val); self.set_negative_flag(new_val);
@ -21,23 +21,23 @@ impl super::Cpu {
// sign is positive if num & 0x80 == 0, negative if num & 0x80 != 0 // sign is positive if num & 0x80 == 0, negative if num & 0x80 != 0
// ((sum & 0x80 != 0) && (acc & 0x80 == 0) && (operand & 0x80 == 0)) || ((sum & 0x80 == 0) && (acc & 0x80 != 0) && (operand & 0x80 != 0)) // ((sum & 0x80 != 0) && (acc & 0x80 == 0) && (operand & 0x80 == 0)) || ((sum & 0x80 == 0) && (acc & 0x80 != 0) && (operand & 0x80 != 0))
// simplifies to the below, thanks http://www.righto.com/2012/12/the-6502-overflow-flag-explained.html // simplifies to the below, thanks http://www.righto.com/2012/12/the-6502-overflow-flag-explained.html
if (byte ^ new_val) & (self.A ^ new_val) & 0x80 != 0 { if (byte ^ new_val) & (self.a ^ new_val) & 0x80 != 0 {
self.P |= OVERFLOW_FLAG; self.p |= OVERFLOW_FLAG;
} else { } else {
self.P &= 0xFF - OVERFLOW_FLAG; self.p &= 0xFF - OVERFLOW_FLAG;
} }
self.A = new_val; // actually change the accumulator self.a = new_val; // actually change the accumulator
} }
pub fn and(&mut self, _address: usize, _mode: Mode) { pub fn and(&mut self, _address: usize, _mode: Mode) {
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 asl(&mut self, _address: usize, _mode: Mode) { pub fn asl(&mut self, _address: usize, _mode: Mode) {
let mut val = match _mode { let mut val = match _mode {
Mode::ACC => self.A, Mode::ACC => self.a,
_ => { _ => {
self.clock += 2; self.clock += 2;
self.read(_address) self.read(_address)
@ -45,13 +45,13 @@ impl super::Cpu {
}; };
// put top bit in carry flag // put top bit in carry flag
if val & (1<<7) != 0 { if val & (1<<7) != 0 {
self.P |= CARRY_FLAG; self.p |= CARRY_FLAG;
} else { } else {
self.P &= 0xFF - CARRY_FLAG; self.p &= 0xFF - CARRY_FLAG;
} }
val <<= 1; val <<= 1;
match _mode { match _mode {
Mode::ACC => self.A = val, Mode::ACC => self.a = val,
_ => self.write(_address, val), _ => self.write(_address, val),
}; };
self.set_zero_flag(val); self.set_zero_flag(val);
@ -60,67 +60,67 @@ impl super::Cpu {
pub fn bcc(&mut self, _address: usize, _mode: Mode) { pub fn bcc(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address); let byte = self.read(_address);
if self.P & CARRY_FLAG == 0 { if self.p & CARRY_FLAG == 0 {
self.branch(byte); self.branch(byte);
} }
} }
pub fn bcs(&mut self, _address: usize, _mode: Mode) { pub fn bcs(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address); let byte = self.read(_address);
if self.P & CARRY_FLAG != 0 { if self.p & CARRY_FLAG != 0 {
self.branch(byte); self.branch(byte);
} }
} }
pub fn beq(&mut self, _address: usize, _mode: Mode) { pub fn beq(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address); let byte = self.read(_address);
if self.P & ZERO_FLAG != 0 { if self.p & ZERO_FLAG != 0 {
self.branch(byte); self.branch(byte);
} }
} }
pub fn bit(&mut self, _address: usize, _mode: Mode) { pub fn bit(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address); let byte = self.read(_address);
let tested = byte & self.A; let tested = byte & self.a;
self.set_zero_flag(tested); self.set_zero_flag(tested);
let bit6 = byte & (1 << 6); let bit6 = byte & (1 << 6);
if bit6 != 0 { if bit6 != 0 {
self.P |= OVERFLOW_FLAG; self.p |= OVERFLOW_FLAG;
} else { } else {
self.P &= 0xFF - OVERFLOW_FLAG; self.p &= 0xFF - OVERFLOW_FLAG;
} }
let bit7 = byte & (1 << 7); let bit7 = byte & (1 << 7);
if bit7 != 0 { if bit7 != 0 {
self.P |= NEGATIVE_FLAG; self.p |= NEGATIVE_FLAG;
} else { } else {
self.P &= 0xFF - NEGATIVE_FLAG; self.p &= 0xFF - NEGATIVE_FLAG;
} }
} }
pub fn bmi(&mut self, _address: usize, _mode: Mode) { pub fn bmi(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address); let byte = self.read(_address);
if self.P & NEGATIVE_FLAG != 0 { if self.p & NEGATIVE_FLAG != 0 {
self.branch(byte); self.branch(byte);
} }
} }
pub fn bne(&mut self, _address: usize, _mode: Mode) { pub fn bne(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address); let byte = self.read(_address);
if self.P & ZERO_FLAG == 0 { if self.p & ZERO_FLAG == 0 {
self.branch(byte); self.branch(byte);
} }
} }
pub fn bpl(&mut self, _address: usize, _mode: Mode) { pub fn bpl(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address); let byte = self.read(_address);
if self.P & NEGATIVE_FLAG == 0 { if self.p & NEGATIVE_FLAG == 0 {
self.branch(byte); self.branch(byte);
} }
} }
pub fn brk(&mut self, _address: usize, _mode: Mode) { pub fn brk(&mut self, _address: usize, _mode: Mode) {
// instr_test-v5/rom_singles/15-brk.nes and instr_test-v5/rom_singles/16-special.nes: // instr_test-v5/rom_singles/15-brk.nes and instr_test-v5/rom_singles/16-special.nes:
// using self.PC + 1 in these next two lines allows these tests to pass. // using self.pc + 1 in these next two lines allows these tests to pass.
// I'm not sure why that's necessary as implied addressing mode is only supposed to consume 1 byte, // I'm not sure why that's necessary as implied addressing mode is only supposed to consume 1 byte,
// but the error message from 16-special.nes said "BRK should push address BRK + 2" // but the error message from 16-special.nes said "BRK should push address BRK + 2"
@ -130,65 +130,65 @@ impl super::Cpu {
// routines called by BRK always return 2 bytes after the actual BRK opcode, // routines called by BRK always return 2 bytes after the actual BRK opcode,
// and not just 1. // and not just 1.
self.push(((self.PC + 1) >> 8) as u8); // push high byte self.push(((self.pc + 1) >> 8) as u8); // push high byte
self.push(((self.PC + 1) & 0xFF) as u8); // push low byte self.push(((self.pc + 1) & 0xFF) as u8); // push low byte
self.push(self.P | 0b00110000); // push status register with break bits set self.push(self.p | 0b00110000); // push status register with break bits set
self.P |= INTERRUPT_DISABLE_FLAG; // set interrupt disable flag self.p |= INTERRUPT_DISABLE_FLAG; // set interrupt disable flag
self.PC = ((self.read(IRQ_VECTOR + 1) as usize) << 8) // set program counter to IRQ/BRK vector, taking high byte self.pc = ((self.read(IRQ_VECTOR + 1) as usize) << 8) // set program counter to IRQ/BRK vector, taking high byte
+ (self.read(IRQ_VECTOR) as usize); // and low byte + (self.read(IRQ_VECTOR) as usize); // and low byte
self.clock += 5; // total of 7 cycles, 2 come from implied() self.clock += 5; // total of 7 cycles, 2 come from implied()
} }
pub fn bvc(&mut self, _address: usize, _mode: Mode) { pub fn bvc(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address); let byte = self.read(_address);
if self.P & OVERFLOW_FLAG == 0 { if self.p & OVERFLOW_FLAG == 0 {
self.branch(byte); self.branch(byte);
} }
} }
pub fn bvs(&mut self, _address: usize, _mode: Mode) { pub fn bvs(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address); let byte = self.read(_address);
if self.P & OVERFLOW_FLAG != 0 { if self.p & OVERFLOW_FLAG != 0 {
self.branch(byte); self.branch(byte);
} }
} }
pub fn clc(&mut self, _address: usize, _mode: Mode) { pub fn clc(&mut self, _address: usize, _mode: Mode) {
self.P &= 0xFF - CARRY_FLAG; self.p &= 0xFF - CARRY_FLAG;
} }
pub fn cld(&mut self, _address: usize, _mode: Mode) { pub fn cld(&mut self, _address: usize, _mode: Mode) {
self.P &= 0xFF - DECIMAL_FLAG; self.p &= 0xFF - DECIMAL_FLAG;
} }
pub fn cli(&mut self, _address: usize, _mode: Mode) { pub fn cli(&mut self, _address: usize, _mode: Mode) {
self.P &= 0xFF - INTERRUPT_DISABLE_FLAG; self.p &= 0xFF - INTERRUPT_DISABLE_FLAG;
} }
pub fn clv(&mut self, _address: usize, _mode: Mode) { pub fn clv(&mut self, _address: usize, _mode: Mode) {
self.P &= 0xFF - OVERFLOW_FLAG; self.p &= 0xFF - OVERFLOW_FLAG;
} }
pub fn cmp(&mut self, _address: usize, _mode: Mode) { pub fn cmp(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address); let byte = self.read(_address);
self.compare(self.A, byte); self.compare(self.a, byte);
} }
pub fn cpx(&mut self, _address: usize, _mode: Mode) { pub fn cpx(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address); let byte = self.read(_address);
self.compare(self.X, byte); self.compare(self.x, byte);
} }
pub fn cpy(&mut self, _address: usize, _mode: Mode) { pub fn cpy(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address); let byte = self.read(_address);
self.compare(self.Y, byte); self.compare(self.y, byte);
} }
pub fn dcp(&mut self, _address: usize, _mode: Mode) { pub fn dcp(&mut self, _address: usize, _mode: Mode) {
// unofficial // unofficial
let val = self.read(_address).wrapping_sub(1); let val = self.read(_address).wrapping_sub(1);
self.write(_address, val); self.write(_address, val);
self.compare(self.A, val); self.compare(self.a, val);
} }
pub fn dec(&mut self, _address: usize, _mode: Mode) { pub fn dec(&mut self, _address: usize, _mode: Mode) {
@ -200,21 +200,21 @@ impl super::Cpu {
} }
pub fn dex(&mut self, _address: usize, _mode: Mode) { pub fn dex(&mut self, _address: usize, _mode: Mode) {
self.X = self.X.wrapping_sub(1); self.x = self.x.wrapping_sub(1);
self.set_zero_flag(self.X); self.set_zero_flag(self.x);
self.set_negative_flag(self.X); self.set_negative_flag(self.x);
} }
pub fn dey(&mut self, _address: usize, _mode: Mode) { pub fn dey(&mut self, _address: usize, _mode: Mode) {
self.Y = self.Y.wrapping_sub(1); self.y = self.y.wrapping_sub(1);
self.set_zero_flag(self.Y); self.set_zero_flag(self.y);
self.set_negative_flag(self.Y); self.set_negative_flag(self.y);
} }
pub fn eor(&mut self, _address: usize, _mode: Mode) { pub fn eor(&mut self, _address: usize, _mode: Mode) {
self.A ^= self.read(_address); self.a ^= self.read(_address);
self.set_negative_flag(self.A); self.set_negative_flag(self.a);
self.set_zero_flag(self.A); self.set_zero_flag(self.a);
} }
pub fn inc(&mut self, _address: usize, _mode: Mode) { pub fn inc(&mut self, _address: usize, _mode: Mode) {
@ -232,77 +232,76 @@ impl super::Cpu {
} }
pub fn inx(&mut self, _address: usize, _mode: Mode) { pub fn inx(&mut self, _address: usize, _mode: Mode) {
self.X = self.X.wrapping_add(1); self.x = self.x.wrapping_add(1);
self.set_zero_flag(self.X); self.set_zero_flag(self.x);
self.set_negative_flag(self.X); self.set_negative_flag(self.x);
} }
pub fn iny(&mut self, _address: usize, _mode: Mode) { pub fn iny(&mut self, _address: usize, _mode: Mode) {
self.Y = self.Y.wrapping_add(1); self.y = self.y.wrapping_add(1);
self.set_zero_flag(self.Y); self.set_zero_flag(self.y);
self.set_negative_flag(self.Y); self.set_negative_flag(self.y);
} }
pub fn jmp(&mut self, _address: usize, _mode: Mode) { pub fn jmp(&mut self, _address: usize, _mode: Mode) {
// TODO: bug here? self.pc = _address;
self.PC = _address;
} }
pub fn jsr(&mut self, _address: usize, _mode: Mode) { pub fn jsr(&mut self, _address: usize, _mode: Mode) {
// call to absolute already advances program counter by 3 // call to absolute already advances program counter by 3
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.PC = _address; self.pc = _address;
} }
pub fn lax(&mut self, _address: usize, _mode: Mode) { pub fn lax(&mut self, _address: usize, _mode: Mode) {
// unofficial opcode that sets both X and accumulator // unofficial opcode that sets both X and accumulator
// TODO: check cycle count? https://wiki.nesdev.com/w/index.php/Programming_with_unofficial_opcodes // TODO: check cycle count? https://wiki.nesdev.com/w/index.php/Programming_with_unofficial_opcodes
let byte = self.read(_address); let byte = self.read(_address);
self.A = byte; self.a = byte;
self.X = byte; self.x = byte;
self.set_zero_flag(byte); self.set_zero_flag(byte);
self.set_negative_flag(byte); self.set_negative_flag(byte);
} }
pub fn lda(&mut self, _address: usize, _mode: Mode) { pub fn lda(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address); let byte = self.read(_address);
self.A = byte; self.a = byte;
self.set_zero_flag(byte); self.set_zero_flag(byte);
self.set_negative_flag(byte); self.set_negative_flag(byte);
} }
pub fn ldx(&mut self, _address: usize, _mode: Mode) { pub fn ldx(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address); let byte = self.read(_address);
self.X = byte; self.x = byte;
self.set_zero_flag(byte); self.set_zero_flag(byte);
self.set_negative_flag(byte); self.set_negative_flag(byte);
} }
pub fn ldy(&mut self, _address: usize, _mode: Mode) { pub fn ldy(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address); let byte = self.read(_address);
self.Y = byte; self.y = byte;
self.set_zero_flag(byte); self.set_zero_flag(byte);
self.set_negative_flag(byte); self.set_negative_flag(byte);
} }
pub fn lsr(&mut self, _address: usize, _mode: Mode) { pub fn lsr(&mut self, _address: usize, _mode: Mode) {
let mut val = match _mode { let mut val = match _mode {
Mode::ACC => self.A, Mode::ACC => self.a,
_ => { _ => {
self.clock += 2; self.clock += 2;
self.read(_address) self.read(_address)
}, },
}; };
if val & 0x1 == 0x1 { if val & 0x1 == 0x1 {
self.P |= CARRY_FLAG; self.p |= CARRY_FLAG;
} else { } else {
self.P &= 0xFF - CARRY_FLAG; self.p &= 0xFF - CARRY_FLAG;
} }
val >>= 1; val >>= 1;
match _mode { match _mode {
Mode::ACC => self.A = val, Mode::ACC => self.a = val,
_ => self.write(_address, val), _ => self.write(_address, val),
}; };
self.set_zero_flag(val); self.set_zero_flag(val);
@ -313,49 +312,49 @@ impl super::Cpu {
} }
pub fn ora(&mut self, _address: usize, _mode: Mode) { pub fn ora(&mut self, _address: usize, _mode: Mode) {
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) {
self.clock += 1; self.clock += 1;
self.push(self.A); self.push(self.a);
} }
pub fn php(&mut self, _address: usize, _mode: Mode) { pub fn php(&mut self, _address: usize, _mode: Mode) {
self.clock += 1; self.clock += 1;
self.push(self.P | 0b00110000); self.push(self.p | 0b00110000);
} }
pub fn pla(&mut self, _address: usize, _mode: Mode) { pub fn pla(&mut self, _address: usize, _mode: Mode) {
self.clock += 2; self.clock += 2;
self.A = self.pop(); self.a = self.pop();
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 plp(&mut self, _address: usize, _mode: Mode) { pub fn plp(&mut self, _address: usize, _mode: Mode) {
self.clock += 2; self.clock += 2;
self.P = self.pop(); self.p = self.pop();
// TODO: figure out exactly what's supposed to happen here // TODO: figure out exactly what's supposed to happen here
// let status = self.pop(); // let status = self.pop();
// // for each bit in the popped status, if it's 1, // // for each bit in the popped status, if it's 1,
// // set that bit of self.P to 1. if it's 0, set that // // set that bit of self.p to 1. if it's 0, set that
// // bit of self.P to 0. // // bit of self.p to 0.
// for i in 0..=7 { // for i in 0..=7 {
// if i == 4 || i == 5 { // if i == 4 || i == 5 {
// continue; // ignore B flags // continue; // ignore B flags
// } // }
// let bit = if status & (1 << i) == 0 {0} else {1}; // let bit = if status & (1 << i) == 0 {0} else {1};
// if bit != 0 { // if bit != 0 {
// self.P |= 1 << i; // self.p |= 1 << i;
// } else { // } else {
// self.P &= 0xFF - (1 << i); // self.p &= 0xFF - (1 << i);
// } // }
// } // }
// self.P |= 1 << 5; // turn on bit 5 // self.p |= 1 << 5; // turn on bit 5
// self.P &= 0xFF - (1 << 4); // and turn off bit 4 because god knows why // self.p &= 0xFF - (1 << 4); // and turn off bit 4 because god knows why
} }
pub fn rla(&mut self, _address: usize, _mode: Mode) { pub fn rla(&mut self, _address: usize, _mode: Mode) {
@ -366,42 +365,42 @@ impl super::Cpu {
pub fn rol(&mut self, _address: usize, _mode: Mode) { pub fn rol(&mut self, _address: usize, _mode: Mode) {
let mut val = match _mode { let mut val = match _mode {
Mode::ACC => self.A, Mode::ACC => self.a,
_ => { _ => {
self.clock += 2; self.clock += 2;
self.read(_address) self.read(_address)
}, },
}; };
let carry_flag_bit = if self.P & CARRY_FLAG != 0 {1} else {0}; let carry_flag_bit = if self.p & CARRY_FLAG != 0 {1} else {0};
let new_cfb = if val & 0x80 != 0 {1} else {0}; let new_cfb = if val & 0x80 != 0 {1} else {0};
val <<= 1; val <<= 1;
val += carry_flag_bit; val += carry_flag_bit;
match _mode { match _mode {
Mode::ACC => self.A = val, Mode::ACC => self.a = val,
_ => self.write(_address, val), _ => self.write(_address, val),
}; };
if new_cfb != 0 { self.P |= CARRY_FLAG; } if new_cfb != 0 { self.p |= CARRY_FLAG; }
else { self.P &= 0xFF - CARRY_FLAG; } else { self.p &= 0xFF - CARRY_FLAG; }
self.set_zero_flag(val); self.set_zero_flag(val);
self.set_negative_flag(val); self.set_negative_flag(val);
} }
pub fn ror(&mut self, _address: usize, _mode: Mode) { pub fn ror(&mut self, _address: usize, _mode: Mode) {
let mut val = match _mode { let mut val = match _mode {
Mode::ACC => self.A, Mode::ACC => self.a,
_ => { _ => {
self.clock += 2; // extra cycles self.clock += 2; // extra cycles
self.read(_address) self.read(_address)
} }
}; };
let cfb = if self.P & CARRY_FLAG != 0 {1} else {0}; let cfb = if self.p & CARRY_FLAG != 0 {1} else {0};
let new_cfb = val & 0x1; let new_cfb = val & 0x1;
val >>= 1; val >>= 1;
val += cfb * 0x80; val += cfb * 0x80;
if new_cfb != 0 { self.P |= CARRY_FLAG; } if new_cfb != 0 { self.p |= CARRY_FLAG; }
else { self.P &= 0xFF - CARRY_FLAG; } else { self.p &= 0xFF - CARRY_FLAG; }
match _mode { match _mode {
Mode::ACC => self.A = val, Mode::ACC => self.a = val,
_ => self.write(_address, val), _ => self.write(_address, val),
}; };
self.set_zero_flag(val); self.set_zero_flag(val);
@ -416,59 +415,59 @@ impl super::Cpu {
pub fn rti(&mut self, _address: usize, _mode: Mode) { pub fn rti(&mut self, _address: usize, _mode: Mode) {
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 += 4;
} }
pub fn rts(&mut self, _address: usize, _mode: Mode) { pub fn rts(&mut self, _address: usize, _mode: Mode) {
self.PC = self.pop() as usize; self.pc = self.pop() as usize;
self.PC += ((self.pop() as usize) << 8) + 1; self.pc += ((self.pop() as usize) << 8) + 1;
self.clock += 4; self.clock += 4;
} }
pub fn sax(&mut self, _address: usize, _mode: Mode) { pub fn sax(&mut self, _address: usize, _mode: Mode) {
// unofficial combo of stx and sta // unofficial combo of stx and sta
self.write(_address, self.A & self.X); self.write(_address, self.a & self.x);
} }
pub fn sbc(&mut self, _address: usize, _mode: Mode) { pub fn sbc(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address); let byte = self.read(_address);
let carry_bit = if self.P & CARRY_FLAG == 0 {1} else {0}; let carry_bit = if self.p & CARRY_FLAG == 0 {1} else {0};
let mut new_val = self.A.wrapping_sub(byte); let mut new_val = self.a.wrapping_sub(byte);
new_val = new_val.wrapping_sub(carry_bit); new_val = new_val.wrapping_sub(carry_bit);
// if overflow occurs and we subtracted something, CLEAR the carry bit // if overflow occurs and we subtracted something, CLEAR the carry bit
if new_val >= self.A && (byte != 0 || carry_bit != 0) { if new_val >= self.a && (byte != 0 || carry_bit != 0) {
self.P &= 0xFF - CARRY_FLAG; self.p &= 0xFF - CARRY_FLAG;
} else { } else {
self.P |= CARRY_FLAG; self.p |= CARRY_FLAG;
} }
self.set_zero_flag(new_val); self.set_zero_flag(new_val);
self.set_negative_flag(new_val); self.set_negative_flag(new_val);
// if acc is positive, mem is negative, and result is negative // if acc is positive, mem is negative, and result is negative
// or if acc is negative, mem is positive, and result is positive // or if acc is negative, mem is positive, and result is positive
let acc = self.A & 0x80 == 0; let acc = self.a & 0x80 == 0;
let mem = byte & 0x80 == 0; let mem = byte & 0x80 == 0;
let res = new_val & 0x80 == 0; let res = new_val & 0x80 == 0;
// if sign is wrong, SET overflow flag // if sign is wrong, SET overflow flag
if (acc && !mem && !res) || (!acc && mem && res) { if (acc && !mem && !res) || (!acc && mem && res) {
self.P |= OVERFLOW_FLAG; self.p |= OVERFLOW_FLAG;
} else { } else {
self.P &= 0xFF - OVERFLOW_FLAG; self.p &= 0xFF - OVERFLOW_FLAG;
} }
self.A = new_val; // actually change the accumulator self.a = new_val; // actually change the accumulator
} }
pub fn sec(&mut self, _address: usize, _mode: Mode) { pub fn sec(&mut self, _address: usize, _mode: Mode) {
self.P |= CARRY_FLAG; self.p |= CARRY_FLAG;
} }
pub fn sed(&mut self, _address: usize, _mode: Mode) { pub fn sed(&mut self, _address: usize, _mode: Mode) {
self.P |= DECIMAL_FLAG; // don't think this is necessary since the NES's 6502 doesn't have decimal _mode but whatever self.p |= DECIMAL_FLAG; // don't think this is necessary since the NES's 6502 doesn't have decimal _mode but whatever
} }
pub fn sei(&mut self, _address: usize, _mode: Mode) { pub fn sei(&mut self, _address: usize, _mode: Mode) {
self.P |= INTERRUPT_DISABLE_FLAG; self.p |= INTERRUPT_DISABLE_FLAG;
} }
pub fn slo(&mut self, _address: usize, _mode: Mode) { pub fn slo(&mut self, _address: usize, _mode: Mode) {
@ -484,72 +483,72 @@ impl super::Cpu {
} }
pub fn sta(&mut self, _address: usize, _mode: Mode) { pub fn sta(&mut self, _address: usize, _mode: Mode) {
self.write(_address, self.A); self.write(_address, self.a);
} }
pub fn stx(&mut self, _address: usize, _mode: Mode) { pub fn stx(&mut self, _address: usize, _mode: Mode) {
self.write(_address, self.X); self.write(_address, self.x);
} }
pub fn sty(&mut self, _address: usize, _mode: Mode) { pub fn sty(&mut self, _address: usize, _mode: Mode) {
self.write(_address, self.Y); self.write(_address, self.y);
} }
pub fn tax(&mut self, _address: usize, _mode: Mode) { pub fn tax(&mut self, _address: usize, _mode: Mode) {
self.X = self.A; self.x = self.a;
self.set_zero_flag(self.X); self.set_zero_flag(self.x);
self.set_negative_flag(self.X); self.set_negative_flag(self.x);
} }
pub fn tay(&mut self, _address: usize, _mode: Mode) { pub fn tay(&mut self, _address: usize, _mode: Mode) {
self.Y = self.A; self.y = self.a;
self.set_zero_flag(self.Y); self.set_zero_flag(self.y);
self.set_negative_flag(self.Y); self.set_negative_flag(self.y);
} }
pub fn tsx(&mut self, _address: usize, _mode: Mode) { pub fn tsx(&mut self, _address: usize, _mode: Mode) {
self.X = self.S; self.x = self.s;
self.set_zero_flag(self.X); self.set_zero_flag(self.x);
self.set_negative_flag(self.X); self.set_negative_flag(self.x);
} }
pub fn txa(&mut self, _address: usize, _mode: Mode) { pub fn txa(&mut self, _address: usize, _mode: Mode) {
self.A = self.X; self.a = self.x;
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 txs(&mut self, _address: usize, _mode: Mode) { pub fn txs(&mut self, _address: usize, _mode: Mode) {
self.S = self.X; self.s = self.x;
} }
pub fn tya(&mut self, _address: usize, _mode: Mode) { pub fn tya(&mut self, _address: usize, _mode: Mode) {
self.A = self.Y; self.a = self.y;
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 bad(&mut self, _address: usize, _mode: Mode) { pub fn bad(&mut self, _address: usize, _mode: Mode) {
panic!("illegal opcode: 0x{:02X}", self.read(self.PC)); // this won't be the illegal opcode because the PC somehow hasn't been updated yet panic!("illegal opcode: 0x{:02X}", self.read(self.pc)); // this won't be the illegal opcode because the PC somehow hasn't been updated yet
} }
// Interrupts // Interrupts
pub fn nmi(&mut self) { pub fn nmi(&mut self) {
self.push((self.PC >> 8) as u8); // push high byte self.push((self.pc >> 8) as u8); // push high byte
self.push((self.PC & 0xFF) as u8); // push low byte self.push((self.pc & 0xFF) as u8); // push low byte
self.push(self.P | 0b00110000); // push status register with break bits set self.push(self.p | 0b00110000); // push status register with break bits set
self.P |= INTERRUPT_DISABLE_FLAG; // set interrupt disable flag self.p |= INTERRUPT_DISABLE_FLAG; // set interrupt disable flag
self.PC = ((self.read(NMI_VECTOR + 1) as usize) << 8) // set program counter to NMI vector, taking high byte self.pc = ((self.read(NMI_VECTOR + 1) as usize) << 8) // set program counter to NMI vector, taking high byte
+ (self.read(NMI_VECTOR) as usize); // and low byte + (self.read(NMI_VECTOR) as usize); // and low byte
self.clock += 7; self.clock += 7;
} }
pub fn irq(&mut self) { pub fn irq(&mut self) {
self.push((self.PC >> 8) as u8); // push high byte self.push((self.pc >> 8) as u8); // push high byte
self.push((self.PC & 0xFF) as u8); // push low byte self.push((self.pc & 0xFF) as u8); // push low byte
self.push(self.P & 0b11001111); // push status register with break bits cleared self.push(self.p & 0b11001111); // push status register with break bits cleared
self.P |= INTERRUPT_DISABLE_FLAG; // set interrupt disable flag self.p |= INTERRUPT_DISABLE_FLAG; // set interrupt disable flag
self.PC = ((self.read(IRQ_VECTOR + 1) as usize) << 8) // set program counter to IRQ/BRK vector, taking high byte self.pc = ((self.read(IRQ_VECTOR + 1) as usize) << 8) // set program counter to IRQ/BRK vector, taking high byte
+ (self.read(IRQ_VECTOR) as usize); // and low byte + (self.read(IRQ_VECTOR) as usize); // and low byte
self.clock += 7; self.clock += 7;
} }

View File

@ -5,12 +5,12 @@ use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct CpuData { pub struct CpuData {
mem: Vec<u8>, mem: Vec<u8>,
A: u8, a: u8,
X: u8, x: u8,
Y: u8, y: u8,
PC: usize, pc: usize,
S: u8, s: u8,
P: u8, p: u8,
clock: u64, clock: u64,
delay: usize, delay: usize,
strobe: u8, strobe: u8,
@ -23,12 +23,12 @@ impl super::Cpu {
pub fn save_state(&self) -> CpuData { pub fn save_state(&self) -> CpuData {
CpuData{ CpuData{
mem: self.mem.clone(), mem: self.mem.clone(),
A: self.A, a: self.a,
X: self.X, x: self.x,
Y: self.Y, y: self.y,
PC: self.PC, pc: self.pc,
S: self.S, s: self.s,
P: self.P, p: self.p,
clock: self.clock, clock: self.clock,
delay: self.delay, delay: self.delay,
strobe: self.strobe, strobe: self.strobe,
@ -40,12 +40,12 @@ impl super::Cpu {
pub fn load_state(&mut self, data: CpuData) { pub fn load_state(&mut self, data: CpuData) {
self.mem = data.mem; self.mem = data.mem;
self.A = data.A; self.a = data.a;
self.X = data.X; self.x = data.x;
self.Y = data.Y; self.y = data.y;
self.PC = data.PC; self.pc = data.pc;
self.S = data.S; self.s = data.s;
self.P = data.P; self.p = data.p;
self.clock = data.clock; self.clock = data.clock;
self.delay = data.delay; self.delay = data.delay;
self.strobe = data.strobe; self.strobe = data.strobe;

View File

@ -3,7 +3,7 @@ use super::{CARRY_FLAG, NEGATIVE_FLAG, STACK_OFFSET, ZERO_FLAG, Mode};
impl super::Cpu { impl super::Cpu {
pub fn advance_pc(&mut self, mode: Mode) { pub fn advance_pc(&mut self, mode: Mode) {
self.PC += match mode { self.pc += match mode {
Mode::ABS => 3, Mode::ABS => 3,
Mode::ABX => 3, Mode::ABX => 3,
Mode::ABY => 3, Mode::ABY => 3,
@ -24,13 +24,13 @@ impl super::Cpu {
match offset >= 0 { match offset >= 0 {
true => { true => {
let decoded_offset = offset as usize; let decoded_offset = offset as usize;
self.PC += decoded_offset; self.pc += decoded_offset;
}, },
false => { false => {
// instr_test-v5/rom_singles/11-stack.nes: // instr_test-v5/rom_singles/11-stack.nes:
// letting decoded_offset be (-offset) as usize was allowing for overflow if offset was -128/0b10000000 // letting decoded_offset be (-offset) as usize was allowing for overflow if offset was -128/0b10000000
let decoded_offset = (-offset) as u8; let decoded_offset = (-offset) as u8;
self.PC -= decoded_offset as usize; self.pc -= decoded_offset as usize;
}, },
} }
} }
@ -50,17 +50,17 @@ impl super::Cpu {
pub fn branch(&mut self, unsigned_offset: u8) { pub fn branch(&mut self, unsigned_offset: u8) {
let offset = unsigned_offset as i8; let offset = unsigned_offset as i8;
self.clock += 1; self.clock += 1;
let old_addr = self.PC; let old_addr = self.pc;
self.add_offset_to_pc(offset); self.add_offset_to_pc(offset);
let new_addr = self.PC; let new_addr = self.pc;
self.branch_page_cross(old_addr, new_addr); self.branch_page_cross(old_addr, new_addr);
} }
pub fn compare(&mut self, reg: u8, byte: u8) { pub fn compare(&mut self, reg: u8, byte: u8) {
if reg >= byte { if reg >= byte {
self.P |= CARRY_FLAG; self.p |= CARRY_FLAG;
} else { } else {
self.P &= 0xFF - CARRY_FLAG; self.p &= 0xFF - CARRY_FLAG;
} }
self.set_zero_flag(if reg == byte {0} else {1}); self.set_zero_flag(if reg == byte {0} else {1});
let diff = reg.wrapping_sub(byte); let diff = reg.wrapping_sub(byte);
@ -68,29 +68,29 @@ impl super::Cpu {
} }
pub fn pop(&mut self) -> u8 { pub fn pop(&mut self) -> u8 {
self.S = self.S.wrapping_add(1); self.s = self.s.wrapping_add(1);
let byte = self.read(STACK_OFFSET + self.S as usize); let byte = self.read(STACK_OFFSET + self.s as usize);
byte byte
} }
pub fn push(&mut self, byte: u8) { pub fn push(&mut self, byte: u8) {
self.write(STACK_OFFSET + self.S as usize, byte); self.write(STACK_OFFSET + self.s as usize, byte);
self.S = self.S.wrapping_sub(1); self.s = self.s.wrapping_sub(1);
} }
pub fn set_negative_flag(&mut self, num: u8) { pub fn set_negative_flag(&mut self, num: u8) {
if num & 0x80 == 0x80 { if num & 0x80 == 0x80 {
self.P |= NEGATIVE_FLAG; self.p |= NEGATIVE_FLAG;
} else { } else {
self.P &= 0xFF - NEGATIVE_FLAG; self.p &= 0xFF - NEGATIVE_FLAG;
} }
} }
pub fn set_zero_flag(&mut self, num: u8) { pub fn set_zero_flag(&mut self, num: u8) {
if num == 0 { if num == 0 {
self.P |= ZERO_FLAG; self.p |= ZERO_FLAG;
} else { } else {
self.P &= 0xFF - ZERO_FLAG; self.p &= 0xFF - ZERO_FLAG;
} }
} }

View File

@ -13,7 +13,7 @@ use apu::Apu;
use cartridge::{check_signature, get_mapper}; use cartridge::{check_signature, get_mapper};
use input::poll_buttons; use input::poll_buttons;
use screen::{init_window, draw_pixel, draw_to_window}; use screen::{init_window, draw_pixel, draw_to_window};
use state::{save_state, load_state, find_next_filename, find_last_filename}; use state::{save_state, load_state, find_next_filename, find_last_save_state};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@ -107,7 +107,7 @@ fn run_game(
let sdl_buffer = Arc::clone(&apu_buffer); // used in audio device's callback to select the samples it needs let sdl_buffer = Arc::clone(&apu_buffer); // used in audio device's callback to select the samples it needs
let audio_device = audio::initialize(sdl_context, sdl_buffer).expect("Could not create audio device"); let audio_device = audio::initialize(sdl_context, sdl_buffer).expect("Could not create audio device");
let mut half_cycle = false; let mut half_cycle = false;
audio_device.resume(); let mut audio_started = false;
// Initialize hardware components // Initialize hardware components
let filepath = Path::new(filename).to_path_buf(); let filepath = Path::new(filename).to_path_buf();
@ -150,6 +150,10 @@ fn run_game(
draw_to_window(texture, canvas, &screen_buffer)?; // draw the buffer to the window with SDL draw_to_window(texture, canvas, &screen_buffer)?; // draw the buffer to the window with SDL
let mut b = apu_buffer.lock().unwrap(); // unlock mutex to the real buffer let mut b = apu_buffer.lock().unwrap(); // unlock mutex to the real buffer
b.append(&mut temp_buffer); // send this frame's audio data, emptying the temp buffer b.append(&mut temp_buffer); // send this frame's audio data, emptying the temp buffer
if !audio_started {
audio_started = true;
audio_device.resume();
}
let now = Instant::now(); let now = Instant::now();
// if we're running faster than 60Hz, kill time // if we're running faster than 60Hz, kill time
if now < timer + Duration::from_millis(1000/60) { if now < timer + Duration::from_millis(1000/60) {
@ -198,7 +202,7 @@ fn process_events(event_pump: &mut EventPump, filepath: &PathBuf, cpu: &mut Cpu)
res.unwrap(); res.unwrap();
}, },
Event::KeyDown{ keycode: Some(Keycode::F9), .. } => { Event::KeyDown{ keycode: Some(Keycode::F9), .. } => {
match find_last_filename(filepath, Some("dat")) { match find_last_save_state(filepath, Some("dat")) {
Some(p) => { Some(p) => {
let res: Result<(), String> = load_state(cpu, &p) let res: Result<(), String> = load_state(cpu, &p)
.or_else(|e| { println!("{}", e); Ok(()) } ); .or_else(|e| { println!("{}", e); Ok(()) } );

View File

@ -54,28 +54,28 @@ impl super::Ppu {
let base = address % 0x1000; let base = address % 0x1000;
let offset = base % 0x0400; let offset = base % 0x0400;
match self.mapper.borrow().get_mirroring() { match self.mapper.borrow().get_mirroring() {
Mirror::LowBank => self.nametable_A[offset], Mirror::LowBank => self.nametable_a[offset],
Mirror::HighBank => self.nametable_B[offset], Mirror::HighBank => self.nametable_b[offset],
Mirror::Horizontal => { Mirror::Horizontal => {
match base { match base {
0x0000..=0x07FF => self.nametable_A[offset], 0x0000..=0x07FF => self.nametable_a[offset],
0x0800..=0x0FFF => self.nametable_B[offset], 0x0800..=0x0FFF => self.nametable_b[offset],
_ => panic!("panicked reading nametable base: {}", base), _ => panic!("panicked reading nametable base: {}", base),
} }
}, },
Mirror::Vertical => { Mirror::Vertical => {
match base { match base {
0x0000..=0x03FF | 0x0800..=0x0BFF => self.nametable_A[offset], 0x0000..=0x03FF | 0x0800..=0x0BFF => self.nametable_a[offset],
0x0400..=0x07FF | 0x0C00..=0x0FFF => self.nametable_B[offset], 0x0400..=0x07FF | 0x0C00..=0x0FFF => self.nametable_b[offset],
_ => panic!("panicked reading nametable base: {}", base), _ => panic!("panicked reading nametable base: {}", base),
} }
}, },
Mirror::FourScreen => { Mirror::FourScreen => {
match base { match base {
0x0000..=0x03FF => self.nametable_A[offset], 0x0000..=0x03FF => self.nametable_a[offset],
0x0400..=0x07FF => self.nametable_B[offset], 0x0400..=0x07FF => self.nametable_b[offset],
0x0800..=0x0BFF => self.nametable_C[offset], 0x0800..=0x0BFF => self.nametable_c[offset],
0x0C00..=0x0FFF => self.nametable_D[offset], 0x0C00..=0x0FFF => self.nametable_d[offset],
_ => panic!("panicked reading nametable base: {}", base), _ => panic!("panicked reading nametable base: {}", base),
} }
}, },
@ -86,28 +86,28 @@ impl super::Ppu {
let base = address % 0x1000; let base = address % 0x1000;
let offset = base % 0x0400; let offset = base % 0x0400;
match self.mapper.borrow().get_mirroring() { match self.mapper.borrow().get_mirroring() {
Mirror::LowBank => self.nametable_A[offset] = value, Mirror::LowBank => self.nametable_a[offset] = value,
Mirror::HighBank => self.nametable_B[offset] = value, Mirror::HighBank => self.nametable_b[offset] = value,
Mirror::Horizontal => { Mirror::Horizontal => {
match base { match base {
0x0000..=0x07FF => self.nametable_A[offset] = value, 0x0000..=0x07FF => self.nametable_a[offset] = value,
0x0800..=0x0FFF => self.nametable_B[offset] = value, 0x0800..=0x0FFF => self.nametable_b[offset] = value,
_ => panic!("panicked writing nametable base: {}", base), _ => panic!("panicked writing nametable base: {}", base),
} }
}, },
Mirror::Vertical => { Mirror::Vertical => {
match base { match base {
0x0000..=0x03FF | 0x0800..=0x0BFF => self.nametable_A[offset] = value, 0x0000..=0x03FF | 0x0800..=0x0BFF => self.nametable_a[offset] = value,
0x0400..=0x07FF | 0x0C00..=0x0FFF => self.nametable_B[offset] = value, 0x0400..=0x07FF | 0x0C00..=0x0FFF => self.nametable_b[offset] = value,
_ => panic!("panicked writing nametable base: {}", base), _ => panic!("panicked writing nametable base: {}", base),
} }
}, },
Mirror::FourScreen => { Mirror::FourScreen => {
match base { match base {
0x0000..=0x03FF => self.nametable_A[offset] = value, 0x0000..=0x03FF => self.nametable_a[offset] = value,
0x0400..=0x07FF => self.nametable_B[offset] = value, 0x0400..=0x07FF => self.nametable_b[offset] = value,
0x0800..=0x0BFF => self.nametable_C[offset] = value, 0x0800..=0x0BFF => self.nametable_c[offset] = value,
0x0C00..=0x0FFF => self.nametable_D[offset] = value, 0x0C00..=0x0FFF => self.nametable_d[offset] = value,
_ => panic!("panicked writing nametable base: {}", base), _ => panic!("panicked writing nametable base: {}", base),
} }
}, },

View File

@ -27,10 +27,10 @@ pub struct Ppu {
// Pictures on http://wiki.nesdev.com/w/index.php/Mirroring refer to them as A and B. // Pictures on http://wiki.nesdev.com/w/index.php/Mirroring refer to them as A and B.
// http://wiki.nesdev.com/w/index.php/MMC1 calls them higher and lower. // http://wiki.nesdev.com/w/index.php/MMC1 calls them higher and lower.
// They can be mirrored at certain memory ranges. // They can be mirrored at certain memory ranges.
nametable_A: Vec<u8>, nametable_a: Vec<u8>,
nametable_B: Vec<u8>, nametable_b: Vec<u8>,
nametable_C: Vec<u8>, nametable_c: Vec<u8>,
nametable_D: Vec<u8>, nametable_d: Vec<u8>,
// The palette shared by both background and sprites. // The palette shared by both background and sprites.
// Consists of 32 bytes, each of which represents an index into the global PALETTE_TABLE. // Consists of 32 bytes, each of which represents an index into the global PALETTE_TABLE.
@ -104,10 +104,10 @@ impl Ppu {
x: 0, x: 0,
w: 0, w: 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],
nametable_C: vec![0u8; 0x0400], nametable_c: vec![0u8; 0x0400],
nametable_D: vec![0u8; 0x0400], nametable_d: vec![0u8; 0x0400],
palette_ram: vec![0u8; 0x0020], palette_ram: vec![0u8; 0x0020],
background_pattern_sr_low: 0, background_pattern_sr_low: 0,
background_pattern_sr_high: 0, background_pattern_sr_high: 0,

View File

@ -9,10 +9,10 @@ pub struct PpuData {
t: u16, t: u16,
x: u8, x: u8,
w: u8, w: u8,
nametable_A: Vec<u8>, nametable_a: Vec<u8>,
nametable_B: Vec<u8>, nametable_b: Vec<u8>,
nametable_C: Vec<u8>, nametable_c: Vec<u8>,
nametable_D: Vec<u8>, nametable_d: Vec<u8>,
palette_ram: Vec<u8>, palette_ram: Vec<u8>,
background_pattern_sr_low: u16, background_pattern_sr_low: u16,
background_pattern_sr_high: u16, background_pattern_sr_high: u16,
@ -65,10 +65,10 @@ impl super::Ppu {
t: self.t, t: self.t,
x: self.x, x: self.x,
w: self.w, w: self.w,
nametable_A: self.nametable_A.clone(), nametable_a: self.nametable_a.clone(),
nametable_B: self.nametable_B.clone(), nametable_b: self.nametable_b.clone(),
nametable_C: self.nametable_C.clone(), nametable_c: self.nametable_c.clone(),
nametable_D: self.nametable_D.clone(), nametable_d: self.nametable_d.clone(),
palette_ram: self.palette_ram.clone(), palette_ram: self.palette_ram.clone(),
background_pattern_sr_low: self.background_pattern_sr_low, background_pattern_sr_low: self.background_pattern_sr_low,
background_pattern_sr_high: self.background_pattern_sr_high, background_pattern_sr_high: self.background_pattern_sr_high,
@ -120,10 +120,10 @@ impl super::Ppu {
self.t = data.t; self.t = data.t;
self.x = data.x; self.x = data.x;
self.w = data.w; self.w = data.w;
self.nametable_A = data.nametable_A; self.nametable_a = data.nametable_a;
self.nametable_B = data.nametable_B; self.nametable_b = data.nametable_b;
self.nametable_C = data.nametable_C; self.nametable_c = data.nametable_c;
self.nametable_D = data.nametable_D; self.nametable_d = data.nametable_d;
self.palette_ram = data.palette_ram; self.palette_ram = data.palette_ram;
self.background_pattern_sr_low = data.background_pattern_sr_low; self.background_pattern_sr_low = data.background_pattern_sr_low;
self.background_pattern_sr_high = data.background_pattern_sr_high; self.background_pattern_sr_high = data.background_pattern_sr_high;

View File

@ -3,7 +3,7 @@ use super::ppu;
use super::apu; use super::apu;
use super::cartridge; use super::cartridge;
use std::fs::File; use std::fs::{DirEntry, File};
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -70,24 +70,29 @@ pub fn find_next_filename(filepath: &PathBuf, new_ext: Option<&str>) -> Option<P
} }
} }
pub fn find_last_filename(filepath: &PathBuf, new_ext: Option<&str>) -> Option<PathBuf> { pub fn find_last_save_state(filepath: &PathBuf, new_ext: Option<&str>) -> Option<PathBuf> {
let path = filepath.parent()?; let path = filepath.parent()?;
let stem = filepath.file_stem()?.to_str()?; let stem = filepath.file_stem()?.to_str()?;
let ext = new_ext.or(Some(filepath.extension()?.to_str()?)).unwrap(); let ext = new_ext.or(Some(filepath.extension()?.to_str()?)).unwrap();
let files = std::fs::read_dir(path).expect("couldn't read directory"); let files = std::fs::read_dir(path).expect("couldn't read directory");
let mut save_states = files let mut save_states = files
.map(|f| f.unwrap().path() ) .filter(|dir_entry| {
.filter(|p| { let os_name = dir_entry.as_ref().unwrap().file_name();
let pfs = p.file_name().unwrap().to_str().unwrap(); let name = os_name.to_str().unwrap();
pfs.len() >= stem.len() name.len() >= stem.len()
&& pfs.len() >= ext.len() && name.len() >= ext.len()
&& &pfs[..stem.len()] == stem && &name[..stem.len()] == stem
&& &pfs[pfs.len()-ext.len()..] == ext && &name[name.len()-ext.len()..] == ext
}) })
.collect::<Vec<PathBuf>>(); .collect::<Vec<std::io::Result<DirEntry>>>();
save_states.sort(); save_states.sort_by(|a, b| {
let a_mod_time = a.as_ref().unwrap().metadata().unwrap().modified().unwrap();
let b_mod_time = b.as_ref().unwrap().metadata().unwrap().modified().unwrap();
b_mod_time.cmp(&a_mod_time) // puts in reverse order by last modified time
});
println!("{:?}", save_states);
match save_states.len() { match save_states.len() {
0 => None, 0 => None,
_ => Some(save_states[save_states.len()-1].clone()), _ => Some(save_states[0].as_ref().unwrap().path()),
} }
} }