more operations, better debugging
This commit is contained in:
26
src/main.rs
26
src/main.rs
@@ -62,7 +62,9 @@ fn main() {
|
|||||||
|
|
||||||
// Create and reset CPU
|
// Create and reset CPU
|
||||||
let mut cpu: CPU = CPU::new();
|
let mut cpu: CPU = CPU::new();
|
||||||
cpu.reset(&bus);
|
cpu.find_pc_addr(&bus);
|
||||||
|
|
||||||
|
// cpu.reset(&bus);
|
||||||
|
|
||||||
// Prepare window and drawing resources
|
// Prepare window and drawing resources
|
||||||
let mut window: PistonWindow = WindowSettings::new("NESemu", [256*3, 240*2])
|
let mut window: PistonWindow = WindowSettings::new("NESemu", [256*3, 240*2])
|
||||||
@@ -192,12 +194,22 @@ fn render_cpu(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, cpu: &CPU, offset:
|
|||||||
transform,
|
transform,
|
||||||
g
|
g
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
transform = transform.trans(16.0, 0.0);
|
||||||
|
color = FT_COLOR_WHITE;
|
||||||
|
Text::new_color(color, FT_SIZE_PT).draw(
|
||||||
|
&format!("({:#4x})", cpu.regs.flags),
|
||||||
|
glyphs,
|
||||||
|
&c.draw_state,
|
||||||
|
transform,
|
||||||
|
g
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
|
||||||
let cpu_register_texts = [
|
let cpu_register_texts = [
|
||||||
&format!("A: {:#x} ({})", cpu.regs.a, cpu.regs.a),
|
&format!("A: {0:#x} ({0})", cpu.regs.a),
|
||||||
&format!("X: {:#x} ({})", cpu.regs.x, cpu.regs.y),
|
&format!("X: {0:#x} ({0})", cpu.regs.x),
|
||||||
&format!("Y: {:#x} ({})", cpu.regs.y, cpu.regs.x),
|
&format!("Y: {0:#x} ({0})", cpu.regs.y),
|
||||||
&format!("Stack P: {:#x} ({})", cpu.regs.sp, cpu.regs.sp),
|
&format!("Stack P: {0:#x} ({0})", cpu.regs.sp),
|
||||||
&format!("Program P: {:#x}", cpu.regs.pc),
|
&format!("Program P: {:#x}", cpu.regs.pc),
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -253,7 +265,7 @@ fn render_disasm(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, disasm: &Disasm,
|
|||||||
|
|
||||||
fn render_memory(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, bus: &MemoryBus, offset: [f64; 2]) {
|
fn render_memory(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, bus: &MemoryBus, offset: [f64; 2]) {
|
||||||
let mut transform_y = c.transform.trans(offset[0], offset[1]);
|
let mut transform_y = c.transform.trans(offset[0], offset[1]);
|
||||||
for page in (0x0000..0x00F0).step_by(16) {
|
for page in (0x0000..0x01FF).step_by(16) {
|
||||||
transform_y = transform_y.trans(0.0, FT_LINE_DISTANCE+FT_SIZE_PX);
|
transform_y = transform_y.trans(0.0, FT_LINE_DISTANCE+FT_SIZE_PX);
|
||||||
Text::new_color(FT_COLOR_WHITE, FT_SIZE_PT).draw(
|
Text::new_color(FT_COLOR_WHITE, FT_SIZE_PT).draw(
|
||||||
&format!("{:#06x}:", page),
|
&format!("{:#06x}:", page),
|
||||||
@@ -277,7 +289,7 @@ fn render_memory(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, bus: &MemoryBus,
|
|||||||
}
|
}
|
||||||
|
|
||||||
transform_y = transform_y.trans(0.0, (FT_LINE_DISTANCE+FT_SIZE_PX) * 1.5);
|
transform_y = transform_y.trans(0.0, (FT_LINE_DISTANCE+FT_SIZE_PX) * 1.5);
|
||||||
for page in (0x8000..0x80F0).step_by(16) {
|
for page in (0x6000..0x6030).step_by(16) {
|
||||||
transform_y = transform_y.trans(0.0, (FT_LINE_DISTANCE+FT_SIZE_PX));
|
transform_y = transform_y.trans(0.0, (FT_LINE_DISTANCE+FT_SIZE_PX));
|
||||||
Text::new_color(FT_COLOR_WHITE, FT_SIZE_PT).draw(
|
Text::new_color(FT_COLOR_WHITE, FT_SIZE_PT).draw(
|
||||||
&format!("{:#06x}:", page),
|
&format!("{:#06x}:", page),
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ impl Registers {
|
|||||||
a: 0,
|
a: 0,
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
sp: 0,
|
sp: 0x00FD,
|
||||||
pc: 0,
|
pc: 0x0000,
|
||||||
flags: 0,
|
flags: 0x0034,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,6 +36,7 @@ impl Debug for Registers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const STACK_BASE_ADDR: Addr = 0x0100;
|
||||||
|
|
||||||
pub const CARRY: Byte = 1 << 0;
|
pub const CARRY: Byte = 1 << 0;
|
||||||
pub const ZERO: Byte = 1 << 1;
|
pub const ZERO: Byte = 1 << 1;
|
||||||
@@ -79,24 +80,25 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Brings the CPU to a known state. Resets all registers and flags
|
// sets PC
|
||||||
// Read location of pc from 0xfffc
|
pub fn find_pc_addr<T: Memory>(&mut self, bus: &T) {
|
||||||
pub fn reset<T: Memory>(&mut self, bus: &T) {
|
|
||||||
// reset registers
|
|
||||||
self.regs.a = 0;
|
|
||||||
self.regs.x = 0;
|
|
||||||
self.regs.y = 0;
|
|
||||||
self.regs.sp = 0xfd;
|
|
||||||
self.regs.pc = 0x00;
|
|
||||||
self.regs.flags = 0x00 | UNUSED;
|
|
||||||
|
|
||||||
// 0xfffc and 0xfffc+1 stores the location of the first op code (where
|
// 0xfffc and 0xfffc+1 stores the location of the first op code (where
|
||||||
// the program starts). Read it and set pc accordingly.
|
// the program starts). Read it and set pc accordingly.
|
||||||
let addr: u16 = 0xfffc;
|
let addr: u16 = 0xfffc;
|
||||||
let lo = bus.readb(addr);
|
let lo = bus.readb(addr);
|
||||||
let hi = bus.readb(addr + 1);
|
let hi = bus.readb(addr + 1);
|
||||||
self.regs.pc = (hi as u16) << 8 | lo as u16;
|
self.regs.pc = (hi as u16) << 8 | lo as u16;
|
||||||
debug!("PC after reset: {:#06x}", self.regs.pc);
|
debug!("PC set to: {:#06x}", self.regs.pc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Brings the CPU to a known state. Resets all registers and flags
|
||||||
|
// Read location of pc from 0xfffc
|
||||||
|
pub fn reset<T: Memory>(&mut self, bus: &T) {
|
||||||
|
// reset registers
|
||||||
|
self.regs.sp = self.regs.sp - 3;
|
||||||
|
self.set_flag(IRQ, true);
|
||||||
|
|
||||||
|
self.find_pc_addr(bus);
|
||||||
|
|
||||||
// A reset takes 8 CPU clocks
|
// A reset takes 8 CPU clocks
|
||||||
self.cycles = 8;
|
self.cycles = 8;
|
||||||
@@ -185,8 +187,8 @@ impl CPU {
|
|||||||
Operation::DEY => self.op_DEY(),
|
Operation::DEY => self.op_DEY(),
|
||||||
Operation::EOR => self.op_EOR(bus, value),
|
Operation::EOR => self.op_EOR(bus, value),
|
||||||
Operation::INC => self.op_INC(bus, value),
|
Operation::INC => self.op_INC(bus, value),
|
||||||
Operation::INX => self.op_INX(bus, value),
|
Operation::INX => self.op_INX(bus),
|
||||||
Operation::INY => self.op_INY(bus, value),
|
Operation::INY => self.op_INY(bus),
|
||||||
Operation::JMP => self.op_JMP(bus, value),
|
Operation::JMP => self.op_JMP(bus, value),
|
||||||
Operation::JSR => self.op_JSR(bus, value),
|
Operation::JSR => self.op_JSR(bus, value),
|
||||||
Operation::LDA => self.op_LDA(bus, value),
|
Operation::LDA => self.op_LDA(bus, value),
|
||||||
@@ -196,7 +198,7 @@ impl CPU {
|
|||||||
Operation::NOP => self.op_NOP(bus, value),
|
Operation::NOP => self.op_NOP(bus, value),
|
||||||
Operation::ORA => self.op_ORA(bus, value),
|
Operation::ORA => self.op_ORA(bus, value),
|
||||||
Operation::PHA => self.op_PHA(bus, value),
|
Operation::PHA => self.op_PHA(bus, value),
|
||||||
Operation::PHP => self.op_PHP(bus, value),
|
Operation::PHP => self.op_PHP(bus),
|
||||||
Operation::PLA => self.op_PLA(bus, value),
|
Operation::PLA => self.op_PLA(bus, value),
|
||||||
Operation::PLP => self.op_PLP(bus, value),
|
Operation::PLP => self.op_PLP(bus, value),
|
||||||
Operation::ROL => self.op_ROL(bus, value),
|
Operation::ROL => self.op_ROL(bus, value),
|
||||||
@@ -212,7 +214,7 @@ impl CPU {
|
|||||||
Operation::STY => self.op_STY(bus, value),
|
Operation::STY => self.op_STY(bus, value),
|
||||||
Operation::TAX => self.op_TAX(bus, value),
|
Operation::TAX => self.op_TAX(bus, value),
|
||||||
Operation::TAY => self.op_TAY(bus, value),
|
Operation::TAY => self.op_TAY(bus, value),
|
||||||
Operation::TSX => self.op_TSX(bus, value),
|
Operation::TSX => self.op_TSX(bus),
|
||||||
Operation::TXA => self.op_TXA(bus, value),
|
Operation::TXA => self.op_TXA(bus, value),
|
||||||
Operation::TXS => self.op_TXS(bus, value),
|
Operation::TXS => self.op_TXS(bus, value),
|
||||||
Operation::TYA => self.op_TYA(bus, value),
|
Operation::TYA => self.op_TYA(bus, value),
|
||||||
@@ -303,7 +305,7 @@ impl CPU {
|
|||||||
// must be read from the next page. Instead it wraps around and reads from
|
// must be read from the next page. Instead it wraps around and reads from
|
||||||
// the same page!
|
// the same page!
|
||||||
fn am_IZX<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
fn am_IZX<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
||||||
let mut ind_addr = self.readw_pc(bus);
|
let mut ind_addr = self.readb_pc(bus) as Word;
|
||||||
ind_addr += self.regs.x as Word;
|
ind_addr += self.regs.x as Word;
|
||||||
|
|
||||||
let lo = ind_addr & 0b00001111;
|
let lo = ind_addr & 0b00001111;
|
||||||
@@ -322,7 +324,7 @@ impl CPU {
|
|||||||
// must be read from the next page. Instead it wraps around and reads from
|
// must be read from the next page. Instead it wraps around and reads from
|
||||||
// the same page!
|
// 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 mut ind_addr = self.readw_pc(bus);
|
let mut ind_addr = self.readb_pc(bus) as Word;
|
||||||
ind_addr += self.regs.y as Word;
|
ind_addr += self.regs.y as Word;
|
||||||
|
|
||||||
let lo = ind_addr & 0b00001111;
|
let lo = ind_addr & 0b00001111;
|
||||||
@@ -460,12 +462,16 @@ impl CPU {
|
|||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_INX<T: Memory>(&mut self, bus: &T, val: Word) {
|
fn op_INX<T: Memory>(&mut self, bus: &T) {
|
||||||
unimplemented!()
|
self.regs.x += 1;
|
||||||
|
self.set_flag(ZERO, self.regs.x == 0);
|
||||||
|
self.set_flag(NEGATIVE, (self.regs.x & 0x80) != 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_INY<T: Memory>(&mut self, bus: &T, val: Word) {
|
fn op_INY<T: Memory>(&mut self, bus: &T) {
|
||||||
unimplemented!()
|
self.regs.y += 1;
|
||||||
|
self.set_flag(ZERO, self.regs.y == 0);
|
||||||
|
self.set_flag(NEGATIVE, (self.regs.y & 0x80) != 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jump to address (set pc)
|
// Jump to address (set pc)
|
||||||
@@ -476,9 +482,9 @@ impl CPU {
|
|||||||
// Jump to subroutine (leaves trace on the stack)
|
// Jump to subroutine (leaves trace on the stack)
|
||||||
fn op_JSR<T: Memory>(&mut self, bus: &mut T, addr: Word) {
|
fn op_JSR<T: Memory>(&mut self, bus: &mut T, addr: Word) {
|
||||||
self.regs.pc -= 1;
|
self.regs.pc -= 1;
|
||||||
bus.writeb(0x0100 + self.regs.sp as Word, ((self.regs.pc >> 8) & 0x00ff) as Byte);
|
bus.writeb(STACK_BASE_ADDR + self.regs.sp as Word, ((self.regs.pc >> 8) & 0x00ff) as Byte);
|
||||||
self.regs.sp -= 1;
|
self.regs.sp -= 1;
|
||||||
bus.writeb(0x0100 + self.regs.sp as Word, (self.regs.pc & 0x00ff) as Byte);
|
bus.writeb(STACK_BASE_ADDR + self.regs.sp as Word, (self.regs.pc & 0x00ff) as Byte);
|
||||||
self.regs.sp -= 1;
|
self.regs.sp -= 1;
|
||||||
self.regs.pc = addr;
|
self.regs.pc = addr;
|
||||||
}
|
}
|
||||||
@@ -524,18 +530,19 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write flags to stack
|
// Write flags to stack
|
||||||
fn op_PHP<T: Memory>(&mut self, bus: &mut T, val: Word) {
|
fn op_PHP<T: Memory>(&mut self, bus: &mut T) {
|
||||||
let tmp = self.regs.flags | BREAK | UNUSED;
|
let tmp = self.regs.flags | BREAK | UNUSED;
|
||||||
bus.writeb(0x0100 + self.regs.sp as Word, tmp);
|
bus.writeb(STACK_BASE_ADDR + self.regs.sp as Word, tmp);
|
||||||
self.set_flag(BREAK, false);
|
self.set_flag(BREAK, false);
|
||||||
self.set_flag(UNUSED, false);
|
self.set_flag(UNUSED, false);
|
||||||
self.regs.sp -= 1;
|
self.regs.sp -= 1;
|
||||||
|
println!("PHP executed");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read from stack into A
|
// Read from stack into A
|
||||||
fn op_PLA<T: Memory>(&mut self, bus: &T, val: Word) {
|
fn op_PLA<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||||
self.regs.sp += 1;
|
self.regs.sp += 1;
|
||||||
self.regs.a = bus.readb(0x0100 + self.regs.sp as Word);
|
self.regs.a = bus.readb(STACK_BASE_ADDR + self.regs.sp as Word);
|
||||||
self.set_flag(ZERO, self.regs.a == 0);
|
self.set_flag(ZERO, self.regs.a == 0);
|
||||||
self.set_flag(NEGATIVE, (self.regs.a & 0x80) == 1)
|
self.set_flag(NEGATIVE, (self.regs.a & 0x80) == 1)
|
||||||
}
|
}
|
||||||
@@ -606,8 +613,9 @@ impl CPU {
|
|||||||
self.set_flag(NEGATIVE, (self.regs.y & 0x80) == 1)
|
self.set_flag(NEGATIVE, (self.regs.y & 0x80) == 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_TSX<T: Memory>(&mut self, bus: &T, val: Word) {
|
// stack pointer to x
|
||||||
self.regs.x = bus.readb(0x0100 + self.regs.sp as Word);
|
fn op_TSX<T: Memory>(&mut self, bus: &T) {
|
||||||
|
self.regs.x = self.regs.sp;
|
||||||
self.set_flag(ZERO, self.regs.x == 0);
|
self.set_flag(ZERO, self.regs.x == 0);
|
||||||
self.set_flag(NEGATIVE, (self.regs.x & 0x80) == 1)
|
self.set_flag(NEGATIVE, (self.regs.x & 0x80) == 1)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,11 +44,14 @@ impl Disasm {
|
|||||||
let jmp_addr = Disasm::get_rel_addr(rel_addr, addr+2);
|
let jmp_addr = Disasm::get_rel_addr(rel_addr, addr+2);
|
||||||
format!("#{:02x} => {:#06x}", rel_addr, jmp_addr)
|
format!("#{:02x} => {:#06x}", rel_addr, jmp_addr)
|
||||||
},
|
},
|
||||||
|
AddrMode::IND => {
|
||||||
AddrMode::IND | AddrMode::IZX | AddrMode::IZY=> {
|
|
||||||
let addr = mem.readw(mem_iter.next().unwrap());
|
let addr = mem.readw(mem_iter.next().unwrap());
|
||||||
mem_iter.next().unwrap();
|
mem_iter.next().unwrap();
|
||||||
format!("{:#06x}", addr)
|
format!("{:#06x}", addr)
|
||||||
|
},
|
||||||
|
AddrMode::IZX | AddrMode::IZY => {
|
||||||
|
let addr = mem.readb(mem_iter.next().unwrap());
|
||||||
|
format!("{:#06x}", addr)
|
||||||
}
|
}
|
||||||
AddrMode::IMP => String::from(""),
|
AddrMode::IMP => String::from(""),
|
||||||
_ => bail!("Disassembly of {} not implemented", i.addr_mode),
|
_ => bail!("Disassembly of {} not implemented", i.addr_mode),
|
||||||
|
|||||||
Reference in New Issue
Block a user