Serial and Timer class, cycle counting

Also a little bit of refactoring.
Move VRAM to the display object
This commit is contained in:
Kevin Hamacher 2016-05-26 15:47:08 +02:00
parent 94e73b2eda
commit 396e87304e
6 changed files with 359 additions and 32 deletions

View File

@ -24,6 +24,8 @@ pub struct CPU {
sp: u16, sp: u16,
interconnect: interconnect::Interconnect, interconnect: interconnect::Interconnect,
interrupts_enabled: bool,
} }
fn to_u16(bytes: Box<[u8]>) -> u16 { fn to_u16(bytes: Box<[u8]>) -> u16 {
@ -39,7 +41,8 @@ impl CPU {
regs: [0, 0, 0, 0, 0, 0, 0], regs: [0, 0, 0, 0, 0, 0, 0],
ip: 0, ip: 0,
sp: 0xFFFE, sp: 0xFFFE,
interconnect: interconnect interconnect: interconnect,
interrupts_enabled: false, // Is this correct?
} }
} }
@ -234,56 +237,80 @@ impl CPU {
print!("C={} ", self.flags & FLAG_C != 0); print!("C={} ", self.flags & FLAG_C != 0);
self.ip += 1; self.ip += 1;
let mut cycles: u16;
match instruction { match instruction {
0x00 => {
println!("NOP");
cycles = 4;
},
0x01 => {
println!("LD (BC), A");
let addr: u16 = self.get_pair_value(REG_B, REG_C);
let val: u8 = self.regs[REG_A];
self.interconnect.write_byte(addr, val);
cycles = 12;
}
0x04 => { 0x04 => {
println!("INC B"); println!("INC B");
self.reg_inc(REG_B); self.reg_inc(REG_B);
cycles = 4;
}, },
0x05 => { 0x05 => {
println!("DEC B"); println!("DEC B");
self.reg_dec(REG_B); self.reg_dec(REG_B);
cycles = 4;
}, },
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];
cycles = 8;
}, },
0x0C => { 0x0C => {
println!("INC C"); println!("INC C");
self.reg_inc(REG_C); self.reg_inc(REG_C);
cycles = 4;
}, },
0x0D => { 0x0D => {
println!("DEC C"); println!("DEC C");
self.reg_dec(REG_C); self.reg_dec(REG_C);
cycles = 4;
}, },
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];
cycles = 8;
}, },
0x11 => { 0x11 => {
let args = self.load_args(2); let args = self.load_args(2);
let val = to_u16(args); let val = to_u16(args);
println!("LD DE, {:04x}", val); println!("LD DE, {:04x}", val);
self.set_pair_value(REG_D, REG_E, val); self.set_pair_value(REG_D, REG_E, val);
cycles = 12;
}, },
0x13 => { 0x13 => {
println!("INC DE"); println!("INC DE");
let old_value = self.get_pair_value(REG_D, REG_E); let old_value = self.get_pair_value(REG_D, REG_E);
self.set_pair_value(REG_D, REG_E, old_value + 1); self.set_pair_value(REG_D, REG_E, old_value + 1);
cycles = 8;
}, },
0x14 => { 0x14 => {
println!("INC D"); println!("INC D");
self.reg_inc(REG_D); self.reg_inc(REG_D);
cycles = 4;
}, },
0x15 => { 0x15 => {
println!("DEC D"); println!("DEC D");
self.reg_dec(REG_D); self.reg_dec(REG_D);
cycles = 4;
}, },
0x16 => { 0x16 => {
let args = self.load_args(1); let args = self.load_args(1);
println!("LD D, {:02x}", args[0]); println!("LD D, {:02x}", args[0]);
self.regs[REG_D] = args[0]; self.regs[REG_D] = args[0];
cycles = 8;
}, },
0x17 => { 0x17 => {
println!("RLA"); println!("RLA");
@ -300,6 +327,7 @@ impl CPU {
} }
self.regs[REG_A] = self.regs[REG_A] << 1 | 1; self.regs[REG_A] = self.regs[REG_A] << 1 | 1;
} }
cycles = 4;
}, },
0x18 => { 0x18 => {
let args = self.load_args(1); let args = self.load_args(1);
@ -310,23 +338,28 @@ impl CPU {
} else { } else {
self.ip += off as u16; self.ip += off as u16;
} }
cycles = 12;
}, },
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));
cycles = 8;
}, },
0x1C => { 0x1C => {
println!("INC E"); println!("INC E");
self.reg_inc(REG_E); self.reg_inc(REG_E);
cycles = 4;
}, },
0x1D => { 0x1D => {
println!("DEC E"); println!("DEC E");
self.reg_dec(REG_E); self.reg_dec(REG_E);
cycles = 4;
}, },
0x1E => { 0x1E => {
let args = self.load_args(1); let args = self.load_args(1);
println!("LD E, {:02x}", args[0]); println!("LD E, {:02x}", args[0]);
self.regs[REG_E] = args[0]; self.regs[REG_E] = args[0];
cycles = 8;
}, },
0x20 => { 0x20 => {
let args = self.load_args(1); let args = self.load_args(1);
@ -338,36 +371,45 @@ impl CPU {
} else { } else {
self.ip += offset as u16; self.ip += offset as u16;
} }
cycles = 12;
} else {
cycles = 8;
} }
} }
0x21 => { 0x21 => {
let value = to_u16(self.load_args(2)); let value = to_u16(self.load_args(2));
println!("LD HL, {:04x}", value); println!("LD HL, {:04x}", value);
self.set_pair_value(REG_H, REG_L, value); self.set_pair_value(REG_H, REG_L, value);
cycles = 12;
}, },
0x22 => { 0x22 => {
println!("LD (HL+), A"); println!("LD (HL+), A");
let addr: u16 = self.get_pair_value(REG_H, REG_L); let addr: u16 = 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]);
self.set_pair_value(REG_H, REG_L, addr + 1); self.set_pair_value(REG_H, REG_L, addr + 1);
cycles = 8;
}, },
0x23 => { 0x23 => {
println!("INC HL"); println!("INC HL");
let old_value = self.get_pair_value(REG_H, REG_L); let old_value = self.get_pair_value(REG_H, REG_L);
self.set_pair_value(REG_H, REG_L, old_value + 1); self.set_pair_value(REG_H, REG_L, old_value + 1);
cycles = 8;
}, },
0x24 => { 0x24 => {
println!("INC H"); println!("INC H");
self.reg_inc(REG_H); self.reg_inc(REG_H);
cycles = 4;
}, },
0x25 => { 0x25 => {
println!("DEC H"); println!("DEC H");
self.reg_dec(REG_H); self.reg_dec(REG_H);
cycles = 4;
}, },
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];
cycles = 8;
}, },
0x28 => { 0x28 => {
let args = self.load_args(1); let args = self.load_args(1);
@ -377,26 +419,35 @@ impl CPU {
} else { } else {
target = self.ip + args[0] as u16; target = self.ip + args[0] as u16;
} }
println!("LR Z, {:04X}", target); println!("JR Z, {:04X}", target);
self.ip = target; if self.flags & FLAG_Z > 0 {
self.ip = target;
cycles = 12;
} else {
cycles = 8;
}
}, },
0x2C => { 0x2C => {
println!("INC L"); println!("INC L");
self.reg_inc(REG_L); self.reg_inc(REG_L);
cycles = 4;
}, },
0x2D => { 0x2D => {
println!("DEC L"); println!("DEC L");
self.reg_dec(REG_L); self.reg_dec(REG_L);
cycles = 4;
}, },
0x2E => { 0x2E => {
let args = self.load_args(1); let args = self.load_args(1);
println!("LD L, {:02x}", args[0]); println!("LD L, {:02x}", args[0]);
self.regs[REG_L] = args[0]; self.regs[REG_L] = args[0];
cycles = 8;
}, },
0x31 => { 0x31 => {
let args = self.load_args(2); let args = self.load_args(2);
self.sp = to_u16(args); self.sp = to_u16(args);
println!("LD SP, {:02x}", self.sp); println!("LD SP, {:02x}", self.sp);
cycles = 12;
}, },
0x32 => { 0x32 => {
println!("LD (HL-), A"); println!("LD (HL-), A");
@ -405,25 +456,30 @@ impl CPU {
addr -= 1; addr -= 1;
self.set_pair_value(REG_H, REG_L, addr); self.set_pair_value(REG_H, REG_L, addr);
cycles = 8;
}, },
0x36 => { 0x36 => {
let args = self.load_args(1); let args = self.load_args(1);
println!("LD (HL), {:02x}", args[0]); println!("LD (HL), {:02x}", args[0]);
let addr = self.get_pair_value(REG_H, REG_L); let addr = self.get_pair_value(REG_H, REG_L);
self.interconnect.write_byte(addr, args[0]); self.interconnect.write_byte(addr, args[0]);
cycles = 12;
}, },
0x3C => { 0x3C => {
println!("INC A"); println!("INC A");
self.reg_inc(REG_A); self.reg_inc(REG_A);
cycles = 4;
} }
0x3D => { 0x3D => {
println!("DEC A"); println!("DEC A");
self.reg_dec(REG_A); self.reg_dec(REG_A);
cycles = 4;
} }
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];
cycles = 8;
}, },
// LDs // LDs
@ -431,37 +487,44 @@ impl CPU {
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]);
self.regs[REG_B] = self.get_8bit_reg(reg_id); self.regs[REG_B] = self.get_8bit_reg(reg_id);
cycles = 4;
}, },
0x48 ... 0x4F => { 0x48 ... 0x4F => {
let reg_id = (instruction - 0x48) as usize; let reg_id = (instruction - 0x48) as usize;
println!("LD C, {}", REG_NAMES[reg_id]); println!("LD C, {}", REG_NAMES[reg_id]);
self.regs[REG_C] = self.get_8bit_reg(reg_id); self.regs[REG_C] = self.get_8bit_reg(reg_id);
cycles = 4;
}, },
0x50 ... 0x57 => { 0x50 ... 0x57 => {
let reg_id = (instruction - 0x50) as usize; let reg_id = (instruction - 0x50) as usize;
println!("LD D, {}", REG_NAMES[reg_id]); println!("LD D, {}", REG_NAMES[reg_id]);
self.regs[REG_D] = self.get_8bit_reg(reg_id); self.regs[REG_D] = self.get_8bit_reg(reg_id);
cycles = 4;
}, },
0x58 ... 0x5F => { 0x58 ... 0x5F => {
let reg_id = (instruction - 0x58) as usize; let reg_id = (instruction - 0x58) as usize;
println!("LD E, {}", REG_NAMES[reg_id]); println!("LD E, {}", REG_NAMES[reg_id]);
self.regs[REG_E] = self.get_8bit_reg(reg_id); self.regs[REG_E] = self.get_8bit_reg(reg_id);
cycles = 4;
}, },
0x60 ... 0x67 => { 0x60 ... 0x67 => {
let reg_id = (instruction - 0x60) as usize; let reg_id = (instruction - 0x60) as usize;
println!("LD H, {}", REG_NAMES[reg_id]); println!("LD H, {}", REG_NAMES[reg_id]);
self.regs[REG_H] = self.get_8bit_reg(reg_id); self.regs[REG_H] = self.get_8bit_reg(reg_id);
cycles = 4;
}, },
0x68 ... 0x6F => { 0x68 ... 0x6F => {
let reg_id = (instruction - 0x68) as usize; let reg_id = (instruction - 0x68) as usize;
println!("LD L, {}", REG_NAMES[reg_id]); println!("LD L, {}", REG_NAMES[reg_id]);
self.regs[REG_L] = self.get_8bit_reg(reg_id); self.regs[REG_L] = self.get_8bit_reg(reg_id);
cycles = 4;
}, },
0x70 ... 0x75 | 0x77 => { 0x70 ... 0x75 | 0x77 => {
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.get_pair_value(REG_H, REG_L); let addr: u16 = self.get_pair_value(REG_H, REG_L);
cycles = 8;
self.interconnect.write_byte(addr, reg_value); self.interconnect.write_byte(addr, reg_value);
}, },
@ -469,6 +532,7 @@ impl CPU {
let reg_id = (instruction - 0x78) as usize; let reg_id = (instruction - 0x78) as usize;
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);
cycles = 4;
}, },
// ADD // ADD
@ -477,6 +541,7 @@ impl CPU {
println!("ADD {}", REG_NAMES[reg_id]); println!("ADD {}", REG_NAMES[reg_id]);
self.regs[REG_A] = self.regs[REG_A].wrapping_add(self.get_8bit_reg(reg_id)); self.regs[REG_A] = self.regs[REG_A].wrapping_add(self.get_8bit_reg(reg_id));
self.flags &= !FLAG_N; self.flags &= !FLAG_N;
cycles = 4;
} }
// ADC // ADC
@ -488,6 +553,7 @@ impl CPU {
self.regs[REG_A] = self.regs[REG_A].wrapping_add(1); self.regs[REG_A] = self.regs[REG_A].wrapping_add(1);
} }
self.flags &= !FLAG_N; self.flags &= !FLAG_N;
cycles = 4;
} }
// SUBs // SUBs
@ -502,6 +568,7 @@ impl CPU {
self.flags &= !FLAG_Z; self.flags &= !FLAG_Z;
} }
// TODO: H, C // TODO: H, C
cycles = 4;
} }
// SBC // SBC
@ -513,6 +580,7 @@ impl CPU {
self.regs[REG_A] = self.regs[REG_A].wrapping_sub(1); self.regs[REG_A] = self.regs[REG_A].wrapping_sub(1);
} }
self.flags |= FLAG_N; self.flags |= FLAG_N;
cycles = 4;
} }
// XOR // XOR
@ -528,6 +596,7 @@ impl CPU {
self.flags &= !FLAG_C; self.flags &= !FLAG_C;
self.flags &= !FLAG_N; self.flags &= !FLAG_N;
self.flags &= !FLAG_H; self.flags &= !FLAG_H;
cycles = 4;
}, },
// CP // CP
@ -545,66 +614,127 @@ impl CPU {
self.flags &= !FLAG_Z; self.flags &= !FLAG_Z;
self.flags &= !FLAG_C; self.flags &= !FLAG_C;
} }
cycles = 4;
}, },
0xC1 => { 0xC1 => {
println!("POP BC"); println!("POP BC");
let val: u16 = self.interconnect.read_word(self.sp); let val: u16 = self.interconnect.read_word(self.sp);
self.sp += 2; self.sp += 2;
self.set_pair_value(REG_B, REG_C, val); self.set_pair_value(REG_B, REG_C, val);
cycles = 12;
},
0xC2 => {
let addr: u16 = to_u16(self.load_args(2));
println!("JP NZ {:04X}", addr);
if self.flags & FLAG_Z == 0 {
self.ip = addr;
cycles = 16;
} else {
cycles = 12;
}
},
0xC3 => {
let addr: u16 = to_u16(self.load_args(2));
println!("JP {:04X}", addr);
self.ip = addr;
cycles = 16;
}
0xC4 => {
let target = to_u16(self.load_args(2));
println!("CALL NZ {:04X}", &target);
if self.flags & FLAG_Z == 0 {
// Push current IP to the stack
self.interconnect.write_word(self.sp - 1, self.ip);
self.sp -= 2;
self.ip = target;
cycles = 24;
} else {
cycles = 12;
}
}, },
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 - 2, val); self.interconnect.write_word(self.sp - 2, val);
self.sp -= 2; self.sp -= 2;
cycles = 16;
}, },
0xC9 => { 0xC9 => {
println!("RET"); println!("RET");
self.dump_stack(); self.dump_stack();
self.ip = self.interconnect.read_word(self.sp+1); self.ip = self.interconnect.read_word(self.sp+1);
self.sp += 2; self.sp += 2;
cycles = 16;
}, },
0xCB => { 0xCB => {
// Prefix CB. This is annoying. // Prefix CB. This is annoying.
self.run_prefix_instruction(); self.run_prefix_instruction();
cycles = 12; // TODO: Verify that this is the case for all prefix instructions.
},
0xCC => {
let target = to_u16(self.load_args(2));
println!("CALL Z {:04X}", &target);
if self.flags & FLAG_Z > 0 {
// Push current IP to the stack
self.interconnect.write_word(self.sp - 1, self.ip);
self.sp -= 2;
self.ip = target;
cycles = 24;
} else {
cycles = 12;
}
}, },
0xCD => { 0xCD => {
let target = to_u16(self.load_args(2)); let target = to_u16(self.load_args(2));
println!("CALL {:04X}", &target); println!("CALL {:04X}", &target);
// Push current IP to the stack // Push current IP to the stack
// self.interconnect.write_byte(self.sp, (self.ip & 0xFF) 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;
cycles = 24;
}, },
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]);
cycles = 12;
}, },
0xE2 => { 0xE2 => {
println!("LD (C), A"); println!("LD (C), A");
println!("[{:04X}] = {:02X}", 0xFF00 + self.regs[REG_C] as u16, self.regs[REG_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]);
cycles = 8;
}, },
0xEA => { 0xEA => {
let addr = to_u16(self.load_args(2)); let addr = to_u16(self.load_args(2));
println!("LD ({:04X}), A", addr); println!("LD ({:04X}), A", addr);
self.interconnect.write_byte(addr, self.regs[REG_A]); self.interconnect.write_byte(addr, self.regs[REG_A]);
cycles = 16;
} }
0xF0 => { 0xF0 => {
let args = self.load_args(1); let args = self.load_args(1);
println!("LDH A, {:02X}", args[0]); println!("LDH A, {:02X}", args[0]);
self.regs[REG_A] = self.interconnect.read_byte(0xFF00 + args[0] as u16); self.regs[REG_A] = self.interconnect.read_byte(0xFF00 + args[0] as u16);
cycles = 12;
}, },
0xF2 => {
println!("LD A, (C)");
self.regs[REG_A] = self.interconnect.read_byte(0xFF00 + self.regs[REG_C] as u16);
cycles = 8;
},
0xF3 => {
println!("DI");
self.interrupts_enabled = false;
cycles = 4;
}
0xFB => { 0xFB => {
// Enable interrupts - TODO // Enable interrupts - TODO
println!("EI"); println!("EI");
self.interconnect.write_byte(0xFFFF, 0x01); self.interrupts_enabled = true;
panic!("Uh uh"); // self.interconnect.write_byte(0xFFFF, 0x01);
// panic!("Uh uh");
cycles = 4;
panic!("ENABLING INTERRUPTS - TODO");
}, },
0xFE => { 0xFE => {
let args = self.load_args(1); let args = self.load_args(1);
@ -622,10 +752,11 @@ impl CPU {
} }
// TODO H // TODO H
cycles = 8;
} }
_ => panic!("Unknown instruction: {:02x}", instruction) _ => panic!("Unknown instruction: {:02x}", instruction)
} }
// self.dump_stack(); // self.dump_stack();
self.interconnect.tick(); self.interconnect.tick(cycles);
} }
} }

