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
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`.

View File

@ -1,4 +1,3 @@
use std::time::{Duration, Instant};
use std::sync::{Arc, Mutex};
use sdl2::Sdl;
use sdl2::audio::{AudioCallback, AudioSpecDesired};
@ -80,7 +79,7 @@ impl AudioCallback for ApuSampler {
*b = b.split_off(target);
}
} 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 {
self.clock += 4;
<usize>::from(
((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 + 2) as usize) << 8) + // high byte, little endian
(self.read(self.pc + 1)) as usize // low byte
)
}
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 new_address = old_address + self.X as usize;
let new_address = old_address + self.x as usize;
match current_opcode {
0x1C | 0x1D | 0x3C | 0x3D | 0x5C | 0x5D | 0x7C | 0x7D | 0xBC | 0xBD | 0xDC | 0xDD | 0xFC | 0xFD
=> self.address_page_cross(old_address, new_address),
@ -23,9 +23,9 @@ impl super::Cpu {
}
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 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 new_address = new_address as usize;
if current_opcode == 0x99 {
@ -43,7 +43,7 @@ impl super::Cpu {
pub fn immediate(&mut self) -> usize {
self.clock += 2;
self.PC + 1
self.pc + 1
}
pub fn implied(&mut self) -> usize {
@ -53,8 +53,8 @@ impl super::Cpu {
pub fn indexed_indirect(&mut self) -> usize {
self.clock += 6;
let operand = self.read(self.PC + 1);
let zp_low_addr = operand.wrapping_add(self.X);
let operand = self.read(self.pc + 1);
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_low_byte = self.read(zp_low_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 {
let operand_address = ((self.read(self.PC + 2) as usize) << 8)
+ (self.read(self.PC + 1) as usize);
let operand_address = ((self.read(self.pc + 2) as usize) << 8)
+ (self.read(self.pc + 1) as usize);
let low_byte = self.read(operand_address) as usize;
// 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
@ -80,14 +80,14 @@ impl super::Cpu {
}
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_high_addr = operand.wrapping_add(1);
let zp_low_byte = self.read(zp_low_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 new_address = old_address.wrapping_add(self.Y as u16);
if self.PC == 0xF1 {
let new_address = old_address.wrapping_add(self.y as u16);
if self.pc == 0xF1 {
self.clock += 1;
} else {
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 {
self.clock += 2;
self.PC + 1
self.pc + 1
}
pub fn zero_page(&mut self) -> usize {
let operand = self.read(self.PC + 1);
let operand = self.read(self.pc + 1);
self.clock += 3;
operand as 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;
operand.wrapping_add(self.X) as usize
operand.wrapping_add(self.x) as 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;
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 {
mem: Vec<u8>, // CPU's RAM, $0000-$1FFF
A: u8, // accumulator
X: u8, // general purpose
Y: u8, // general purpose
PC: usize, // 16-bit program counter
S: u8, // stack pointer
P: u8, // status
a: u8, // accumulator
x: u8, // general purpose
y: u8, // general purpose
pc: usize, // 16-bit program counter
s: u8, // stack pointer
p: u8, // status
clock: u64, // number of ticks in current cycle
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 {
let mut cpu = Cpu{
mem: vec![0; 0x2000],
A: 0, X: 0, Y: 0,
PC: 0,
S: 0xFD,
P: 0x24,
a: 0, x: 0, y: 0,
pc: 0,
s: 0xFD,
p: 0x24,
clock: 0,
delay: 0,
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*/
],
};
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
}
@ -151,12 +151,12 @@ impl Cpu {
}
self.ppu.trigger_nmi = false;
// 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.apu.trigger_irq = false;
// 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();
}
// 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
let clock = self.clock;
let opcode = <usize>::from(self.read(self.PC));
let opcode = <usize>::from(self.read(self.pc));
// get addressing mode
let mode = self.mode_table[opcode].clone();
@ -264,7 +264,7 @@ impl Cpu {
}
fn _debug(&mut self, num_bytes: usize, opcode: usize) {
let pc = self.PC;
let pc = self.pc;
let operands = match num_bytes {
1 => " ".to_string(),
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}",
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) {
let byte = self.read(_address);
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 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
new_val = new_val.wrapping_add(carry_bit); // add carry flag to accumulator
// set carry flag if we wrapped around and added something
if new_val <= self.A && (byte != 0 || carry_bit != 0) {
self.P |= CARRY_FLAG;
if new_val <= self.a && (byte != 0 || carry_bit != 0) {
self.p |= CARRY_FLAG;
} else {
self.P &= 0xFF - CARRY_FLAG;
self.p &= 0xFF - CARRY_FLAG;
}
self.set_zero_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
// ((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
if (byte ^ new_val) & (self.A ^ new_val) & 0x80 != 0 {
self.P |= OVERFLOW_FLAG;
if (byte ^ new_val) & (self.a ^ new_val) & 0x80 != 0 {
self.p |= OVERFLOW_FLAG;
} 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) {
self.A &= self.read(_address);
self.set_zero_flag(self.A);
self.set_negative_flag(self.A);
self.a &= self.read(_address);
self.set_zero_flag(self.a);
self.set_negative_flag(self.a);
}
pub fn asl(&mut self, _address: usize, _mode: Mode) {
let mut val = match _mode {
Mode::ACC => self.A,
Mode::ACC => self.a,
_ => {
self.clock += 2;
self.read(_address)
@ -45,13 +45,13 @@ impl super::Cpu {
};
// put top bit in carry flag
if val & (1<<7) != 0 {
self.P |= CARRY_FLAG;
self.p |= CARRY_FLAG;
} else {
self.P &= 0xFF - CARRY_FLAG;
self.p &= 0xFF - CARRY_FLAG;
}
val <<= 1;
match _mode {
Mode::ACC => self.A = val,
Mode::ACC => self.a = val,
_ => self.write(_address, val),
};
self.set_zero_flag(val);
@ -60,67 +60,67 @@ impl super::Cpu {
pub fn bcc(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address);
if self.P & CARRY_FLAG == 0 {
if self.p & CARRY_FLAG == 0 {
self.branch(byte);
}
}
pub fn bcs(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address);
if self.P & CARRY_FLAG != 0 {
if self.p & CARRY_FLAG != 0 {
self.branch(byte);
}
}
pub fn beq(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address);
if self.P & ZERO_FLAG != 0 {
if self.p & ZERO_FLAG != 0 {
self.branch(byte);
}
}
pub fn bit(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address);
let tested = byte & self.A;
let tested = byte & self.a;
self.set_zero_flag(tested);
let bit6 = byte & (1 << 6);
if bit6 != 0 {
self.P |= OVERFLOW_FLAG;
self.p |= OVERFLOW_FLAG;
} else {
self.P &= 0xFF - OVERFLOW_FLAG;
self.p &= 0xFF - OVERFLOW_FLAG;
}
let bit7 = byte & (1 << 7);
if bit7 != 0 {
self.P |= NEGATIVE_FLAG;
self.p |= NEGATIVE_FLAG;
} else {
self.P &= 0xFF - NEGATIVE_FLAG;
self.p &= 0xFF - NEGATIVE_FLAG;
}
}
pub fn bmi(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address);
if self.P & NEGATIVE_FLAG != 0 {
if self.p & NEGATIVE_FLAG != 0 {
self.branch(byte);
}
}
pub fn bne(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address);
if self.P & ZERO_FLAG == 0 {
if self.p & ZERO_FLAG == 0 {
self.branch(byte);
}
}
pub fn bpl(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address);
if self.P & NEGATIVE_FLAG == 0 {
if self.p & NEGATIVE_FLAG == 0 {
self.branch(byte);
}
}
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:
// 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,
// 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,
// and not just 1.
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.P | 0b00110000); // push status register with break bits set
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.push(((self.pc + 1) >> 8) as u8); // push high 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.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.read(IRQ_VECTOR) as usize); // and low byte
self.clock += 5; // total of 7 cycles, 2 come from implied()
}
pub fn bvc(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address);
if self.P & OVERFLOW_FLAG == 0 {
if self.p & OVERFLOW_FLAG == 0 {
self.branch(byte);
}
}
pub fn bvs(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address);
if self.P & OVERFLOW_FLAG != 0 {
if self.p & OVERFLOW_FLAG != 0 {
self.branch(byte);
}
}
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) {
self.P &= 0xFF - DECIMAL_FLAG;
self.p &= 0xFF - DECIMAL_FLAG;
}
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) {
self.P &= 0xFF - OVERFLOW_FLAG;
self.p &= 0xFF - OVERFLOW_FLAG;
}
pub fn cmp(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address);
self.compare(self.A, byte);
self.compare(self.a, byte);
}
pub fn cpx(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address);
self.compare(self.X, byte);
self.compare(self.x, byte);
}
pub fn cpy(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address);
self.compare(self.Y, byte);
self.compare(self.y, byte);
}
pub fn dcp(&mut self, _address: usize, _mode: Mode) {
// unofficial
let val = self.read(_address).wrapping_sub(1);
self.write(_address, val);
self.compare(self.A, val);
self.compare(self.a, val);
}
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) {
self.X = self.X.wrapping_sub(1);
self.set_zero_flag(self.X);
self.set_negative_flag(self.X);
self.x = self.x.wrapping_sub(1);
self.set_zero_flag(self.x);
self.set_negative_flag(self.x);
}
pub fn dey(&mut self, _address: usize, _mode: Mode) {
self.Y = self.Y.wrapping_sub(1);
self.set_zero_flag(self.Y);
self.set_negative_flag(self.Y);
self.y = self.y.wrapping_sub(1);
self.set_zero_flag(self.y);
self.set_negative_flag(self.y);
}
pub fn eor(&mut self, _address: usize, _mode: Mode) {
self.A ^= self.read(_address);
self.set_negative_flag(self.A);
self.set_zero_flag(self.A);
self.a ^= self.read(_address);
self.set_negative_flag(self.a);
self.set_zero_flag(self.a);
}
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) {
self.X = self.X.wrapping_add(1);
self.set_zero_flag(self.X);
self.set_negative_flag(self.X);
self.x = self.x.wrapping_add(1);
self.set_zero_flag(self.x);
self.set_negative_flag(self.x);
}
pub fn iny(&mut self, _address: usize, _mode: Mode) {
self.Y = self.Y.wrapping_add(1);
self.set_zero_flag(self.Y);
self.set_negative_flag(self.Y);
self.y = self.y.wrapping_add(1);
self.set_zero_flag(self.y);
self.set_negative_flag(self.y);
}
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) {
// 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 & 0xFF) as u8);
self.PC = _address;
self.pc = _address;
}
pub fn lax(&mut self, _address: usize, _mode: Mode) {
// unofficial opcode that sets both X and accumulator
// TODO: check cycle count? https://wiki.nesdev.com/w/index.php/Programming_with_unofficial_opcodes
let byte = self.read(_address);
self.A = byte;
self.X = byte;
self.a = byte;
self.x = byte;
self.set_zero_flag(byte);
self.set_negative_flag(byte);
}
pub fn lda(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address);
self.A = byte;
self.a = byte;
self.set_zero_flag(byte);
self.set_negative_flag(byte);
}
pub fn ldx(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address);
self.X = byte;
self.x = byte;
self.set_zero_flag(byte);
self.set_negative_flag(byte);
}
pub fn ldy(&mut self, _address: usize, _mode: Mode) {
let byte = self.read(_address);
self.Y = byte;
self.y = byte;
self.set_zero_flag(byte);
self.set_negative_flag(byte);
}
pub fn lsr(&mut self, _address: usize, _mode: Mode) {
let mut val = match _mode {
Mode::ACC => self.A,
Mode::ACC => self.a,
_ => {
self.clock += 2;
self.read(_address)
},
};
if val & 0x1 == 0x1 {
self.P |= CARRY_FLAG;
self.p |= CARRY_FLAG;
} else {
self.P &= 0xFF - CARRY_FLAG;
self.p &= 0xFF - CARRY_FLAG;
}
val >>= 1;
match _mode {
Mode::ACC => self.A = val,
Mode::ACC => self.a = val,
_ => self.write(_address, val),
};
self.set_zero_flag(val);
@ -313,49 +312,49 @@ impl super::Cpu {
}
pub fn ora(&mut self, _address: usize, _mode: Mode) {
self.A |= self.read(_address);
self.set_zero_flag(self.A);
self.set_negative_flag(self.A);
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) {
self.clock += 1;
self.push(self.A);
self.push(self.a);
}
pub fn php(&mut self, _address: usize, _mode: Mode) {
self.clock += 1;
self.push(self.P | 0b00110000);
self.push(self.p | 0b00110000);
}
pub fn pla(&mut self, _address: usize, _mode: Mode) {
self.clock += 2;
self.A = self.pop();
self.set_zero_flag(self.A);
self.set_negative_flag(self.A);
self.a = self.pop();
self.set_zero_flag(self.a);
self.set_negative_flag(self.a);
}
pub fn plp(&mut self, _address: usize, _mode: Mode) {
self.clock += 2;
self.P = self.pop();
self.p = self.pop();
// TODO: figure out exactly what's supposed to happen here
// let status = self.pop();
// // 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
// // bit of self.P to 0.
// // set that bit of self.p to 1. if it's 0, set that
// // bit of self.p to 0.
// for i in 0..=7 {
// if i == 4 || i == 5 {
// continue; // ignore B flags
// }
// let bit = if status & (1 << i) == 0 {0} else {1};
// if bit != 0 {
// self.P |= 1 << i;
// self.p |= 1 << i;
// } else {
// self.P &= 0xFF - (1 << i);
// self.p &= 0xFF - (1 << i);
// }
// }
// self.P |= 1 << 5; // turn on bit 5
// self.P &= 0xFF - (1 << 4); // and turn off bit 4 because god knows why
// self.p |= 1 << 5; // turn on bit 5
// self.p &= 0xFF - (1 << 4); // and turn off bit 4 because god knows why
}
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) {
let mut val = match _mode {
Mode::ACC => self.A,
Mode::ACC => self.a,
_ => {
self.clock += 2;
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};
val <<= 1;
val += carry_flag_bit;
match _mode {
Mode::ACC => self.A = val,
Mode::ACC => self.a = val,
_ => self.write(_address, val),
};
if new_cfb != 0 { self.P |= CARRY_FLAG; }
else { self.P &= 0xFF - CARRY_FLAG; }
if new_cfb != 0 { self.p |= CARRY_FLAG; }
else { self.p &= 0xFF - CARRY_FLAG; }
self.set_zero_flag(val);
self.set_negative_flag(val);
}
pub fn ror(&mut self, _address: usize, _mode: Mode) {
let mut val = match _mode {
Mode::ACC => self.A,
Mode::ACC => self.a,
_ => {
self.clock += 2; // extra cycles
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;
val >>= 1;
val += cfb * 0x80;
if new_cfb != 0 { self.P |= CARRY_FLAG; }
else { self.P &= 0xFF - CARRY_FLAG; }
if new_cfb != 0 { self.p |= CARRY_FLAG; }
else { self.p &= 0xFF - CARRY_FLAG; }
match _mode {
Mode::ACC => self.A = val,
Mode::ACC => self.a = val,
_ => self.write(_address, val),
};
self.set_zero_flag(val);
@ -416,59 +415,59 @@ impl super::Cpu {
pub fn rti(&mut self, _address: usize, _mode: Mode) {
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) << 8; // high byte
self.pc = self.pop() as usize; // low byte
self.pc += (self.pop() as usize) << 8; // high byte
self.clock += 4;
}
pub fn rts(&mut self, _address: usize, _mode: Mode) {
self.PC = self.pop() as usize;
self.PC += ((self.pop() as usize) << 8) + 1;
self.pc = self.pop() as usize;
self.pc += ((self.pop() as usize) << 8) + 1;
self.clock += 4;
}
pub fn sax(&mut self, _address: usize, _mode: Mode) {
// 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) {
let byte = self.read(_address);
let carry_bit = if self.P & CARRY_FLAG == 0 {1} else {0};
let mut new_val = self.A.wrapping_sub(byte);
let carry_bit = if self.p & CARRY_FLAG == 0 {1} else {0};
let mut new_val = self.a.wrapping_sub(byte);
new_val = new_val.wrapping_sub(carry_bit);
// if overflow occurs and we subtracted something, CLEAR the carry bit
if new_val >= self.A && (byte != 0 || carry_bit != 0) {
self.P &= 0xFF - CARRY_FLAG;
if new_val >= self.a && (byte != 0 || carry_bit != 0) {
self.p &= 0xFF - CARRY_FLAG;
} else {
self.P |= CARRY_FLAG;
self.p |= CARRY_FLAG;
}
self.set_zero_flag(new_val);
self.set_negative_flag(new_val);
// if acc is positive, mem is negative, and result is negative
// 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 res = new_val & 0x80 == 0;
// if sign is wrong, SET overflow flag
if (acc && !mem && !res) || (!acc && mem && res) {
self.P |= OVERFLOW_FLAG;
self.p |= OVERFLOW_FLAG;
} 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) {
self.P |= CARRY_FLAG;
self.p |= CARRY_FLAG;
}
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) {
self.P |= INTERRUPT_DISABLE_FLAG;
self.p |= INTERRUPT_DISABLE_FLAG;
}
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) {
self.write(_address, self.A);
self.write(_address, self.a);
}
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) {
self.write(_address, self.Y);
self.write(_address, self.y);
}
pub fn tax(&mut self, _address: usize, _mode: Mode) {
self.X = self.A;
self.set_zero_flag(self.X);
self.set_negative_flag(self.X);
self.x = self.a;
self.set_zero_flag(self.x);
self.set_negative_flag(self.x);
}
pub fn tay(&mut self, _address: usize, _mode: Mode) {
self.Y = self.A;
self.set_zero_flag(self.Y);
self.set_negative_flag(self.Y);
self.y = self.a;
self.set_zero_flag(self.y);
self.set_negative_flag(self.y);
}
pub fn tsx(&mut self, _address: usize, _mode: Mode) {
self.X = self.S;
self.set_zero_flag(self.X);
self.set_negative_flag(self.X);
self.x = self.s;
self.set_zero_flag(self.x);
self.set_negative_flag(self.x);
}
pub fn txa(&mut self, _address: usize, _mode: Mode) {
self.A = self.X;
self.set_zero_flag(self.A);
self.set_negative_flag(self.A);
self.a = self.x;
self.set_zero_flag(self.a);
self.set_negative_flag(self.a);
}
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) {
self.A = self.Y;
self.set_zero_flag(self.A);
self.set_negative_flag(self.A);
self.a = self.y;
self.set_zero_flag(self.a);
self.set_negative_flag(self.a);
}
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
pub fn nmi(&mut self) {
self.push((self.PC >> 8) as u8); // push high byte
self.push((self.PC & 0xFF) as u8); // push low byte
self.push(self.P | 0b00110000); // push status register with break bits set
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.push((self.pc >> 8) as u8); // push high byte
self.push((self.pc & 0xFF) as u8); // push low byte
self.push(self.p | 0b00110000); // push status register with break bits set
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.read(NMI_VECTOR) as usize); // and low byte
self.clock += 7;
}
pub fn irq(&mut self) {
self.push((self.PC >> 8) as u8); // push high byte
self.push((self.PC & 0xFF) as u8); // push low byte
self.push(self.P & 0b11001111); // push status register with break bits cleared
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.push((self.pc >> 8) as u8); // push high byte
self.push((self.pc & 0xFF) as u8); // push low byte
self.push(self.p & 0b11001111); // push status register with break bits cleared
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.read(IRQ_VECTOR) as usize); // and low byte
self.clock += 7;
}

View File

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

View File

@ -3,7 +3,7 @@ use super::{CARRY_FLAG, NEGATIVE_FLAG, STACK_OFFSET, ZERO_FLAG, Mode};
impl super::Cpu {
pub fn advance_pc(&mut self, mode: Mode) {
self.PC += match mode {
self.pc += match mode {
Mode::ABS => 3,
Mode::ABX => 3,
Mode::ABY => 3,
@ -24,13 +24,13 @@ impl super::Cpu {
match offset >= 0 {
true => {
let decoded_offset = offset as usize;
self.PC += decoded_offset;
self.pc += decoded_offset;
},
false => {
// instr_test-v5/rom_singles/11-stack.nes:
// letting decoded_offset be (-offset) as usize was allowing for overflow if offset was -128/0b10000000
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) {
let offset = unsigned_offset as i8;
self.clock += 1;
let old_addr = self.PC;
let old_addr = self.pc;
self.add_offset_to_pc(offset);
let new_addr = self.PC;
let new_addr = self.pc;
self.branch_page_cross(old_addr, new_addr);
}
pub fn compare(&mut self, reg: u8, byte: u8) {
if reg >= byte {
self.P |= CARRY_FLAG;
self.p |= CARRY_FLAG;
} else {
self.P &= 0xFF - CARRY_FLAG;
self.p &= 0xFF - CARRY_FLAG;
}
self.set_zero_flag(if reg == byte {0} else {1});
let diff = reg.wrapping_sub(byte);
@ -68,29 +68,29 @@ impl super::Cpu {
}
pub fn pop(&mut self) -> u8 {
self.S = self.S.wrapping_add(1);
let byte = self.read(STACK_OFFSET + self.S as usize);
self.s = self.s.wrapping_add(1);
let byte = self.read(STACK_OFFSET + self.s as usize);
byte
}
pub fn push(&mut self, byte: u8) {
self.write(STACK_OFFSET + self.S as usize, byte);
self.S = self.S.wrapping_sub(1);
self.write(STACK_OFFSET + self.s as usize, byte);
self.s = self.s.wrapping_sub(1);
}
pub fn set_negative_flag(&mut self, num: u8) {
if num & 0x80 == 0x80 {
self.P |= NEGATIVE_FLAG;
self.p |= NEGATIVE_FLAG;
} else {
self.P &= 0xFF - NEGATIVE_FLAG;
self.p &= 0xFF - NEGATIVE_FLAG;
}
}
pub fn set_zero_flag(&mut self, num: u8) {
if num == 0 {
self.P |= ZERO_FLAG;
self.p |= ZERO_FLAG;
} 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 input::poll_buttons;
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::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 audio_device = audio::initialize(sdl_context, sdl_buffer).expect("Could not create audio device");
let mut half_cycle = false;
audio_device.resume();
let mut audio_started = false;
// Initialize hardware components
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
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
if !audio_started {
audio_started = true;
audio_device.resume();
}
let now = Instant::now();
// if we're running faster than 60Hz, kill time
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();
},
Event::KeyDown{ keycode: Some(Keycode::F9), .. } => {
match find_last_filename(filepath, Some("dat")) {
match find_last_save_state(filepath, Some("dat")) {
Some(p) => {
let res: Result<(), String> = load_state(cpu, &p)
.or_else(|e| { println!("{}", e); Ok(()) } );

View File

@ -54,28 +54,28 @@ impl super::Ppu {
let base = address % 0x1000;
let offset = base % 0x0400;
match self.mapper.borrow().get_mirroring() {
Mirror::LowBank => self.nametable_A[offset],
Mirror::HighBank => self.nametable_B[offset],
Mirror::LowBank => self.nametable_a[offset],
Mirror::HighBank => self.nametable_b[offset],
Mirror::Horizontal => {
match base {
0x0000..=0x07FF => self.nametable_A[offset],
0x0800..=0x0FFF => self.nametable_B[offset],
0x0000..=0x07FF => self.nametable_a[offset],
0x0800..=0x0FFF => self.nametable_b[offset],
_ => panic!("panicked reading nametable base: {}", base),
}
},
Mirror::Vertical => {
match base {
0x0000..=0x03FF | 0x0800..=0x0BFF => self.nametable_A[offset],
0x0400..=0x07FF | 0x0C00..=0x0FFF => self.nametable_B[offset],
0x0000..=0x03FF | 0x0800..=0x0BFF => self.nametable_a[offset],
0x0400..=0x07FF | 0x0C00..=0x0FFF => self.nametable_b[offset],
_ => panic!("panicked reading nametable base: {}", base),
}
},
Mirror::FourScreen => {
match base {
0x0000..=0x03FF => self.nametable_A[offset],
0x0400..=0x07FF => self.nametable_B[offset],
0x0800..=0x0BFF => self.nametable_C[offset],
0x0C00..=0x0FFF => self.nametable_D[offset],
0x0000..=0x03FF => self.nametable_a[offset],
0x0400..=0x07FF => self.nametable_b[offset],
0x0800..=0x0BFF => self.nametable_c[offset],
0x0C00..=0x0FFF => self.nametable_d[offset],
_ => panic!("panicked reading nametable base: {}", base),
}
},
@ -86,28 +86,28 @@ impl super::Ppu {
let base = address % 0x1000;
let offset = base % 0x0400;
match self.mapper.borrow().get_mirroring() {
Mirror::LowBank => self.nametable_A[offset] = value,
Mirror::HighBank => self.nametable_B[offset] = value,
Mirror::LowBank => self.nametable_a[offset] = value,
Mirror::HighBank => self.nametable_b[offset] = value,
Mirror::Horizontal => {
match base {
0x0000..=0x07FF => self.nametable_A[offset] = value,
0x0800..=0x0FFF => self.nametable_B[offset] = value,
0x0000..=0x07FF => self.nametable_a[offset] = value,
0x0800..=0x0FFF => self.nametable_b[offset] = value,
_ => panic!("panicked writing nametable base: {}", base),
}
},
Mirror::Vertical => {
match base {
0x0000..=0x03FF | 0x0800..=0x0BFF => self.nametable_A[offset] = value,
0x0400..=0x07FF | 0x0C00..=0x0FFF => self.nametable_B[offset] = value,
0x0000..=0x03FF | 0x0800..=0x0BFF => self.nametable_a[offset] = value,
0x0400..=0x07FF | 0x0C00..=0x0FFF => self.nametable_b[offset] = value,
_ => panic!("panicked writing nametable base: {}", base),
}
},
Mirror::FourScreen => {
match base {
0x0000..=0x03FF => self.nametable_A[offset] = value,
0x0400..=0x07FF => self.nametable_B[offset] = value,
0x0800..=0x0BFF => self.nametable_C[offset] = value,
0x0C00..=0x0FFF => self.nametable_D[offset] = value,
0x0000..=0x03FF => self.nametable_a[offset] = value,
0x0400..=0x07FF => self.nametable_b[offset] = value,
0x0800..=0x0BFF => self.nametable_c[offset] = value,
0x0C00..=0x0FFF => self.nametable_d[offset] = value,
_ => 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.
// http://wiki.nesdev.com/w/index.php/MMC1 calls them higher and lower.
// They can be mirrored at certain memory ranges.
nametable_A: Vec<u8>,
nametable_B: Vec<u8>,
nametable_C: Vec<u8>,
nametable_D: Vec<u8>,
nametable_a: Vec<u8>,
nametable_b: Vec<u8>,
nametable_c: Vec<u8>,
nametable_d: Vec<u8>,
// The palette shared by both background and sprites.
// Consists of 32 bytes, each of which represents an index into the global PALETTE_TABLE.
@ -104,10 +104,10 @@ impl Ppu {
x: 0,
w: 0,
mapper: mapper,
nametable_A: vec![0u8; 0x0400],
nametable_B: vec![0u8; 0x0400],
nametable_C: vec![0u8; 0x0400],
nametable_D: vec![0u8; 0x0400],
nametable_a: vec![0u8; 0x0400],
nametable_b: vec![0u8; 0x0400],
nametable_c: vec![0u8; 0x0400],
nametable_d: vec![0u8; 0x0400],
palette_ram: vec![0u8; 0x0020],
background_pattern_sr_low: 0,
background_pattern_sr_high: 0,

View File

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

View File

@ -3,7 +3,7 @@ use super::ppu;
use super::apu;
use super::cartridge;
use std::fs::File;
use std::fs::{DirEntry, File};
use std::io::{Read, Write};
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 stem = filepath.file_stem()?.to_str()?;
let ext = new_ext.or(Some(filepath.extension()?.to_str()?)).unwrap();
let files = std::fs::read_dir(path).expect("couldn't read directory");
let mut save_states = files
.map(|f| f.unwrap().path() )
.filter(|p| {
let pfs = p.file_name().unwrap().to_str().unwrap();
pfs.len() >= stem.len()
&& pfs.len() >= ext.len()
&& &pfs[..stem.len()] == stem
&& &pfs[pfs.len()-ext.len()..] == ext
.filter(|dir_entry| {
let os_name = dir_entry.as_ref().unwrap().file_name();
let name = os_name.to_str().unwrap();
name.len() >= stem.len()
&& name.len() >= ext.len()
&& &name[..stem.len()] == stem
&& &name[name.len()-ext.len()..] == ext
})
.collect::<Vec<PathBuf>>();
save_states.sort();
.collect::<Vec<std::io::Result<DirEntry>>>();
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() {
0 => None,
_ => Some(save_states[save_states.len()-1].clone()),
_ => Some(save_states[0].as_ref().unwrap().path()),
}
}