// // sprocketnes/cpu.rs // // Author: Patrick Walton // use mem::{Mem, MemUtil}; // // Constants // static CARRY_FLAG: u8 = 1 << 0; static ZERO_FLAG: u8 = 1 << 1; static IRQ_FLAG: u8 = 1 << 2; static DECIMAL_FLAG: u8 = 1 << 3; static BREAK_FLAG: u8 = 1 << 4; static OVERFLOW_FLAG: u8 = 1 << 6; static NEGATIVE_FLAG: u8 = 1 << 7; static NMI_VECTOR: u16 = 0xfffa; static RESET_VECTOR: u16 = 0xfffc; static BRK_VECTOR: u16 = 0xfffe; /// The number of cycles that each machine operation takes. Indexed by opcode number. /// /// FIXME: This is copied from FCEU. static CYCLE_TABLE: [u8, ..256] = [ /*0x00*/ 7,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6, /*0x10*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7, /*0x20*/ 6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6, /*0x30*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7, /*0x40*/ 6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6, /*0x50*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7, /*0x60*/ 6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6, /*0x70*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7, /*0x80*/ 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4, /*0x90*/ 2,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5, /*0xA0*/ 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4, /*0xB0*/ 2,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4, /*0xC0*/ 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6, /*0xD0*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7, /*0xE0*/ 2,6,3,8,3,3,5,5,2,2,2,2,4,4,6,6, /*0xF0*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7, ]; // // Registers // struct Regs { pub a: u8, pub x: u8, pub y: u8, pub s: u8, pub flags: u8, pub pc: u16 } impl Regs { fn new() -> Regs { Regs { a: 0, x: 0, y: 0, s: 0xfd, flags: 0x24, pc: 0xc000 } } } // // Addressing modes // trait AddressingMode { fn load(&self, cpu: &mut Cpu) -> u8; fn store(&self, cpu: &mut Cpu, val: u8); } struct AccumulatorAddressingMode; impl AddressingMode for AccumulatorAddressingMode { fn load(&self, cpu: &mut Cpu) -> u8 { cpu.regs.a } fn store(&self, cpu: &mut Cpu, val: u8) { cpu.regs.a = val } } struct ImmediateAddressingMode; impl AddressingMode for ImmediateAddressingMode { fn load(&self, cpu: &mut Cpu) -> u8 { cpu.loadb_bump_pc() } fn store(&self, _: &mut Cpu, _: u8) { // Not particularly type-safe, but probably not worth using trait inheritance for this. fail!("can't store to immediate") } } struct MemoryAddressingMode(u16); impl AddressingMode for MemoryAddressingMode { fn load(&self, cpu: &mut Cpu) -> u8 { match *self { MemoryAddressingMode(addr) => cpu.loadb(addr) } } fn store(&self, cpu: &mut Cpu, val: u8) { match *self { MemoryAddressingMode(addr) => cpu.storeb(addr, val) } } } // // Opcode decoding // // This is implemented as a macro so that both the disassembler and the emulator can use it. // macro_rules! decode_op { ($op:expr, $this:ident) => { // We try to keep this in the same order as the implementations above. // TODO: Use arm macros to fix some of this duplication. match $op { // Loads 0xa1 => { let v = $this.indexed_indirect_x(); $this.lda(v) } 0xa5 => { let v = $this.zero_page(); $this.lda(v) } 0xa9 => { let v = $this.immediate(); $this.lda(v) } 0xad => { let v = $this.absolute(); $this.lda(v) } 0xb1 => { let v = $this.indirect_indexed_y(); $this.lda(v) } 0xb5 => { let v = $this.zero_page_x(); $this.lda(v) } 0xb9 => { let v = $this.absolute_y(); $this.lda(v) } 0xbd => { let v = $this.absolute_x(); $this.lda(v) } 0xa2 => { let v = $this.immediate(); $this.ldx(v) } 0xa6 => { let v = $this.zero_page(); $this.ldx(v) } 0xb6 => { let v = $this.zero_page_y(); $this.ldx(v) } 0xae => { let v = $this.absolute(); $this.ldx(v) } 0xbe => { let v = $this.absolute_y(); $this.ldx(v) } 0xa0 => { let v = $this.immediate(); $this.ldy(v) } 0xa4 => { let v = $this.zero_page(); $this.ldy(v) } 0xb4 => { let v = $this.zero_page_x(); $this.ldy(v) } 0xac => { let v = $this.absolute(); $this.ldy(v) } 0xbc => { let v = $this.absolute_x(); $this.ldy(v) } // Stores 0x85 => { let v = $this.zero_page(); $this.sta(v) } 0x95 => { let v = $this.zero_page_x(); $this.sta(v) } 0x8d => { let v = $this.absolute(); $this.sta(v) } 0x9d => { let v = $this.absolute_x(); $this.sta(v) } 0x99 => { let v = $this.absolute_y(); $this.sta(v) } 0x81 => { let v = $this.indexed_indirect_x(); $this.sta(v) } 0x91 => { let v = $this.indirect_indexed_y(); $this.sta(v) } 0x86 => { let v = $this.zero_page(); $this.stx(v) } 0x96 => { let v = $this.zero_page_y(); $this.stx(v) } 0x8e => { let v = $this.absolute(); $this.stx(v) } 0x84 => { let v = $this.zero_page(); $this.sty(v) } 0x94 => { let v = $this.zero_page_x(); $this.sty(v) } 0x8c => { let v = $this.absolute(); $this.sty(v) } // Arithmetic 0x69 => { let v = $this.immediate(); $this.adc(v) } 0x65 => { let v = $this.zero_page(); $this.adc(v) } 0x75 => { let v = $this.zero_page_x(); $this.adc(v) } 0x6d => { let v = $this.absolute(); $this.adc(v) } 0x7d => { let v = $this.absolute_x(); $this.adc(v) } 0x79 => { let v = $this.absolute_y(); $this.adc(v) } 0x61 => { let v = $this.indexed_indirect_x(); $this.adc(v) } 0x71 => { let v = $this.indirect_indexed_y(); $this.adc(v) } 0xe9 => { let v = $this.immediate(); $this.sbc(v) } 0xe5 => { let v = $this.zero_page(); $this.sbc(v) } 0xf5 => { let v = $this.zero_page_x(); $this.sbc(v) } 0xed => { let v = $this.absolute(); $this.sbc(v) } 0xfd => { let v = $this.absolute_x(); $this.sbc(v) } 0xf9 => { let v = $this.absolute_y(); $this.sbc(v) } 0xe1 => { let v = $this.indexed_indirect_x(); $this.sbc(v) } 0xf1 => { let v = $this.indirect_indexed_y(); $this.sbc(v) } // Comparisons 0xc9 => { let v = $this.immediate(); $this.cmp(v) } 0xc5 => { let v = $this.zero_page(); $this.cmp(v) } 0xd5 => { let v = $this.zero_page_x(); $this.cmp(v) } 0xcd => { let v = $this.absolute(); $this.cmp(v) } 0xdd => { let v = $this.absolute_x(); $this.cmp(v) } 0xd9 => { let v = $this.absolute_y(); $this.cmp(v) } 0xc1 => { let v = $this.indexed_indirect_x(); $this.cmp(v) } 0xd1 => { let v = $this.indirect_indexed_y(); $this.cmp(v) } 0xe0 => { let v = $this.immediate(); $this.cpx(v) } 0xe4 => { let v = $this.zero_page(); $this.cpx(v) } 0xec => { let v = $this.absolute(); $this.cpx(v) } 0xc0 => { let v = $this.immediate(); $this.cpy(v) } 0xc4 => { let v = $this.zero_page(); $this.cpy(v) } 0xcc => { let v = $this.absolute(); $this.cpy(v) } // Bitwise operations 0x29 => { let v = $this.immediate(); $this.and(v) } 0x25 => { let v = $this.zero_page(); $this.and(v) } 0x35 => { let v = $this.zero_page_x(); $this.and(v) } 0x2d => { let v = $this.absolute(); $this.and(v) } 0x3d => { let v = $this.absolute_x(); $this.and(v) } 0x39 => { let v = $this.absolute_y(); $this.and(v) } 0x21 => { let v = $this.indexed_indirect_x(); $this.and(v) } 0x31 => { let v = $this.indirect_indexed_y(); $this.and(v) } 0x09 => { let v = $this.immediate(); $this.ora(v) } 0x05 => { let v = $this.zero_page(); $this.ora(v) } 0x15 => { let v = $this.zero_page_x(); $this.ora(v) } 0x0d => { let v = $this.absolute(); $this.ora(v) } 0x1d => { let v = $this.absolute_x(); $this.ora(v) } 0x19 => { let v = $this.absolute_y(); $this.ora(v) } 0x01 => { let v = $this.indexed_indirect_x(); $this.ora(v) } 0x11 => { let v = $this.indirect_indexed_y(); $this.ora(v) } 0x49 => { let v = $this.immediate(); $this.eor(v) } 0x45 => { let v = $this.zero_page(); $this.eor(v) } 0x55 => { let v = $this.zero_page_x(); $this.eor(v) } 0x4d => { let v = $this.absolute(); $this.eor(v) } 0x5d => { let v = $this.absolute_x(); $this.eor(v) } 0x59 => { let v = $this.absolute_y(); $this.eor(v) } 0x41 => { let v = $this.indexed_indirect_x(); $this.eor(v) } 0x51 => { let v = $this.indirect_indexed_y(); $this.eor(v) } 0x24 => { let v = $this.zero_page(); $this.bit(v) } 0x2c => { let v = $this.absolute(); $this.bit(v) } // Shifts and rotates 0x2a => { let v = $this.accumulator(); $this.rol(v) } 0x26 => { let v = $this.zero_page(); $this.rol(v) } 0x36 => { let v = $this.zero_page_x(); $this.rol(v) } 0x2e => { let v = $this.absolute(); $this.rol(v) } 0x3e => { let v = $this.absolute_x(); $this.rol(v) } 0x6a => { let v = $this.accumulator(); $this.ror(v) } 0x66 => { let v = $this.zero_page(); $this.ror(v) } 0x76 => { let v = $this.zero_page_x(); $this.ror(v) } 0x6e => { let v = $this.absolute(); $this.ror(v) } 0x7e => { let v = $this.absolute_x(); $this.ror(v) } 0x0a => { let v = $this.accumulator(); $this.asl(v) } 0x06 => { let v = $this.zero_page(); $this.asl(v) } 0x16 => { let v = $this.zero_page_x(); $this.asl(v) } 0x0e => { let v = $this.absolute(); $this.asl(v) } 0x1e => { let v = $this.absolute_x(); $this.asl(v) } 0x4a => { let v = $this.accumulator(); $this.lsr(v) } 0x46 => { let v = $this.zero_page(); $this.lsr(v) } 0x56 => { let v = $this.zero_page_x(); $this.lsr(v) } 0x4e => { let v = $this.absolute(); $this.lsr(v) } 0x5e => { let v = $this.absolute_x(); $this.lsr(v) } // Increments and decrements 0xe6 => { let v = $this.zero_page(); $this.inc(v) } 0xf6 => { let v = $this.zero_page_x(); $this.inc(v) } 0xee => { let v = $this.absolute(); $this.inc(v) } 0xfe => { let v = $this.absolute_x(); $this.inc(v) } 0xc6 => { let v = $this.zero_page(); $this.dec(v) } 0xd6 => { let v = $this.zero_page_x(); $this.dec(v) } 0xce => { let v = $this.absolute(); $this.dec(v) } 0xde => { let v = $this.absolute_x(); $this.dec(v) } 0xe8 => $this.inx(), 0xca => $this.dex(), 0xc8 => $this.iny(), 0x88 => $this.dey(), // Register moves 0xaa => $this.tax(), 0xa8 => $this.tay(), 0x8a => $this.txa(), 0x98 => $this.tya(), 0x9a => $this.txs(), 0xba => $this.tsx(), // Flag operations 0x18 => $this.clc(), 0x38 => $this.sec(), 0x58 => $this.cli(), 0x78 => $this.sei(), 0xb8 => $this.clv(), 0xd8 => $this.cld(), 0xf8 => $this.sed(), // Branches 0x10 => $this.bpl(), 0x30 => $this.bmi(), 0x50 => $this.bvc(), 0x70 => $this.bvs(), 0x90 => $this.bcc(), 0xb0 => $this.bcs(), 0xd0 => $this.bne(), 0xf0 => $this.beq(), // Jumps 0x4c => $this.jmp(), 0x6c => $this.jmpi(), // Procedure calls 0x20 => $this.jsr(), 0x60 => $this.rts(), 0x00 => $this.brk(), 0x40 => $this.rti(), // Stack operations 0x48 => $this.pha(), 0x68 => $this.pla(), 0x08 => $this.php(), 0x28 => $this.plp(), // No operation 0xea => $this.nop(), _ => warn!("unimplemented or illegal instruction") } } } // // Main CPU implementation // type Cycles = u64; /// The main CPU structure definition. pub struct Cpu { cy: Cycles, regs: Regs, mem: M, } // The CPU implements Mem so that it can handle writes to the DMA register. impl Mem for Cpu { fn loadb(&mut self, addr: u16) -> u8 { self.mem.loadb(addr) } fn storeb(&mut self, addr: u16, val: u8) { // Handle OAM_DMA. if addr == 0x4014 { self.dma(val) } else { self.mem.storeb(addr, val) } } } impl Cpu { // Debugging #[cfg(cpuspew)] fn trace(&mut self) { let mut disassembler = Disassembler { pc: self.regs.pc, mem: &mut self.mem }; println!( "%04X %-20s A:%02X X:%02X Y:%02X P:%02X SP:%02X CYC:%4u", self.regs.pc as uint, disassembler.disassemble(), self.regs.a as uint, self.regs.x as uint, self.regs.y as uint, self.regs.flags as uint, self.regs.s as uint, self.cy as uint ); } #[cfg(not(cpuspew))] fn trace(&mut self) {} // Performs DMA to the OAMDATA ($2004) register. fn dma(&mut self, hi_addr: u8) { for addr in range(hi_addr as uint << 8, (hi_addr + 1) as uint << 8) { let val = self.loadb(addr as u16); self.storeb(0x2004, val); // FIXME: The last address sometimes takes 1 cycle, sometimes 2 -- NESdev isn't very // clear on this. self.cy += 2; } } // Memory access helpers /// Loads the byte at the program counter and increments the program counter. fn loadb_bump_pc(&mut self) -> u8 { let val = self.loadb(self.regs.pc); self.regs.pc += 1; val } /// Loads two bytes (little-endian) at the program counter and bumps the program counter over /// them. fn loadw_bump_pc(&mut self) -> u16 { let val = self.loadw(self.regs.pc); self.regs.pc += 2; val } // Stack helpers fn pushb(&mut self, val: u8) { self.storeb(0x100 + self.regs.s as u16, val); self.regs.s -= 1; } fn pushw(&mut self, val: u16) { // FIXME: Is this correct? FCEU has two self.storeb()s here. Might have different // semantics... self.storew(0x100 + (self.regs.s - 1) as u16, val); self.regs.s -= 2; } fn popb(&mut self) -> u8 { let val = self.loadb(0x100 + self.regs.s as u16 + 1); self.regs.s += 1; val } fn popw(&mut self) -> u16 { // FIXME: See comment in pushw(). let val = self.loadw(0x100 + self.regs.s as u16 + 1); self.regs.s += 2; val } // Flag helpers fn get_flag(&self, flag: u8) -> bool { (self.regs.flags & flag) != 0 } fn set_flag(&mut self, flag: u8, on: bool) { if on { self.regs.flags |= flag; } else { self.regs.flags &= !flag; } } fn set_flags(&mut self, val: u8) { // Flags get munged in a strange way relating to the unused bit 5 on the NES. self.regs.flags = (val | 0x30) - 0x10; } fn set_zn(&mut self, val: u8) -> u8 { self.set_flag(ZERO_FLAG, val == 0); self.set_flag(NEGATIVE_FLAG, (val & 0x80) != 0); val } // Addressing modes fn immediate(&mut self) -> ImmediateAddressingMode { ImmediateAddressingMode } fn accumulator(&mut self) -> AccumulatorAddressingMode { AccumulatorAddressingMode } fn zero_page(&mut self) -> MemoryAddressingMode { MemoryAddressingMode(self.loadb_bump_pc() as u16) } fn zero_page_x(&mut self) -> MemoryAddressingMode { MemoryAddressingMode((self.loadb_bump_pc() + self.regs.x) as u16) } fn zero_page_y(&mut self) -> MemoryAddressingMode { MemoryAddressingMode((self.loadb_bump_pc() + self.regs.y) as u16) } fn absolute(&mut self) -> MemoryAddressingMode { MemoryAddressingMode(self.loadw_bump_pc()) } fn absolute_x(&mut self) -> MemoryAddressingMode { MemoryAddressingMode(self.loadw_bump_pc() + self.regs.x as u16) } fn absolute_y(&mut self) -> MemoryAddressingMode { MemoryAddressingMode(self.loadw_bump_pc() + self.regs.y as u16) } fn indexed_indirect_x(&mut self) -> MemoryAddressingMode { let val = self.loadb_bump_pc(); let addr = self.loadw_zp(val + self.regs.x); MemoryAddressingMode(addr) } fn indirect_indexed_y(&mut self) -> MemoryAddressingMode { let val = self.loadb_bump_pc(); let addr = self.loadw_zp(val) + self.regs.y as u16; MemoryAddressingMode(addr) } // // Instructions // // Loads fn lda>(&mut self, am: AM) { let val = am.load(self); self.regs.a = self.set_zn(val) } fn ldx>(&mut self, am: AM) { let val = am.load(self); self.regs.x = self.set_zn(val) } fn ldy>(&mut self, am: AM) { let val = am.load(self); self.regs.y = self.set_zn(val) } // Stores fn sta>(&mut self, am: AM) { am.store(self, self.regs.a) } fn stx>(&mut self, am: AM) { am.store(self, self.regs.x) } fn sty>(&mut self, am: AM) { am.store(self, self.regs.y) } // Arithmetic #[inline(always)] fn adc>(&mut self, am: AM) { let val = am.load(self); let mut result = self.regs.a as u32 + val as u32; if self.get_flag(CARRY_FLAG) { result += 1; } self.set_flag(CARRY_FLAG, (result & 0x100) != 0); let result = result as u8; self.set_flag(OVERFLOW_FLAG, (self.regs.a ^ val) & 0x80 == 0 && (self.regs.a ^ result) & 0x80 == 0x80); self.regs.a = self.set_zn(result); } #[inline(always)] fn sbc>(&mut self, am: AM) { let val = am.load(self); let mut result = self.regs.a as u32 - val as u32; if !self.get_flag(CARRY_FLAG) { result -= 1; } self.set_flag(CARRY_FLAG, (result & 0x100) == 0); let result = result as u8; self.set_flag(OVERFLOW_FLAG, (self.regs.a ^ result) & 0x80 != 0 && (self.regs.a ^ val) & 0x80 == 0x80); self.regs.a = self.set_zn(result); } // Comparisons fn cmp_base>(&mut self, x: u8, am: AM) { let y = am.load(self); let result = x as u32 - y as u32; self.set_flag(CARRY_FLAG, (result & 0x100) == 0); let _ = self.set_zn(result as u8); } fn cmp>(&mut self, am: AM) { self.cmp_base(self.regs.a, am) } fn cpx>(&mut self, am: AM) { self.cmp_base(self.regs.x, am) } fn cpy>(&mut self, am: AM) { self.cmp_base(self.regs.y, am) } // Bitwise operations fn and>(&mut self, am: AM) { let val = am.load(self) & self.regs.a; self.regs.a = self.set_zn(val) } fn ora>(&mut self, am: AM) { let val = am.load(self) | self.regs.a; self.regs.a = self.set_zn(val) } fn eor>(&mut self, am: AM) { let val = am.load(self) ^ self.regs.a; self.regs.a = self.set_zn(val) } fn bit>(&mut self, am: AM) { let val = am.load(self); self.set_flag(ZERO_FLAG, (val & self.regs.a) == 0); self.set_flag(NEGATIVE_FLAG, (val & 0x80) != 0); self.set_flag(OVERFLOW_FLAG, (val & 0x40) != 0); } // Shifts and rotates fn shl_base>(&mut self, lsb: bool, am: AM) { let val = am.load(self); let new_carry = (val & 0x80) != 0; let mut result = val << 1; if lsb { result |= 1; } self.set_flag(CARRY_FLAG, new_carry); let val = self.set_zn(result as u8); am.store(self, val) } fn shr_base>(&mut self, msb: bool, am: AM) { let val = am.load(self); let new_carry = (val & 0x1) != 0; let mut result = val >> 1; if msb { result |= 0x80; } self.set_flag(CARRY_FLAG, new_carry); let val = self.set_zn(result as u8); am.store(self, val) } fn rol>(&mut self, am: AM) { let val = self.get_flag(CARRY_FLAG); self.shl_base(val, am) } fn ror>(&mut self, am: AM) { let val = self.get_flag(CARRY_FLAG); self.shr_base(val, am) } fn asl>(&mut self, am: AM) { self.shl_base(false, am) } fn lsr>(&mut self, am: AM) { self.shr_base(false, am) } // Increments and decrements fn inc>(&mut self, am: AM) { let val = am.load(self); let val = self.set_zn(val + 1); am.store(self, val) } fn dec>(&mut self, am: AM) { let val = am.load(self); let val = self.set_zn(val - 1); am.store(self, val) } fn inx(&mut self) { self.regs.x = self.set_zn(self.regs.x + 1) } fn dex(&mut self) { self.regs.x = self.set_zn(self.regs.x - 1) } fn iny(&mut self) { self.regs.y = self.set_zn(self.regs.y + 1) } fn dey(&mut self) { self.regs.y = self.set_zn(self.regs.y - 1) } // Register moves fn tax(&mut self) { self.regs.x = self.set_zn(self.regs.a) } fn tay(&mut self) { self.regs.y = self.set_zn(self.regs.a) } fn txa(&mut self) { self.regs.a = self.set_zn(self.regs.x) } fn tya(&mut self) { self.regs.a = self.set_zn(self.regs.y) } fn txs(&mut self) { self.regs.s = self.regs.x } fn tsx(&mut self) { self.regs.x = self.set_zn(self.regs.s) } // Flag operations fn clc(&mut self) { self.set_flag(CARRY_FLAG, false) } fn sec(&mut self) { self.set_flag(CARRY_FLAG, true) } fn cli(&mut self) { self.set_flag(IRQ_FLAG, false) } fn sei(&mut self) { self.set_flag(IRQ_FLAG, true) } fn clv(&mut self) { self.set_flag(OVERFLOW_FLAG, false) } fn cld(&mut self) { self.set_flag(DECIMAL_FLAG, false) } fn sed(&mut self) { self.set_flag(DECIMAL_FLAG, true) } // Branches fn bra_base(&mut self, cond: bool) { let disp = self.loadb_bump_pc() as i8; if cond { self.regs.pc = (self.regs.pc as i32 + disp as i32) as u16; } } fn bpl(&mut self) { let flag = !self.get_flag(NEGATIVE_FLAG); self.bra_base(flag) } fn bmi(&mut self) { let flag = self.get_flag(NEGATIVE_FLAG); self.bra_base(flag) } fn bvc(&mut self) { let flag = !self.get_flag(OVERFLOW_FLAG); self.bra_base(flag) } fn bvs(&mut self) { let flag = self.get_flag(OVERFLOW_FLAG); self.bra_base(flag) } fn bcc(&mut self) { let flag = !self.get_flag(CARRY_FLAG); self.bra_base(flag) } fn bcs(&mut self) { let flag = self.get_flag(CARRY_FLAG); self.bra_base(flag) } fn bne(&mut self) { let flag = !self.get_flag(ZERO_FLAG); self.bra_base(flag) } fn beq(&mut self) { let flag = self.get_flag(ZERO_FLAG); self.bra_base(flag) } // Jumps fn jmp(&mut self) { self.regs.pc = self.loadw_bump_pc() } fn jmpi(&mut self) { let addr = self.loadw_bump_pc(); // Replicate the famous CPU bug... let lo = self.loadb(addr); let hi = self.loadb((addr & 0xff00) | ((addr + 1) & 0x00ff)); self.regs.pc = (hi as u16 << 8) | lo as u16; } // Procedure calls fn jsr(&mut self) { let addr = self.loadw_bump_pc(); self.pushw(self.regs.pc - 1); self.regs.pc = addr; } fn rts(&mut self) { self.regs.pc = self.popw() + 1 } fn brk(&mut self) { self.pushw(self.regs.pc + 1); self.pushb(self.regs.flags); // FIXME: FCEU sets BREAK_FLAG and U_FLAG here, why? self.set_flag(IRQ_FLAG, true); self.regs.pc = self.loadw(BRK_VECTOR); } fn rti(&mut self) { let flags = self.popb(); self.set_flags(flags); self.regs.pc = self.popw(); // NB: no + 1 } // Stack operations fn pha(&mut self) { self.pushb(self.regs.a) } fn pla(&mut self) { let val = self.popb(); self.regs.a = self.set_zn(val) } fn php(&mut self) { self.pushb(self.regs.flags | BREAK_FLAG) } fn plp(&mut self) { let val = self.popb(); self.set_flags(val) } // No operation fn nop(&mut self) {} // The main fetch-and-decode routine pub fn step(&mut self) { self.trace(); let op = self.loadb_bump_pc(); decode_op!(op, self); self.cy += CYCLE_TABLE[op] as Cycles; } /// External interfaces pub fn reset(&mut self) { self.regs.pc = self.loadw(RESET_VECTOR); } pub fn nmi(&mut self) { self.pushw(self.regs.pc); self.pushb(self.regs.flags); self.regs.pc = self.loadw(NMI_VECTOR); } pub fn irq(&mut self) { if self.get_flag(IRQ_FLAG) { return; } self.pushw(self.regs.pc); self.pushb(self.regs.flags); self.regs.pc = self.loadw(BRK_VECTOR); } /// The constructor. pub fn new(mem: M) -> Cpu { Cpu { cy: 0, regs: Regs::new(), mem: mem } } }