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:
parent
13d617059c
commit
ede3545260
|
@ -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`.
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
10
src/main.rs
10
src/main.rs
|
@ -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(()) } );
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
29
src/state.rs
29
src/state.rs
|
@ -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()),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue