Add basic hardware IO.
Emulator passes bios! hooray
This commit is contained in:
parent
8df38e9c11
commit
94e73b2eda
@ -6,7 +6,7 @@ E000 FCFF Copy of the work RAM
|
|||||||
FE00 FE9F OAM (Sprite Attribute Table)
|
FE00 FE9F OAM (Sprite Attribute Table)
|
||||||
FEA0 FEFF Unused
|
FEA0 FEFF Unused
|
||||||
FF00 FF7F Hardware IO
|
FF00 FF7F Hardware IO
|
||||||
FE80 FFFE High RAM
|
FF80 FFFE High RAM
|
||||||
FFFF FFFF Interrupt switch
|
FFFF FFFF Interrupt switch
|
||||||
|
|
||||||
Hardware IO:
|
Hardware IO:
|
||||||
|
|||||||
398
src/cpu.rs
398
src/cpu.rs
@ -32,6 +32,8 @@ fn to_u16(bytes: Box<[u8]>) -> u16 {
|
|||||||
|
|
||||||
impl CPU {
|
impl CPU {
|
||||||
pub fn new(interconnect: interconnect::Interconnect) -> CPU {
|
pub fn new(interconnect: interconnect::Interconnect) -> CPU {
|
||||||
|
// Ugly patch ^.^
|
||||||
|
// interconnect.write_byte(0xFF44 as u16, 0x90);
|
||||||
CPU {
|
CPU {
|
||||||
flags: 0,
|
flags: 0,
|
||||||
regs: [0, 0, 0, 0, 0, 0, 0],
|
regs: [0, 0, 0, 0, 0, 0, 0],
|
||||||
@ -59,7 +61,8 @@ impl CPU {
|
|||||||
if reg_id == 7 {
|
if reg_id == 7 {
|
||||||
self.regs[REG_A] = value;
|
self.regs[REG_A] = value;
|
||||||
} else if reg_id == 6 {
|
} else if reg_id == 6 {
|
||||||
panic!("(HL) not supported yet.");
|
let addr: u16 = self.get_pair_value(REG_H, REG_L);
|
||||||
|
self.interconnect.write_byte(addr, value);
|
||||||
} else {
|
} else {
|
||||||
self.regs[reg_id] = value;
|
self.regs[reg_id] = value;
|
||||||
}
|
}
|
||||||
@ -70,7 +73,8 @@ impl CPU {
|
|||||||
if reg_id == 7 {
|
if reg_id == 7 {
|
||||||
self.regs[REG_A]
|
self.regs[REG_A]
|
||||||
} else if reg_id == 6 {
|
} else if reg_id == 6 {
|
||||||
panic!("(HL) not supported yet.");
|
let addr: u16 = self.get_pair_value(REG_H, REG_L);
|
||||||
|
self.interconnect.read_byte(addr)
|
||||||
} else {
|
} else {
|
||||||
self.regs[reg_id]
|
self.regs[reg_id]
|
||||||
}
|
}
|
||||||
@ -83,10 +87,75 @@ impl CPU {
|
|||||||
match instruction {
|
match instruction {
|
||||||
0x10 ... 0x17 => {
|
0x10 ... 0x17 => {
|
||||||
let reg_id = (instruction - 0x10) as usize;
|
let reg_id = (instruction - 0x10) as usize;
|
||||||
let args = self.load_args(1);
|
let val = self.get_8bit_reg(reg_id);
|
||||||
let mut val = self.get_8bit_reg(reg_id);
|
println!("RL {}", REG_NAMES[reg_id]);
|
||||||
println!("RL {}, {}", REG_NAMES[reg_id], args[0]);
|
self.regs[REG_A].rotate_left(val as u32);
|
||||||
self.set_8bit_reg(reg_id, val.rotate_left(args[0] as u32));
|
}
|
||||||
|
0x40 ... 0x47 => {
|
||||||
|
// Test 0th bit
|
||||||
|
let reg_id = (instruction - 0x40) as usize;
|
||||||
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
|
println!("BIT 0, {}", REG_NAMES[reg_id]);
|
||||||
|
if reg_content & (1 << 0) == 0 {
|
||||||
|
self.flags |= FLAG_Z;
|
||||||
|
} else {
|
||||||
|
self.flags &= !FLAG_Z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0x48 ... 0x4F => {
|
||||||
|
// Test 1th bit
|
||||||
|
let reg_id = (instruction - 0x48) as usize;
|
||||||
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
|
println!("BIT 1, {}", REG_NAMES[reg_id]);
|
||||||
|
if reg_content & (1 << 1) == 0 {
|
||||||
|
self.flags |= FLAG_Z;
|
||||||
|
} else {
|
||||||
|
self.flags &= !FLAG_Z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0x50 ... 0x57 => {
|
||||||
|
// Test 2th bit
|
||||||
|
let reg_id = (instruction - 0x50) as usize;
|
||||||
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
|
println!("BIT 2, {}", REG_NAMES[reg_id]);
|
||||||
|
if reg_content & (1 << 2) == 0 {
|
||||||
|
self.flags |= FLAG_Z;
|
||||||
|
} else {
|
||||||
|
self.flags &= !FLAG_Z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0x58 ... 0x5F => {
|
||||||
|
// Test 3th bit
|
||||||
|
let reg_id = (instruction - 0x58) as usize;
|
||||||
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
|
println!("BIT 3, {}", REG_NAMES[reg_id]);
|
||||||
|
if reg_content & (1 << 3) == 0 {
|
||||||
|
self.flags |= FLAG_Z;
|
||||||
|
} else {
|
||||||
|
self.flags &= !FLAG_Z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0x60 ... 0x67 => {
|
||||||
|
// Test 4th bit
|
||||||
|
let reg_id = (instruction - 0x60) as usize;
|
||||||
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
|
println!("BIT 4, {}", REG_NAMES[reg_id]);
|
||||||
|
if reg_content & (1 << 4) == 0 {
|
||||||
|
self.flags |= FLAG_Z;
|
||||||
|
} else {
|
||||||
|
self.flags &= !FLAG_Z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0x68 ... 0x6F => {
|
||||||
|
// Test 5th bit
|
||||||
|
let reg_id = (instruction - 0x68) as usize;
|
||||||
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
|
println!("BIT 5, {}", REG_NAMES[reg_id]);
|
||||||
|
if reg_content & (1 << 5) == 0 {
|
||||||
|
self.flags |= FLAG_Z;
|
||||||
|
} else {
|
||||||
|
self.flags &= !FLAG_Z;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
0x70 ... 0x77 => {
|
0x70 ... 0x77 => {
|
||||||
// Test 6th bit
|
// Test 6th bit
|
||||||
@ -117,51 +186,151 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_pair_value(&self, a: usize, b: usize) -> u16 {
|
fn get_pair_value(&self, a: usize, b: usize) -> u16 {
|
||||||
(self.regs[a] as u16) | (self.regs[b] as u16) << 8
|
(self.regs[a] as u16) << 8 | (self.regs[b] as u16)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_pair_value(&mut self, a: usize, b: usize, value: u16) {
|
fn set_pair_value(&mut self, a: usize, b: usize, value: u16) {
|
||||||
self.regs[a] = value as u8;
|
self.regs[a] = (value >> 8) as u8;
|
||||||
self.regs[b] = (value >> 8) as u8;
|
self.regs[b] = value as u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump_stack(&self) {
|
||||||
|
for i in self.sp .. 0xFFFE {
|
||||||
|
println!("[{:#04X}]: {:#02X}", i, self.interconnect.read_byte(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reg_inc(&mut self, reg_id: usize) {
|
||||||
|
self.regs[reg_id] = self.regs[reg_id].wrapping_add(1);
|
||||||
|
if self.regs[reg_id] == 0 {
|
||||||
|
self.flags |= FLAG_Z;
|
||||||
|
}
|
||||||
|
self.flags &= !FLAG_N;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reg_dec(&mut self, reg_id: usize) {
|
||||||
|
self.regs[reg_id] = self.regs[reg_id].wrapping_sub(1);
|
||||||
|
if self.regs[reg_id] == 0 {
|
||||||
|
self.flags |= FLAG_Z;
|
||||||
|
} else {
|
||||||
|
self.flags &= !FLAG_Z;
|
||||||
|
}
|
||||||
|
self.flags |= FLAG_N;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_instruction(&mut self) {
|
pub fn run_instruction(&mut self) {
|
||||||
let instruction = self.read_byte(self.ip);
|
let instruction = self.read_byte(self.ip);
|
||||||
print!("{:#04x}: {:#04x}: ", &self.ip, &instruction);
|
print!("{:#04x}: [SP: {:#04X}] i={:02X}. ", &self.ip, &self.sp, &instruction);
|
||||||
|
for i in 0 .. 6 {
|
||||||
|
print!("{}: {:02X} ", REG_NAMES[i], self.get_8bit_reg(i));
|
||||||
|
}
|
||||||
|
print!("A: {:02X} ", self.regs[REG_A]);
|
||||||
|
print!("I: {:02X} ", self.interconnect.read_byte(0xFFFF));
|
||||||
|
|
||||||
|
// Flags
|
||||||
|
print!("Z={} ", self.flags & FLAG_Z != 0);
|
||||||
|
print!("N={} ", self.flags & FLAG_N != 0);
|
||||||
|
print!("H={} ", self.flags & FLAG_H != 0);
|
||||||
|
print!("C={} ", self.flags & FLAG_C != 0);
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
|
|
||||||
match instruction {
|
match instruction {
|
||||||
|
0x04 => {
|
||||||
|
println!("INC B");
|
||||||
|
self.reg_inc(REG_B);
|
||||||
|
},
|
||||||
0x05 => {
|
0x05 => {
|
||||||
println!("DEC B");
|
println!("DEC B");
|
||||||
self.regs[REG_B].wrapping_sub(1);
|
self.reg_dec(REG_B);
|
||||||
}
|
},
|
||||||
0x06 => {
|
0x06 => {
|
||||||
let args = self.load_args(1);
|
let args = self.load_args(1);
|
||||||
println!("LD B, {:02x}", args[0]);
|
println!("LD B, {:02x}", args[0]);
|
||||||
self.regs[REG_B] = args[0];
|
self.regs[REG_B] = args[0];
|
||||||
}
|
},
|
||||||
0x0C => {
|
0x0C => {
|
||||||
println!("INC C");
|
println!("INC C");
|
||||||
self.regs[REG_C] += 1;
|
self.reg_inc(REG_C);
|
||||||
}
|
},
|
||||||
|
0x0D => {
|
||||||
|
println!("DEC C");
|
||||||
|
self.reg_dec(REG_C);
|
||||||
|
},
|
||||||
0x0E => {
|
0x0E => {
|
||||||
let args = self.load_args(1);
|
let args = self.load_args(1);
|
||||||
println!("LD C, {:02x}", args[0]);
|
println!("LD C, {:02x}", args[0]);
|
||||||
self.regs[REG_C] = args[0];
|
self.regs[REG_C] = args[0];
|
||||||
}
|
},
|
||||||
0x11 => {
|
0x11 => {
|
||||||
let args = self.load_args(2);
|
let args = self.load_args(2);
|
||||||
println!("LD DE, {:02x}{:02x}", args[1], args[0]);
|
let val = to_u16(args);
|
||||||
|
println!("LD DE, {:04x}", val);
|
||||||
|
self.set_pair_value(REG_D, REG_E, val);
|
||||||
|
},
|
||||||
|
0x13 => {
|
||||||
|
println!("INC DE");
|
||||||
|
let old_value = self.get_pair_value(REG_D, REG_E);
|
||||||
|
self.set_pair_value(REG_D, REG_E, old_value + 1);
|
||||||
|
},
|
||||||
|
0x14 => {
|
||||||
|
println!("INC D");
|
||||||
|
self.reg_inc(REG_D);
|
||||||
|
},
|
||||||
|
0x15 => {
|
||||||
|
println!("DEC D");
|
||||||
|
self.reg_dec(REG_D);
|
||||||
|
},
|
||||||
|
0x16 => {
|
||||||
|
let args = self.load_args(1);
|
||||||
|
println!("LD D, {:02x}", args[0]);
|
||||||
self.regs[REG_D] = args[0];
|
self.regs[REG_D] = args[0];
|
||||||
self.regs[REG_E] = args[1];
|
},
|
||||||
|
0x17 => {
|
||||||
|
println!("RLA");
|
||||||
|
let carry = self.flags & FLAG_C == FLAG_C;
|
||||||
|
if !carry {
|
||||||
|
// No carry before, now we got a carry => set it
|
||||||
|
if self.regs[REG_A] & 0x80 == 0x80 {
|
||||||
|
self.flags |= FLAG_C
|
||||||
|
}
|
||||||
|
self.regs[REG_A] = self.regs[REG_A] << 1;
|
||||||
|
} else {
|
||||||
|
if self.regs[REG_A] & 0x80 == 0 {
|
||||||
|
self.flags &= !FLAG_C;
|
||||||
|
}
|
||||||
|
self.regs[REG_A] = self.regs[REG_A] << 1 | 1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
0x18 => {
|
||||||
|
let args = self.load_args(1);
|
||||||
|
println!("JR {:02X}", args[0] as i8);
|
||||||
|
let off: i8 = args[0] as i8;
|
||||||
|
if off < 0 {
|
||||||
|
self.ip -= (-off) as u16;
|
||||||
|
} else {
|
||||||
|
self.ip += off as u16;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
0x1A => {
|
0x1A => {
|
||||||
println!("LD A, (DE)");
|
println!("LD A, (DE)");
|
||||||
self.regs[REG_A] = self.interconnect.read_byte(self.get_pair_value(REG_D, REG_E));
|
self.regs[REG_A] = self.interconnect.read_byte(self.get_pair_value(REG_D, REG_E));
|
||||||
}
|
},
|
||||||
|
0x1C => {
|
||||||
|
println!("INC E");
|
||||||
|
self.reg_inc(REG_E);
|
||||||
|
},
|
||||||
|
0x1D => {
|
||||||
|
println!("DEC E");
|
||||||
|
self.reg_dec(REG_E);
|
||||||
|
},
|
||||||
|
0x1E => {
|
||||||
|
let args = self.load_args(1);
|
||||||
|
println!("LD E, {:02x}", args[0]);
|
||||||
|
self.regs[REG_E] = args[0];
|
||||||
|
},
|
||||||
0x20 => {
|
0x20 => {
|
||||||
let args = self.load_args(1);
|
let args = self.load_args(1);
|
||||||
println!("JR NZ {:02x}", args[0]);
|
println!("JR NZ {:02x}", args[0] as i8);
|
||||||
if self.flags & FLAG_Z == 0 {
|
if self.flags & FLAG_Z == 0 {
|
||||||
let offset = args[0] as i8;
|
let offset = args[0] as i8;
|
||||||
if offset < 0 {
|
if offset < 0 {
|
||||||
@ -172,16 +341,58 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
0x21 => {
|
0x21 => {
|
||||||
let args = self.load_args(2);
|
let value = to_u16(self.load_args(2));
|
||||||
println!("LD HL, {:02x}{:02x}", args[1], args[0]);
|
println!("LD HL, {:04x}", value);
|
||||||
self.regs[REG_H] = args[0];
|
self.set_pair_value(REG_H, REG_L, value);
|
||||||
self.regs[REG_L] = args[1];
|
},
|
||||||
|
0x22 => {
|
||||||
|
println!("LD (HL+), A");
|
||||||
|
let addr: u16 = self.get_pair_value(REG_H, REG_L);
|
||||||
|
self.interconnect.write_byte(addr, self.regs[REG_A]);
|
||||||
|
self.set_pair_value(REG_H, REG_L, addr + 1);
|
||||||
|
},
|
||||||
|
0x23 => {
|
||||||
|
println!("INC HL");
|
||||||
|
let old_value = self.get_pair_value(REG_H, REG_L);
|
||||||
|
self.set_pair_value(REG_H, REG_L, old_value + 1);
|
||||||
|
},
|
||||||
|
0x24 => {
|
||||||
|
println!("INC H");
|
||||||
|
self.reg_inc(REG_H);
|
||||||
|
},
|
||||||
|
0x25 => {
|
||||||
|
println!("DEC H");
|
||||||
|
self.reg_dec(REG_H);
|
||||||
},
|
},
|
||||||
0x26 => {
|
0x26 => {
|
||||||
let args = self.load_args(1);
|
let args = self.load_args(1);
|
||||||
println!("LD H, {:02x}", args[0]);
|
println!("LD H, {:02x}", args[0]);
|
||||||
self.regs[REG_H] = args[0];
|
self.regs[REG_H] = args[0];
|
||||||
},
|
},
|
||||||
|
0x28 => {
|
||||||
|
let args = self.load_args(1);
|
||||||
|
let mut target;
|
||||||
|
if args[0] < 0 {
|
||||||
|
target = self.ip - (-(args[0] as i8)) as u16;
|
||||||
|
} else {
|
||||||
|
target = self.ip + args[0] as u16;
|
||||||
|
}
|
||||||
|
println!("LR Z, {:04X}", target);
|
||||||
|
self.ip = target;
|
||||||
|
},
|
||||||
|
0x2C => {
|
||||||
|
println!("INC L");
|
||||||
|
self.reg_inc(REG_L);
|
||||||
|
},
|
||||||
|
0x2D => {
|
||||||
|
println!("DEC L");
|
||||||
|
self.reg_dec(REG_L);
|
||||||
|
},
|
||||||
|
0x2E => {
|
||||||
|
let args = self.load_args(1);
|
||||||
|
println!("LD L, {:02x}", args[0]);
|
||||||
|
self.regs[REG_L] = args[0];
|
||||||
|
},
|
||||||
0x31 => {
|
0x31 => {
|
||||||
let args = self.load_args(2);
|
let args = self.load_args(2);
|
||||||
self.sp = to_u16(args);
|
self.sp = to_u16(args);
|
||||||
@ -189,18 +400,33 @@ impl CPU {
|
|||||||
},
|
},
|
||||||
0x32 => {
|
0x32 => {
|
||||||
println!("LD (HL-), A");
|
println!("LD (HL-), A");
|
||||||
let mut addr: u16 = (self.regs[REG_H] as u16) | ((self.regs[REG_L] as u16) << 8);
|
let mut addr = self.get_pair_value(REG_H, REG_L);
|
||||||
self.interconnect.write_byte(addr, self.regs[REG_A]);
|
self.interconnect.write_byte(addr, self.regs[REG_A]);
|
||||||
|
|
||||||
addr -= 1;
|
addr -= 1;
|
||||||
self.regs[REG_H] = addr as u8;
|
self.set_pair_value(REG_H, REG_L, addr);
|
||||||
self.regs[REG_L] = (addr >> 8) as u8;
|
|
||||||
},
|
},
|
||||||
|
0x36 => {
|
||||||
|
let args = self.load_args(1);
|
||||||
|
println!("LD (HL), {:02x}", args[0]);
|
||||||
|
let addr = self.get_pair_value(REG_H, REG_L);
|
||||||
|
self.interconnect.write_byte(addr, args[0]);
|
||||||
|
},
|
||||||
|
0x3C => {
|
||||||
|
println!("INC A");
|
||||||
|
self.reg_inc(REG_A);
|
||||||
|
}
|
||||||
|
0x3D => {
|
||||||
|
println!("DEC A");
|
||||||
|
self.reg_dec(REG_A);
|
||||||
|
}
|
||||||
0x3E => {
|
0x3E => {
|
||||||
let args = self.load_args(1);
|
let args = self.load_args(1);
|
||||||
println!("LD A, {:02x}", args[0]);
|
println!("LD A, {:02x}", args[0]);
|
||||||
self.regs[REG_A] = args[0];
|
self.regs[REG_A] = args[0];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// LDs
|
||||||
0x40 ... 0x47 => {
|
0x40 ... 0x47 => {
|
||||||
let reg_id = (instruction - 0x40) as usize;
|
let reg_id = (instruction - 0x40) as usize;
|
||||||
println!("LD B, {}", REG_NAMES[reg_id]);
|
println!("LD B, {}", REG_NAMES[reg_id]);
|
||||||
@ -235,7 +461,7 @@ impl CPU {
|
|||||||
let reg_id = (instruction - 0x70) as usize;
|
let reg_id = (instruction - 0x70) as usize;
|
||||||
println!("LD (HL), {}", REG_NAMES[reg_id]);
|
println!("LD (HL), {}", REG_NAMES[reg_id]);
|
||||||
let reg_value: u8 = self.get_8bit_reg(reg_id);
|
let reg_value: u8 = self.get_8bit_reg(reg_id);
|
||||||
let addr: u16 = (self.regs[REG_L] as u16) << 8 | (self.regs[REG_H] as u16);
|
let addr: u16 = self.get_pair_value(REG_H, REG_L);
|
||||||
|
|
||||||
self.interconnect.write_byte(addr, reg_value);
|
self.interconnect.write_byte(addr, reg_value);
|
||||||
},
|
},
|
||||||
@ -244,10 +470,81 @@ impl CPU {
|
|||||||
println!("LD A, {}", REG_NAMES[reg_id]);
|
println!("LD A, {}", REG_NAMES[reg_id]);
|
||||||
self.regs[REG_A] = self.get_8bit_reg(reg_id);
|
self.regs[REG_A] = self.get_8bit_reg(reg_id);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ADD
|
||||||
|
0x80 ... 0x87 => {
|
||||||
|
let reg_id = (instruction - 0x80) as usize;
|
||||||
|
println!("ADD {}", REG_NAMES[reg_id]);
|
||||||
|
self.regs[REG_A] = self.regs[REG_A].wrapping_add(self.get_8bit_reg(reg_id));
|
||||||
|
self.flags &= !FLAG_N;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ADC
|
||||||
|
0x88 ... 0x8F => {
|
||||||
|
let reg_id = (instruction - 0x88) as usize;
|
||||||
|
println!("ADC {}", REG_NAMES[reg_id]);
|
||||||
|
self.regs[REG_A] = self.regs[REG_A].wrapping_add(self.get_8bit_reg(reg_id));
|
||||||
|
if self.flags & FLAG_C == FLAG_C {
|
||||||
|
self.regs[REG_A] = self.regs[REG_A].wrapping_add(1);
|
||||||
|
}
|
||||||
|
self.flags &= !FLAG_N;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SUBs
|
||||||
|
0x90 ... 0x97 => {
|
||||||
|
let reg_id = (instruction - 0x90) as usize;
|
||||||
|
println!("SUB {}", REG_NAMES[reg_id]);
|
||||||
|
self.regs[REG_A] = self.regs[REG_A].wrapping_sub(self.get_8bit_reg(reg_id));
|
||||||
|
self.flags |= FLAG_N;
|
||||||
|
if self.regs[REG_A] == 0 {
|
||||||
|
self.flags |= FLAG_Z;
|
||||||
|
} else {
|
||||||
|
self.flags &= !FLAG_Z;
|
||||||
|
}
|
||||||
|
// TODO: H, C
|
||||||
|
}
|
||||||
|
|
||||||
|
// SBC
|
||||||
|
0x98 ... 0x9F => {
|
||||||
|
let reg_id = (instruction - 0x98) as usize;
|
||||||
|
println!("SBC {}", REG_NAMES[reg_id]);
|
||||||
|
self.regs[REG_A] = self.regs[REG_A].wrapping_sub(self.get_8bit_reg(reg_id));
|
||||||
|
if self.flags & FLAG_C == FLAG_C {
|
||||||
|
self.regs[REG_A] = self.regs[REG_A].wrapping_sub(1);
|
||||||
|
}
|
||||||
|
self.flags |= FLAG_N;
|
||||||
|
}
|
||||||
|
|
||||||
|
// XOR
|
||||||
0xA8 ... 0xAF => {
|
0xA8 ... 0xAF => {
|
||||||
let reg_id = (instruction - 0xA8) as usize;
|
let reg_id = (instruction - 0xA8) as usize;
|
||||||
println!("XOR {}", REG_NAMES[reg_id]);
|
println!("XOR {}", REG_NAMES[reg_id]);
|
||||||
self.regs[REG_A] ^= self.get_8bit_reg(reg_id);
|
self.regs[REG_A] ^= self.get_8bit_reg(reg_id);
|
||||||
|
if self.regs[REG_A] == 0 {
|
||||||
|
self.flags |= FLAG_Z;
|
||||||
|
} else {
|
||||||
|
self.flags &= !FLAG_Z;
|
||||||
|
}
|
||||||
|
self.flags &= !FLAG_C;
|
||||||
|
self.flags &= !FLAG_N;
|
||||||
|
self.flags &= !FLAG_H;
|
||||||
|
},
|
||||||
|
|
||||||
|
// CP
|
||||||
|
0xB8 ... 0xBF => {
|
||||||
|
let reg_id = (instruction - 0xB8) as usize;
|
||||||
|
println!("CP {}", REG_NAMES[reg_id]);
|
||||||
|
let val = self.get_8bit_reg(reg_id);
|
||||||
|
if self.regs[REG_A] < self.get_8bit_reg(reg_id) {
|
||||||
|
self.flags |= FLAG_C;
|
||||||
|
self.flags &= !FLAG_Z;
|
||||||
|
} else if self.regs[REG_A] == self.get_8bit_reg(reg_id) {
|
||||||
|
self.flags |= FLAG_Z;
|
||||||
|
self.flags &= !FLAG_C;
|
||||||
|
} else {
|
||||||
|
self.flags &= !FLAG_Z;
|
||||||
|
self.flags &= !FLAG_C;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
0xC1 => {
|
0xC1 => {
|
||||||
println!("POP BC");
|
println!("POP BC");
|
||||||
@ -258,9 +555,15 @@ impl CPU {
|
|||||||
0xC5 => {
|
0xC5 => {
|
||||||
println!("PUSH BC");
|
println!("PUSH BC");
|
||||||
let val: u16 = self.get_pair_value(REG_B, REG_C);
|
let val: u16 = self.get_pair_value(REG_B, REG_C);
|
||||||
self.interconnect.write_word(self.sp, val);
|
self.interconnect.write_word(self.sp - 2, val);
|
||||||
self.sp -= 2;
|
self.sp -= 2;
|
||||||
},
|
},
|
||||||
|
0xC9 => {
|
||||||
|
println!("RET");
|
||||||
|
self.dump_stack();
|
||||||
|
self.ip = self.interconnect.read_word(self.sp+1);
|
||||||
|
self.sp += 2;
|
||||||
|
},
|
||||||
0xCB => {
|
0xCB => {
|
||||||
// Prefix CB. This is annoying.
|
// Prefix CB. This is annoying.
|
||||||
self.run_prefix_instruction();
|
self.run_prefix_instruction();
|
||||||
@ -273,23 +576,56 @@ impl CPU {
|
|||||||
// self.interconnect.write_byte(self.sp - 1, (self.ip >> 8) as u8);
|
// self.interconnect.write_byte(self.sp - 1, (self.ip >> 8) as u8);
|
||||||
self.interconnect.write_word(self.sp - 1, self.ip);
|
self.interconnect.write_word(self.sp - 1, self.ip);
|
||||||
self.sp -= 2;
|
self.sp -= 2;
|
||||||
|
self.dump_stack();
|
||||||
|
|
||||||
self.ip = target;
|
self.ip = target;
|
||||||
}
|
},
|
||||||
0xE0 => {
|
0xE0 => {
|
||||||
let args = self.load_args(1);
|
let args = self.load_args(1);
|
||||||
println!("LDH {:02X}, A", args[0]);
|
println!("LDH {:02X}, A", args[0]);
|
||||||
self.interconnect.write_byte(0xFF00 + args[0] as u16, self.regs[REG_A]);
|
self.interconnect.write_byte(0xFF00 + args[0] as u16, self.regs[REG_A]);
|
||||||
}
|
},
|
||||||
0xE2 => {
|
0xE2 => {
|
||||||
println!("LD (C), A");
|
println!("LD (C), A");
|
||||||
|
println!("[{:04X}] = {:02X}", 0xFF00 + self.regs[REG_C] as u16, self.regs[REG_A]);
|
||||||
self.interconnect.write_byte(0xFF00 + self.regs[REG_C] as u16, self.regs[REG_A])
|
self.interconnect.write_byte(0xFF00 + self.regs[REG_C] as u16, self.regs[REG_A])
|
||||||
|
},
|
||||||
|
0xEA => {
|
||||||
|
let addr = to_u16(self.load_args(2));
|
||||||
|
println!("LD ({:04X}), A", addr);
|
||||||
|
self.interconnect.write_byte(addr, self.regs[REG_A]);
|
||||||
}
|
}
|
||||||
|
0xF0 => {
|
||||||
|
let args = self.load_args(1);
|
||||||
|
println!("LDH A, {:02X}", args[0]);
|
||||||
|
self.regs[REG_A] = self.interconnect.read_byte(0xFF00 + args[0] as u16);
|
||||||
|
},
|
||||||
0xFB => {
|
0xFB => {
|
||||||
// Enable interrupts - TODO
|
// Enable interrupts - TODO
|
||||||
println!("EI");
|
println!("EI");
|
||||||
|
self.interconnect.write_byte(0xFFFF, 0x01);
|
||||||
|
panic!("Uh uh");
|
||||||
|
},
|
||||||
|
0xFE => {
|
||||||
|
let args = self.load_args(1);
|
||||||
|
println!("CP {:02X}", args[0]);
|
||||||
|
self.flags |= FLAG_N;
|
||||||
|
if args[0] > self.regs[REG_A] {
|
||||||
|
self.flags |= FLAG_C;
|
||||||
|
} else {
|
||||||
|
self.flags &= !FLAG_C;
|
||||||
|
}
|
||||||
|
if args[0] == self.regs[REG_A] {
|
||||||
|
self.flags |= FLAG_Z;
|
||||||
|
} else {
|
||||||
|
self.flags &= !FLAG_Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO H
|
||||||
}
|
}
|
||||||
_ => panic!("Unknown instruction: {:02x}", instruction)
|
_ => panic!("Unknown instruction: {:02x}", instruction)
|
||||||
}
|
}
|
||||||
|
// self.dump_stack();
|
||||||
|
self.interconnect.tick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
51
src/display.rs
Normal file
51
src/display.rs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct Display {
|
||||||
|
control: u8,
|
||||||
|
status: u8,
|
||||||
|
background_palette: u8,
|
||||||
|
scrollx: u8,
|
||||||
|
scrolly: u8,
|
||||||
|
curline: u8,
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display {
|
||||||
|
pub fn new() -> Display {
|
||||||
|
Display::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_byte(&mut self, addr: u16, val: u8) {
|
||||||
|
match addr {
|
||||||
|
0xFF40 => self.control = val,
|
||||||
|
0xFF41 => self.status = val,
|
||||||
|
0xFF42 => self.scrolly = val,
|
||||||
|
0xFF43 => self.scrollx = val,
|
||||||
|
0xFF44 => self.curline = 0,
|
||||||
|
0xFF47 => self.background_palette = val,
|
||||||
|
_ => panic!("Display: Write {:02X} to {:04X} unsupported", val, addr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_byte(&self, addr: u16) -> u8 {
|
||||||
|
match addr {
|
||||||
|
0xFF40 => self.control,
|
||||||
|
0xFF41 => self.status,
|
||||||
|
0xFF42 => self.scrolly,
|
||||||
|
0xFF43 => self.scrollx,
|
||||||
|
0xFF44 => self.curline,
|
||||||
|
0xFF47 => self.background_palette,
|
||||||
|
_ => panic!("Display: Read from {:04X} unsupported", addr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vblank_interrupt(&mut self) {
|
||||||
|
self.curline += 1;
|
||||||
|
if self.curline > 153 {
|
||||||
|
self.curline = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn controller_interrupt(&self) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,13 +1,24 @@
|
|||||||
const RAM_SIZE: usize = 0x2000;
|
const RAM_SIZE: usize = 0x2000;
|
||||||
const VRAM_SIZE: usize = 0x2000;
|
const VRAM_SIZE: usize = 0x2000;
|
||||||
const HIRAM_SIZE: usize = (0xFFFE - 0xFE80) + 1;
|
const HIRAM_SIZE: usize = (0xFFFE - 0xFF80) + 1;
|
||||||
|
|
||||||
|
const INTERRUPT_DISPLAY: u8 = 1 << 1;
|
||||||
|
const INTERRUPT_DISPLAY_VBLANK: u8 = 1 << 0;
|
||||||
|
|
||||||
|
use super::display;
|
||||||
|
use super::sound;
|
||||||
|
|
||||||
pub struct Interconnect {
|
pub struct Interconnect {
|
||||||
bios: Box<[u8]>,
|
bios: Box<[u8]>,
|
||||||
rom: Box<[u8]>,
|
rom: Box<[u8]>,
|
||||||
ram: Box<[u8]>,
|
ram: Box<[u8]>,
|
||||||
vram: Box<[u8]>,
|
vram: Box<[u8]>,
|
||||||
hiram: Box<[u8]>
|
hiram: Box<[u8]>,
|
||||||
|
sound: sound::Sound,
|
||||||
|
display: display::Display,
|
||||||
|
interrupt: u8,
|
||||||
|
timer: u8,
|
||||||
|
disable_bootrom: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interconnect {
|
impl Interconnect {
|
||||||
@ -18,6 +29,32 @@ impl Interconnect {
|
|||||||
ram: vec![0; RAM_SIZE].into_boxed_slice(),
|
ram: vec![0; RAM_SIZE].into_boxed_slice(),
|
||||||
vram: vec![0; VRAM_SIZE].into_boxed_slice(),
|
vram: vec![0; VRAM_SIZE].into_boxed_slice(),
|
||||||
hiram: vec![0; HIRAM_SIZE].into_boxed_slice(),
|
hiram: vec![0; HIRAM_SIZE].into_boxed_slice(),
|
||||||
|
sound: sound::Sound::new(),
|
||||||
|
display: display::Display::new(),
|
||||||
|
interrupt: 0,
|
||||||
|
timer: 0,
|
||||||
|
disable_bootrom: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Somehow we need different timers for this.
|
||||||
|
pub fn tick(&mut self) {
|
||||||
|
self.timer += 1;
|
||||||
|
if self.timer == 5 {
|
||||||
|
self.display_blank_interrupt();
|
||||||
|
self.timer = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn display_blank_interrupt(&mut self) {
|
||||||
|
if self.interrupt & INTERRUPT_DISPLAY_VBLANK >= 0 {
|
||||||
|
self.display.vblank_interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn display_interrupt(&mut self) {
|
||||||
|
if self.interrupt & INTERRUPT_DISPLAY > 0 {
|
||||||
|
self.display.controller_interrupt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,7 +65,7 @@ impl Interconnect {
|
|||||||
match addr {
|
match addr {
|
||||||
0x0000 ... 0x7FFF => {
|
0x0000 ... 0x7FFF => {
|
||||||
// TODO: Check if bios or cartridge (additional condition: isEnabled)
|
// TODO: Check if bios or cartridge (additional condition: isEnabled)
|
||||||
if addr < 0x100 {
|
if addr < 0x100 && self.disable_bootrom == 0 {
|
||||||
self.bios[addr as usize]
|
self.bios[addr as usize]
|
||||||
} else {
|
} else {
|
||||||
self.rom[addr as usize]
|
self.rom[addr as usize]
|
||||||
@ -40,8 +77,20 @@ impl Interconnect {
|
|||||||
0xC000 ... 0xDFFF => {
|
0xC000 ... 0xDFFF => {
|
||||||
self.ram[(addr - 0xC000) as usize]
|
self.ram[(addr - 0xC000) as usize]
|
||||||
},
|
},
|
||||||
0xFE80 ... 0xFFFE => {
|
0xFF10 ... 0xFF26 => {
|
||||||
self.hiram[(addr - 0xFE80) as usize]
|
self.sound.read_byte(addr)
|
||||||
|
},
|
||||||
|
0xFF40 ... 0xFF4B => {
|
||||||
|
self.display.read_byte(addr)
|
||||||
|
},
|
||||||
|
0xFF50 => {
|
||||||
|
self.disable_bootrom
|
||||||
|
},
|
||||||
|
0xFF80 ... 0xFFFE => {
|
||||||
|
self.hiram[(addr - 0xFF80) as usize]
|
||||||
|
},
|
||||||
|
0xFFFF => {
|
||||||
|
self.interrupt
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
panic!("Read from {:04X} not supported.", addr);
|
panic!("Read from {:04X} not supported.", addr);
|
||||||
@ -61,7 +110,7 @@ impl Interconnect {
|
|||||||
FE00 FE9F OAM (Sprite Attribute Table)
|
FE00 FE9F OAM (Sprite Attribute Table)
|
||||||
FEA0 FEFF Unused
|
FEA0 FEFF Unused
|
||||||
FF00 FF7F Hardware IO
|
FF00 FF7F Hardware IO
|
||||||
FE80 FFFE High RAM
|
FF80 FFFE High RAM
|
||||||
FFFF FFFF Interrupt switch
|
FFFF FFFF Interrupt switch
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -72,9 +121,21 @@ impl Interconnect {
|
|||||||
0xC000 ... 0xDFFF => {
|
0xC000 ... 0xDFFF => {
|
||||||
self.ram[(addr - 0xC000) as usize] = val;
|
self.ram[(addr - 0xC000) as usize] = val;
|
||||||
},
|
},
|
||||||
0xFE80 ... 0xFFFE => {
|
0xFF10 ... 0xFF26 => {
|
||||||
self.hiram[(addr - 0xFE80) as usize] = val;
|
self.sound.write_byte(addr, val);
|
||||||
|
},
|
||||||
|
0xFF40 ... 0xFF4B => {
|
||||||
|
self.display.write_byte(addr, val);
|
||||||
|
},
|
||||||
|
0xFF50 => {
|
||||||
|
self.disable_bootrom = val;
|
||||||
}
|
}
|
||||||
|
0xFF80 ... 0xFFFE => {
|
||||||
|
self.hiram[(addr - 0xFF80) as usize] = val;
|
||||||
|
},
|
||||||
|
0xFFFF => {
|
||||||
|
self.interrupt = val;
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
panic!("Write {:02X} to {:04X} not supported.", val, addr);
|
panic!("Write {:02X} to {:04X} not supported.", val, addr);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,10 @@ use std::fs;
|
|||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
mod cpu;
|
mod cpu;
|
||||||
|
mod display;
|
||||||
mod interconnect;
|
mod interconnect;
|
||||||
|
mod sound;
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let bios_path = env::args().nth(1).unwrap();
|
let bios_path = env::args().nth(1).unwrap();
|
||||||
@ -16,11 +19,11 @@ fn main() {
|
|||||||
let rom = read_rom(&rom_path);
|
let rom = read_rom(&rom_path);
|
||||||
|
|
||||||
// Now we need to execute commands
|
// Now we need to execute commands
|
||||||
let mut interconnect = interconnect::Interconnect::new(bios, rom);
|
let interconnect = interconnect::Interconnect::new(bios, rom);
|
||||||
let mut CPU = cpu::CPU::new(interconnect);
|
let mut cpu = cpu::CPU::new(interconnect);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
CPU.run_instruction();
|
cpu.run_instruction();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
42
src/sound.rs
Normal file
42
src/sound.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct Sound {
|
||||||
|
enabled: u8,
|
||||||
|
sound_length_1: u8,
|
||||||
|
sound_control_1: u8,
|
||||||
|
sound_channel_volume_control: u8,
|
||||||
|
sound_output_terminal_selector: u8,
|
||||||
|
sound_freq_low: u8,
|
||||||
|
sound_freq_high: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sound {
|
||||||
|
pub fn new() -> Sound {
|
||||||
|
Sound::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_byte(&mut self, addr: u16, val: u8) {
|
||||||
|
match addr {
|
||||||
|
0xFF11 => self.sound_length_1 = val,
|
||||||
|
0xFF12 => self.sound_control_1 = val,
|
||||||
|
0xFF13 => self.sound_freq_low = val,
|
||||||
|
0xFF14 => self.sound_freq_high = val,
|
||||||
|
0xFF24 => self.sound_channel_volume_control = val,
|
||||||
|
0xFF25 => self.sound_output_terminal_selector = val,
|
||||||
|
0xFF26 => self.enabled = val,
|
||||||
|
_ => panic!("Sound: Write {:02X} to {:04X} unsupported", val, addr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_byte(&self, addr: u16) -> u8 {
|
||||||
|
match addr {
|
||||||
|
0xFF11 => self.sound_length_1,
|
||||||
|
0xFF12 => self.sound_control_1,
|
||||||
|
0xFF13 => self.sound_freq_low,
|
||||||
|
0xFF14 => self.sound_freq_high,
|
||||||
|
0xFF24 => self.sound_channel_volume_control,
|
||||||
|
0xFF25 => self.sound_output_terminal_selector,
|
||||||
|
0xFF26 => self.enabled,
|
||||||
|
_ => panic!("Sound: Read from {:04X} unsupported", addr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user