diff --git a/README.md b/README.md index 716280e..d981702 100644 --- a/README.md +++ b/README.md @@ -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`. diff --git a/src/audio.rs b/src/audio.rs index afbe020..573ff9c 100644 --- a/src/audio.rs +++ b/src/audio.rs @@ -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 } } } diff --git a/src/cpu/addressing_modes.rs b/src/cpu/addressing_modes.rs index fe88a06..63aaa61 100644 --- a/src/cpu/addressing_modes.rs +++ b/src/cpu/addressing_modes.rs @@ -3,15 +3,15 @@ impl super::Cpu { pub fn absolute(&mut self) -> usize { self.clock += 4; ::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 } } diff --git a/src/cpu/mod.rs b/src/cpu/mod.rs index 0862895..771be3a 100644 --- a/src/cpu/mod.rs +++ b/src/cpu/mod.rs @@ -55,12 +55,12 @@ impl Mode { pub struct Cpu { mem: Vec, // 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>, 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 = ::from(self.read(self.PC)); + let opcode = ::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, ); } diff --git a/src/cpu/opcodes.rs b/src/cpu/opcodes.rs index b9d9f4e..c677a6e 100644 --- a/src/cpu/opcodes.rs +++ b/src/cpu/opcodes.rs @@ -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; } diff --git a/src/cpu/serialize.rs b/src/cpu/serialize.rs index 912f60f..aa20fdb 100644 --- a/src/cpu/serialize.rs +++ b/src/cpu/serialize.rs @@ -5,12 +5,12 @@ use serde::{Serialize, Deserialize}; #[derive(Serialize, Deserialize, Debug)] pub struct CpuData { mem: Vec, - 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; diff --git a/src/cpu/utility.rs b/src/cpu/utility.rs index 5db8aac..e59b08a 100644 --- a/src/cpu/utility.rs +++ b/src/cpu/utility.rs @@ -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; } } diff --git a/src/main.rs b/src/main.rs index 158e954..63a258a 100644 --- a/src/main.rs +++ b/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(()) } ); diff --git a/src/ppu/memory.rs b/src/ppu/memory.rs index f26e7dd..ce32a61 100644 --- a/src/ppu/memory.rs +++ b/src/ppu/memory.rs @@ -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), } }, diff --git a/src/ppu/mod.rs b/src/ppu/mod.rs index 2d50fee..129dca7 100644 --- a/src/ppu/mod.rs +++ b/src/ppu/mod.rs @@ -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, - nametable_B: Vec, - nametable_C: Vec, - nametable_D: Vec, + nametable_a: Vec, + nametable_b: Vec, + nametable_c: Vec, + nametable_d: Vec, // 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, diff --git a/src/ppu/serialize.rs b/src/ppu/serialize.rs index c923919..ac658ca 100644 --- a/src/ppu/serialize.rs +++ b/src/ppu/serialize.rs @@ -9,10 +9,10 @@ pub struct PpuData { t: u16, x: u8, w: u8, - nametable_A: Vec, - nametable_B: Vec, - nametable_C: Vec, - nametable_D: Vec, + nametable_a: Vec, + nametable_b: Vec, + nametable_c: Vec, + nametable_d: Vec, palette_ram: Vec, 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; diff --git a/src/state.rs b/src/state.rs index 5106f13..5cb6091 100644 --- a/src/state.rs +++ b/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

) -> Option { +pub fn find_last_save_state(filepath: &PathBuf, new_ext: Option<&str>) -> Option { 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::>(); - save_states.sort(); + .collect::>>(); + 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()), } }