more bug fixes. Running nestest until first unoff. opcode

This commit is contained in:
Daniel Bauer
2019-12-28 00:33:31 +01:00
parent 678c6d0092
commit 0fecfe8607
4 changed files with 104 additions and 73 deletions

View File

@@ -86,6 +86,7 @@ fn main() -> Result<(), Error> {
let mut run = false; let mut run = false;
while let Some(event) = events.next(&mut window) { while let Some(event) = events.next(&mut window) {
if let Some(_) = event.update_args() { if let Some(_) = event.update_args() {
// if cpu.regs.pc == 0xD031 || cpu.regs.pc == 0xD01A { run = false }
if run { if run {
cpu.clock(&mut bus); cpu.clock(&mut bus);
} }
@@ -292,7 +293,7 @@ fn render_disasm(glyphs: &mut GlyphBrush<Resources, Factory>,
fn render_memory(glyphs: &mut GlyphBrush<Resources, Factory>, bus: &MemoryBus, offset: [f32; 2]) { fn render_memory(glyphs: &mut GlyphBrush<Resources, Factory>, bus: &MemoryBus, offset: [f32; 2]) {
let mut position_y = (offset[0], offset[1]); let mut position_y = (offset[0], offset[1]);
for page in (0x0100..0x01FF).step_by(16) { for page in (0x0000..0x00FF).step_by(16) {
position_y.1 += FT_LINE_DISTANCE + FT_SIZE_PX; position_y.1 += FT_LINE_DISTANCE + FT_SIZE_PX;
let mut line = format!("{:#06x}:", page); let mut line = format!("{:#06x}:", page);
(0u16..16u16).map(|offset| offset + page) (0u16..16u16).map(|offset| offset + page)
@@ -309,7 +310,7 @@ fn render_memory(glyphs: &mut GlyphBrush<Resources, Factory>, bus: &MemoryBus, o
} }
position_y.1 += FT_LINE_DISTANCE+FT_SIZE_PX * 1.5; position_y.1 += FT_LINE_DISTANCE+FT_SIZE_PX * 1.5;
for page in (0x2000..0x20FF).step_by(16) { for page in (0x0400..0x06FF).step_by(16) {
position_y.1 += FT_LINE_DISTANCE + FT_SIZE_PX; position_y.1 += FT_LINE_DISTANCE + FT_SIZE_PX;
let mut line = format!("{:#06x}:", page); let mut line = format!("{:#06x}:", page);
(0u16..16u16).map(|offset| offset + page) (0u16..16u16).map(|offset| offset + page)

View File

@@ -24,7 +24,7 @@ impl Registers {
y: 0, y: 0,
sp: 0x00FD, sp: 0x00FD,
pc: 0x0000, pc: 0x0000,
flags: 0b00100100, flags: 0x24,
} }
} }
} }
@@ -371,24 +371,27 @@ impl CPU {
fn am_IZX<T: Memory>(&mut self, bus: &T) -> (Word, bool) { fn am_IZX<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
let ind_addr = self.readb_pc(bus); let ind_addr = self.readb_pc(bus);
// since its a zero page addr, we are only interested in low let lo_addr = ind_addr.wrapping_add(self.regs.x);
let addr = ind_addr.wrapping_add(self.regs.x); let hi_addr = ind_addr.wrapping_add(self.regs.x).wrapping_add(1);
let lo = self.readb(bus, lo_addr as Word);
(addr as Word, false) let hi = self.readb(bus, hi_addr as Word);
((hi as Word) << 8 | lo as Word, false)
} }
// the next 8 bits + y are an address. This address stores the real address
// that is used for the operation.
// Hardware bug: Normally, if lo of the supplied address is 0xFF, high byte
// must be read from the next page. Instead it wraps around and reads from
// the same page!
fn am_IZY<T: Memory>(&mut self, bus: &T) -> (Word, bool) { fn am_IZY<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
let ind_addr = self.readb_pc(bus); let ind_addr = self.readb_pc(bus);
// since its a zero page addr, we are only interested in low let lo = self.readb(bus, ind_addr as Word);
let addr = ind_addr.wrapping_add(self.regs.y); let hi = self.readb(bus, ind_addr.wrapping_add(1) as Word);
(addr as Word, false) let addr = (hi as Word) << 8 | lo as Word;
let addr = addr.wrapping_add(self.regs.y as Word);
if addr & HI != (hi as Word) << 8 {
(addr, true)
} else {
(addr, false)
}
} }
// Operations // Operations
@@ -402,15 +405,19 @@ impl CPU {
// Negative bit is set // Negative bit is set
fn op_ADC<T: Memory>(&mut self, bus: &T, addr: Word) -> bool { fn op_ADC<T: Memory>(&mut self, bus: &T, addr: Word) -> bool {
let val = self.readb(bus, addr) as Word; let val = self.readb(bus, addr) as Word;
let tmp1 = (self.regs.a as Word).overflowing_add(val); let tmp = self.regs.a as Word + val + self.get_flag(CARRY) as Word;
let result = tmp1.0.overflowing_add(self.get_flag(CARRY) as Word);
self.regs.a = result.0 as Byte; self.set_flag(CARRY, tmp > 255);
let is_overflown = tmp1.1 || result.1; self.set_flag_nz(tmp as Byte);
self.set_flag(CARRY, (result.0 & LO) > 255); // There are two cases where the overflow bit should be set. if we look
// at the last bit of val and A: a) 0 + 0 = 1 b) 1 + 1 = 0. This
// expression selects for both of them
let is_overflown = (!(self.regs.a as Word ^ val) &
(self.regs.a as Word ^ tmp)) & 0x0080 != 0;
self.set_flag(OVERFLOW, is_overflown); self.set_flag(OVERFLOW, is_overflown);
self.set_flag_nz(self.regs.a);
self.regs.a = tmp as Byte;
true true
} }
@@ -423,7 +430,7 @@ impl CPU {
fn op_AND<T: Memory>(&mut self, bus: &T, addr: Addr) -> bool { fn op_AND<T: Memory>(&mut self, bus: &T, addr: Addr) -> bool {
let val = self.readb(bus, addr); let val = self.readb(bus, addr);
self.regs.a &= val; self.regs.a &= val;
self.set_flag_nz(val as Byte); self.set_flag_nz(self.regs.a);
true true
} }
@@ -437,16 +444,22 @@ impl CPU {
// If the result is 0, Zero bit is set. If the result if negative, // If the result is 0, Zero bit is set. If the result if negative,
// Negative bit is set // Negative bit is set
fn op_ASL<T: Memory>(&mut self, bus: &mut T, addr: Addr) -> bool { fn op_ASL<T: Memory>(&mut self, bus: &mut T, addr: Addr) -> bool {
let val = self.readb(bus, addr); // LSR works on memory or A. We can differenciate by the addr mode
let addr_mode = &Instruction::decode_op(self.curr_op).unwrap().addr_mode;
let val = if *addr_mode == AddrMode::IMP {
self.regs.a
} else {
self.readb(bus, addr)
};
let shifted = (val << 1) as Byte; let shifted = (val << 1) as Byte;
self.set_flag(CARRY, (val & 0b1000000) == 0);
if Instruction::decode_op(self.curr_op).unwrap().addr_mode == AddrMode::IMP { if *addr_mode == AddrMode::IMP {
self.regs.a = shifted; self.regs.a = shifted;
} else { } else {
self.writeb(bus, addr, shifted); self.writeb(bus, addr, shifted);
} }
self.set_flag(CARRY, (val & 0b1000000) != 0);
self.set_flag_nz(shifted); self.set_flag_nz(shifted);
false false
} }
@@ -618,7 +631,7 @@ impl CPU {
// Compare Y // Compare Y
fn op_CPY<T: Memory>(&mut self, bus: &T, addr: Addr) -> bool { fn op_CPY<T: Memory>(&mut self, bus: &T, addr: Addr) -> bool {
let val = self.readb(bus, addr); let val = self.readb(bus, addr);
let tmp = (self.regs.a as Word).wrapping_sub(val as Word); let tmp = (self.regs.y as Word).wrapping_sub(val as Word);
self.set_flag(CARRY, self.regs.y >= val); self.set_flag(CARRY, self.regs.y >= val);
self.set_flag_nz(tmp as Byte); self.set_flag_nz(tmp as Byte);
@@ -749,12 +762,19 @@ impl CPU {
// Each of the bits in A or M is shift one place to the right. The bit // Each of the bits in A or M is shift one place to the right. The bit
// that was in bit 0 is shifted into the carry flag. Bit 7 is set to zero. // that was in bit 0 is shifted into the carry flag. Bit 7 is set to zero.
fn op_LSR<T: Memory>(&mut self, bus: &mut T, addr: Addr) -> bool { fn op_LSR<T: Memory>(&mut self, bus: &mut T, addr: Addr) -> bool {
let val = self.readb(bus, addr) as Word; // LSR works on memory or A. We can differenciate by the addr mode
self.set_flag(CARRY, (val & 0b00000001) == 1); let addr_mode = &Instruction::decode_op(self.curr_op).unwrap().addr_mode;
let val = if *addr_mode == AddrMode::IMP {
self.regs.a as Word
} else {
self.readb(bus, addr) as Word
};
self.set_flag(CARRY, (val & 0b00000001) != 0);
let shifted = (val >> 1) as Byte; let shifted = (val >> 1) as Byte;
self.set_flag_nz(shifted); self.set_flag_nz(shifted);
if Instruction::decode_op(self.curr_op).unwrap().addr_mode == AddrMode::IMP { if *addr_mode == AddrMode::IMP {
self.regs.a = shifted; self.regs.a = shifted;
} else { } else {
self.writeb(bus, addr, shifted); self.writeb(bus, addr, shifted);
@@ -787,18 +807,16 @@ impl CPU {
// PHP - Push Processor Status // PHP - Push Processor Status
// Pushes a copy of the status flags on to the stack. // Pushes a copy of the status flags on to the stack.
fn op_PHP<T: Memory>(&mut self, bus: &mut T) -> bool { fn op_PHP<T: Memory>(&mut self, bus: &mut T) -> bool {
let tmp = self.regs.flags | BREAK | UNUSED; let tmp = self.regs.flags | BREAK;
self.pushb_sp(bus, tmp); self.pushb_sp(bus, tmp);
self.set_flag(BREAK, false); self.set_flag(BREAK, false);
self.set_flag(UNUSED, false);
false false
} }
// Read from stack into A // Read from stack into A
fn op_PLA<T: Memory>(&mut self, bus: &T) -> bool { fn op_PLA<T: Memory>(&mut self, bus: &T) -> bool {
self.regs.a = self.popb_sp(bus); self.regs.a = self.popb_sp(bus);
self.set_flag(ZERO, self.regs.a == 0); self.set_flag_nz(self.regs.a);
self.set_flag(NEGATIVE, (self.regs.a & 0x80) == 1);
false false
} }
@@ -807,7 +825,10 @@ impl CPU {
// flags will take on new states as determined by the value pulled. // flags will take on new states as determined by the value pulled.
fn op_PLP<T: Memory>(&mut self, bus: &T) -> bool { fn op_PLP<T: Memory>(&mut self, bus: &T) -> bool {
self.regs.flags = self.popb_sp(bus); self.regs.flags = self.popb_sp(bus);
self.set_flag(UNUSED, true); // Just to be sure this keeps set.
// Im not sure why this is set to false and stack value is not used
// but that's how the nestest.log shows it..
self.set_flag(BREAK, false);
false false
} }
@@ -816,13 +837,18 @@ impl CPU {
// filled with the current value of the carry flag whilst the old bit 7 // filled with the current value of the carry flag whilst the old bit 7
// becomes the new carry flag value. // becomes the new carry flag value.
fn op_ROL<T: Memory>(&mut self, bus: &mut T, addr: Addr) -> bool { fn op_ROL<T: Memory>(&mut self, bus: &mut T, addr: Addr) -> bool {
let val = self.readb(bus, addr) as Word; let addr_mode = &Instruction::decode_op(self.curr_op).unwrap().addr_mode;
let val = if *addr_mode == AddrMode::IMP {
self.regs.a as Word
} else {
self.readb(bus, addr) as Word
};
let shifted = (val << 1) as Byte | self.get_flag(CARRY); let shifted = (val << 1) as Byte | self.get_flag(CARRY);
self.set_flag(CARRY, (val & 0b1000000) > 0); self.set_flag(CARRY, (val & 0b1000000) == 0);
self.set_flag_nz(shifted); self.set_flag_nz(shifted);
if Instruction::decode_op(self.curr_op).unwrap().addr_mode == AddrMode::IMP { if *addr_mode == AddrMode::IMP {
self.regs.a = shifted as Byte; self.regs.a = shifted as Byte;
} else { } else {
self.writeb(bus, addr, shifted); self.writeb(bus, addr, shifted);
@@ -835,13 +861,19 @@ impl CPU {
// filled with the current value of the carry flag whilst the old bit 0 // filled with the current value of the carry flag whilst the old bit 0
// becomes the new carry flag value. // becomes the new carry flag value.
fn op_ROR<T: Memory>(&mut self, bus: &mut T, addr: Addr) -> bool { fn op_ROR<T: Memory>(&mut self, bus: &mut T, addr: Addr) -> bool {
let val = self.readb(bus, addr) as Word; let addr_mode = &Instruction::decode_op(self.curr_op).unwrap().addr_mode;
let val = if *addr_mode == AddrMode::IMP {
self.regs.a as Word
} else {
self.readb(bus, addr) as Word
};
let shifted = (val >> 1) as Byte | (self.get_flag(CARRY) << 7); let shifted = (val >> 1) as Byte | (self.get_flag(CARRY) << 7);
self.set_flag(CARRY, (val & 0b00000001) > 0); self.set_flag(CARRY, (val & 0b00000001) > 0);
self.set_flag_nz(shifted); self.set_flag_nz(shifted);
if Instruction::decode_op(self.curr_op).unwrap().addr_mode == AddrMode::IMP { if *addr_mode == AddrMode::IMP {
self.regs.a = shifted; self.regs.a = shifted;
} else { } else {
self.writeb(bus, addr, shifted); self.writeb(bus, addr, shifted);
@@ -856,7 +888,6 @@ impl CPU {
fn op_RTI<T: Memory>(&mut self, bus: &T) -> bool { fn op_RTI<T: Memory>(&mut self, bus: &T) -> bool {
self.regs.flags = self.popb_sp(bus); self.regs.flags = self.popb_sp(bus);
self.regs.flags &= !BREAK; self.regs.flags &= !BREAK;
self.regs.flags &= !UNUSED;
let pc_lo = self.popb_sp(bus) as Word; let pc_lo = self.popb_sp(bus) as Word;
let pc_hi = self.popb_sp(bus) as Word; let pc_hi = self.popb_sp(bus) as Word;
@@ -889,16 +920,20 @@ impl CPU {
// invert buttom 8 bits // invert buttom 8 bits
let val = val ^ LO; let val = val ^ LO;
// Now its a simple addition // Now it's similar to ADC
let tmp1 = (self.regs.a as Word).overflowing_add(val); let tmp = self.regs.a as Word + val + self.get_flag(CARRY) as Word;
let result = tmp1.0.overflowing_add(self.get_flag(CARRY) as Word);
self.regs.a = result.0 as Byte; self.set_flag(CARRY, tmp > 255);
let is_overflown = tmp1.1 || result.1; self.set_flag_nz(tmp as Byte);
self.set_flag(CARRY, (result.0 & LO) > 255); // There are two cases where the overflow bit should be set. if we look
// at the last bit of val and A: a) 0 + 0 = 1 b) 1 + 1 = 0. This
// expression selects for both of them
let is_overflown = (!(self.regs.a as Word ^ val) &
(self.regs.a as Word ^ tmp)) & 0x0080 != 0;
self.set_flag(OVERFLOW, is_overflown); self.set_flag(OVERFLOW, is_overflown);
self.set_flag_nz(self.regs.a);
self.regs.a = tmp as Byte;
true true
} }
@@ -943,40 +978,35 @@ impl CPU {
// a to x // a to x
fn op_TAX(&mut self) -> bool { fn op_TAX(&mut self) -> bool {
self.regs.x = self.regs.a; self.regs.x = self.regs.a;
self.set_flag(ZERO, self.regs.x == 0); self.set_flag_nz(self.regs.x);
self.set_flag(NEGATIVE, (self.regs.x & 0x80) == 1);
false false
} }
// a to y // a to y
fn op_TAY(&mut self) -> bool { fn op_TAY(&mut self) -> bool {
self.regs.y = self.regs.a; self.regs.y = self.regs.a;
self.set_flag(ZERO, self.regs.y == 0); self.set_flag_nz(self.regs.y);
self.set_flag(NEGATIVE, (self.regs.y & 0x80) == 1);
false false
} }
// stack pointer to x // stack pointer to x
fn op_TSX(&mut self) -> bool { fn op_TSX(&mut self) -> bool {
self.regs.x = self.regs.sp; self.regs.x = self.regs.sp;
self.set_flag(ZERO, self.regs.x == 0); self.set_flag_nz(self.regs.x);
self.set_flag(NEGATIVE, (self.regs.x & 0x80) == 1);
false false
} }
// transfer x to a // transfer x to a
fn op_TXA(&mut self) -> bool { fn op_TXA(&mut self) -> bool {
self.regs.a = self.regs.x; self.regs.a = self.regs.x;
self.set_flag(ZERO, self.regs.a == 0); self.set_flag_nz(self.regs.x);
self.set_flag(NEGATIVE, (self.regs.a & 0x80) == 1);
false false
} }
// transfer y to a // transfer y to a
fn op_TYA(&mut self) -> bool { fn op_TYA(&mut self) -> bool {
self.regs.a = self.regs.y; self.regs.a = self.regs.y;
self.set_flag(ZERO, self.regs.a == 0); self.set_flag_nz(self.regs.a);
self.set_flag(NEGATIVE, (self.regs.a & 0x80) == 1);
false false
} }

View File

@@ -6,18 +6,18 @@ use std::fmt::{Debug,Display};
#[derive(Debug,PartialEq)] #[derive(Debug,PartialEq)]
pub enum AddrMode { pub enum AddrMode {
IMP, IMP, // Implied
IMM, IMM, // Immediate
ZP0, ZP0, // Zero page
ZPX, ZPX, // Zero Page with X (ZPX and ZPY are the same at nesdev)
ZPY, ZPY, // Zero Page with Y (ZPX and ZPY are the same at nesdev)
REL, REL, // Relatvive (Only for branching)
ABS, ABS, // Absolute address
ABX, ABX, // Absolute with X offset
ABY, ABY, // Absolute with Y offset
IND, IND, // Indirect addressing
IZX, IZX, // Pre Indexed
IZY, IZY, // Post Indexed
} }
impl fmt::Display for AddrMode { impl fmt::Display for AddrMode {
@@ -27,7 +27,7 @@ impl fmt::Display for AddrMode {
} }
#[derive(Debug)] #[derive(Debug,PartialEq)]
pub enum Operation { pub enum Operation {
ADC, ADC,
AND, AND,