View File

@ -1,43 +1,159 @@
const VRAM_SIZE: usize = 0x2000;
const TICKS_END_SCANLINE: u16 = 80;
const TICKS_END_READMODE: u16 = 172;
const TICKS_END_HBLANK: u16 = 204;
const TICKS_END_VBLANK: u16 = 456;
#[derive(Debug)]
enum DisplayMode {
Scanline,
Readmode,
HBlank,
VBlank,
}
impl Default for DisplayMode {
fn default() -> DisplayMode {
DisplayMode::Scanline
}
}
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Display { pub struct Display {
control: u8, control: u8,
status: u8, status: u8,
background_palette: u8, background_palette: u8,
object_palette_0: u8,
object_palette_1: u8,
scrollx: u8, scrollx: u8,
scrolly: u8, scrolly: u8,
windowx: u8,
windowy: u8,
curline: u8, curline: u8,
vram: Box<[u8]>,
current_ticks: u16,
current_mode: DisplayMode,
// TODO // TODO
} }
impl Display { impl Display {
pub fn new() -> Display { pub fn new() -> Display {
Display::default() Display {
control: 0,
status: 0,
background_palette: 0,
object_palette_0: 0,
object_palette_1: 0,
scrollx: 0,
scrolly: 0,
windowx: 0,
windowy: 0,
curline: 0,
current_ticks: 0,
current_mode: DisplayMode::default(),
vram: vec![0; VRAM_SIZE].into_boxed_slice(),
}
} }
pub fn write_byte(&mut self, addr: u16, val: u8) { pub fn write_byte(&mut self, addr: u16, val: u8) {
match addr { match addr {
0x8000 ... 0x9FFF => self.vram[(addr - 0x8000) as usize] = val,
0xFF40 => self.control = val, 0xFF40 => self.control = val,
0xFF41 => self.status = val, 0xFF41 => self.status = val,
0xFF42 => self.scrolly = val, 0xFF42 => self.scrolly = val,
0xFF43 => self.scrollx = val, 0xFF43 => self.scrollx = val,
0xFF44 => self.curline = 0, 0xFF44 => self.curline = 0,
0xFF47 => self.background_palette = val, 0xFF47 => self.background_palette = val,
0xFF48 => self.object_palette_0 = val,
0xFF49 => self.object_palette_1 = val,
0xFF4A => self.windowy = val,
0xFF4B => self.windowx = val,
_ => panic!("Display: Write {:02X} to {:04X} unsupported", val, addr), _ => panic!("Display: Write {:02X} to {:04X} unsupported", val, addr),
} }
} }
pub fn read_byte(&self, addr: u16) -> u8 { pub fn read_byte(&self, addr: u16) -> u8 {
match addr { match addr {
0x8000 ... 0x9FFF => self.vram[(addr - 0x8000) as usize],
0xFF40 => self.control, 0xFF40 => self.control,
0xFF41 => self.status, 0xFF41 => self.status,
0xFF42 => self.scrolly, 0xFF42 => self.scrolly,
0xFF43 => self.scrollx, 0xFF43 => self.scrollx,
0xFF44 => self.curline, 0xFF44 => self.curline,
0xFF47 => self.background_palette, 0xFF47 => self.background_palette,
0xFF48 => self.object_palette_0,
0xFF49 => self.object_palette_1,
0xFF4A => self.windowy,
0xFF4B => self.windowx,
_ => panic!("Display: Read from {:04X} unsupported", addr), _ => panic!("Display: Read from {:04X} unsupported", addr),
} }
} }
// Do we want to have a time delta here?
pub fn tick(&mut self, ticks: u16) {
self.current_ticks += ticks;
match self.current_mode {
DisplayMode::Scanline => {
if self.current_ticks > TICKS_END_SCANLINE {
self.current_ticks = 0;
self.current_mode = DisplayMode::Readmode;
}
},
DisplayMode::Readmode => {
if self.current_ticks > TICKS_END_READMODE {
self.current_ticks = 0;
self.current_mode = DisplayMode::HBlank;
}
},
DisplayMode::HBlank => {
if self.current_ticks > TICKS_END_HBLANK {
self.current_ticks = 0;
self.curline += 1;
if self.curline == 143 {
self.current_mode = DisplayMode::VBlank;
} else {
self.current_mode = DisplayMode::Scanline;
}
}
},
DisplayMode::VBlank => {
if self.current_ticks > TICKS_END_VBLANK {
self.current_ticks = 0;
self.curline += 1;
if self.curline > 153 {
self.current_mode = DisplayMode::Scanline;
self.curline = 0;
}
}
}
}
}
fn renderscan(&mut self) {
// TODO: Allow switching of tile map
let tilemap: u16 = 0x1800;
let map_offset_y: u8 = self.curline.wrapping_add(self.scrolly);
let map_offset_x: u8 = self.scrollx;
let tile_index_y: u8 = map_offset_y / 8;
// Render line
for render_x in 0 .. 159 {
let tile_index_x: u8 = render_x / 8;
let tile_offset_x: u8 = render_x % 8;
// TODO: Draw bit
// let tile_base_addr = tilemap + tile_id*128
// pixel(render_x, map_offset_y) := *tile_base_addr + 2* *(tile_base_addr+1)
//[tile_index_x][tile_index_y]
}
}
pub fn vblank_interrupt(&mut self) { pub fn vblank_interrupt(&mut self) {
self.curline += 1; self.curline += 1;
if self.curline > 153 { if self.curline > 153 {

View File

@ -1,5 +1,4 @@
const RAM_SIZE: usize = 0x2000; const RAM_SIZE: usize = 0x2000;
const VRAM_SIZE: usize = 0x2000;
const HIRAM_SIZE: usize = (0xFFFE - 0xFF80) + 1; const HIRAM_SIZE: usize = (0xFFFE - 0xFF80) + 1;
const INTERRUPT_DISPLAY: u8 = 1 << 1; const INTERRUPT_DISPLAY: u8 = 1 << 1;
@ -7,18 +6,23 @@ const INTERRUPT_DISPLAY_VBLANK: u8 = 1 << 0;
use super::display; use super::display;
use super::sound; use super::sound;
use super::timer;
use super::serial;
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]>,
hiram: Box<[u8]>, hiram: Box<[u8]>,
sound: sound::Sound, sound: sound::Sound,
display: display::Display, display: display::Display,
interrupt: u8, interrupt: u8,
timer: u8, iflags: u8,
itimer: u8,
disable_bootrom: u8, disable_bootrom: u8,
infrared_com_port: u8,
serial: serial::Serial,
timer: timer::Timer,
} }
impl Interconnect { impl Interconnect {
@ -27,23 +31,30 @@ impl Interconnect {
bios: bios, bios: bios,
rom: rom, rom: rom,
ram: vec![0; RAM_SIZE].into_boxed_slice(), ram: vec![0; RAM_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(), sound: sound::Sound::new(),
display: display::Display::new(), display: display::Display::new(),
// Refactor those
iflags: 0,
interrupt: 0, interrupt: 0,
timer: 0, infrared_com_port: 0,
itimer: 0,
disable_bootrom: 0, disable_bootrom: 0,
timer: timer::Timer::new(),
serial: serial::Serial::new(),
} }
} }
// Somehow we need different timers for this. // Somehow we need different timers for this.
pub fn tick(&mut self) { pub fn tick(&mut self, cycles: u16) {
self.timer += 1; /*
if self.timer == 5 { self.itimer += 1;
if self.itimer == 5 {
self.display_blank_interrupt(); self.display_blank_interrupt();
self.timer = 0; self.itimer = 0;
} }
*/
self.display.tick(cycles * 4);
} }
pub fn display_blank_interrupt(&mut self) { pub fn display_blank_interrupt(&mut self) {
@ -71,12 +82,15 @@ impl Interconnect {
self.rom[addr as usize] self.rom[addr as usize]
} }
} }
0x8000 ... 0x9FFF => { 0x8000 ... 0x9FFF => self.display.read_byte(addr),
self.vram[(addr - 0x8000) as usize]
},
0xC000 ... 0xDFFF => { 0xC000 ... 0xDFFF => {
self.ram[(addr - 0xC000) as usize] self.ram[(addr - 0xC000) as usize]
}, },
0xFF01 ... 0xFF02 => self.serial.read_byte(addr),
0xFF04 ... 0xFF07 => self.timer.read_byte(addr),
0xFF0F => {
self.iflags
},
0xFF10 ... 0xFF26 => { 0xFF10 ... 0xFF26 => {
self.sound.read_byte(addr) self.sound.read_byte(addr)
}, },
@ -86,6 +100,9 @@ impl Interconnect {
0xFF50 => { 0xFF50 => {
self.disable_bootrom self.disable_bootrom
}, },
0xFF56 => {
self.infrared_com_port
}
0xFF80 ... 0xFFFE => { 0xFF80 ... 0xFFFE => {
self.hiram[(addr - 0xFF80) as usize] self.hiram[(addr - 0xFF80) as usize]
}, },
@ -100,7 +117,6 @@ impl Interconnect {
pub fn write_byte(&mut self, addr: u16, val: u8) { pub fn write_byte(&mut self, addr: u16, val: u8) {
// TODO: Make this more beautful // TODO: Make this more beautful
// TODO: Write byte
/* /*
0000 7FFF Cartridge 0000 7FFF Cartridge
8000 9FFF Video RAM 8000 9FFF Video RAM
@ -115,12 +131,15 @@ impl Interconnect {
*/ */
match addr { match addr {
0x8000 ... 0x9FFF => { 0x8000 ... 0x9FFF => self.display.write_byte(addr, val),
self.vram[(addr - 0x8000) as usize] = val;
},
0xC000 ... 0xDFFF => { 0xC000 ... 0xDFFF => {
self.ram[(addr - 0xC000) as usize] = val; self.ram[(addr - 0xC000) as usize] = val;
}, },
0xFF01 ... 0xFF02 => self.serial.write_byte(addr, val),
0xFF04 ... 0xFF07 => self.timer.write_byte(addr, val),
0xFF0F => {
self.iflags = val;
}
0xFF10 ... 0xFF26 => { 0xFF10 ... 0xFF26 => {
self.sound.write_byte(addr, val); self.sound.write_byte(addr, val);
}, },
@ -129,7 +148,10 @@ impl Interconnect {
}, },
0xFF50 => { 0xFF50 => {
self.disable_bootrom = val; self.disable_bootrom = val;
} },
0xFF56 => {
self.infrared_com_port = val;
},
0xFF80 ... 0xFFFE => { 0xFF80 ... 0xFFFE => {
self.hiram[(addr - 0xFF80) as usize] = val; self.hiram[(addr - 0xFF80) as usize] = val;
}, },

View File

@ -10,7 +10,8 @@ mod cpu;
mod display; mod display;
mod interconnect; mod interconnect;
mod sound; mod sound;
mod timer;
mod serial;
fn main() { fn main() {
let bios_path = env::args().nth(1).unwrap(); let bios_path = env::args().nth(1).unwrap();

27
src/serial.rs Normal file
View File

@ -0,0 +1,27 @@
#[derive(Default, Debug)]
pub struct Serial {
data: u8,
control: u8,
}
impl Serial {
pub fn new() -> Serial {
Serial::default()
}
pub fn write_byte(&mut self, addr: u16, val: u8) {
match addr {
0xFF01 => self.data = val,
0xFF02 => self.control = val,
_ => panic!("Serial: Write {:02X} to {:04X} unsupported", val, addr),
}
}
pub fn read_byte(&self, addr: u16) -> u8 {
match addr {
0xFF01 => self.data,
0xFF02 => self.control,
_ => panic!("Serial: Read from {:04X} unsupported", addr),
}
}
}

30
src/timer.rs Normal file
View File

@ -0,0 +1,30 @@
#[derive(Default, Debug)]
pub struct Timer {
tima: u8,
tma: u8,
tac: u8
}
impl Timer {
pub fn new() -> Timer {
Timer::default()
}
pub fn write_byte(&mut self, addr: u16, val: u8) {
match addr {
0xFF05 => self.tima = val,
0xFF06 => self.tma = val,
0xFF07 => self.tac = val,
_ => panic!("Timer: Write {:02X} to {:04X} unsupported", val, addr),
}
}
pub fn read_byte(&self, addr: u16) -> u8 {
match addr {
0xFF05 => self.tima,
0xFF06 => self.tma,
0xFF07 => self.tac,
_ => panic!("Timer: Read from {:04X} unsupported", addr),
}
}
}