diff --git a/src/cartridge.rs b/src/cartridge.rs new file mode 100644 index 0000000..504459d --- /dev/null +++ b/src/cartridge.rs @@ -0,0 +1,151 @@ +#[derive(Debug,PartialEq)] +enum MemoryBankControllerType { + None, + MBC1, + MBC2, + MBC3, + HuC1, +} + +#[derive(Debug)] +enum RamSize { + None, + Ram2KB, + Ram8KB, + Ram32KB, +} + +pub struct Cartridge { + rom: Box<[u8]>, + ram: Box<[u8]>, + bank_no: u8, + mbc_type: MemoryBankControllerType, + ram_rtc_enabled: bool, + ram_bank_no: u8, + + ram_size: RamSize, + rom_banks: u16, +} + +impl Cartridge { + pub fn new(rom: Box<[u8]>) -> Cartridge { + let mbc: MemoryBankControllerType = match rom[0x0147] { + 0x00 | 0x08 ... 0x09 => MemoryBankControllerType::None, + 0x01 ... 0x03 => MemoryBankControllerType::MBC1, + 0x05 ... 0x06 => MemoryBankControllerType::MBC2, + 0x0F ... 0x13 => MemoryBankControllerType::MBC3, + 0xFF => MemoryBankControllerType::HuC1, + _ => panic!("Unsupported MBC type: {:02X}", rom[0x0147]), + }; + + println!("MBC Type: {:?}", &mbc); + + let rom_banks: u16 = match rom[0x0148] { + 0x00 => 0, + 0x01 ... 0x07 => 2u16.pow(rom[0x0148] as u32 + 1), + 0x52 => 72, + 0x53 => 80, + 0x54 => 96, + _ => panic!("Unknown rom size: {:?}", rom[0x148]) + }; + + let ram_size: RamSize = match rom[0x0149] { + 0x00 => RamSize::None, + 0x01 => RamSize::Ram2KB, + 0x02 => RamSize::Ram8KB, + 0x03 => RamSize::Ram32KB, + _ => panic!("Unknown ram size: {:?}", rom[0x149]) + }; + + println!("Rom size: {} banks", rom_banks); + println!("Ram size: {:?}", ram_size); + + let ram = match ram_size { + RamSize::None => vec![0u8; 0].into_boxed_slice(), + RamSize::Ram2KB => vec![0u8; 2048].into_boxed_slice(), + RamSize::Ram8KB => vec![0u8; 4 * 2048].into_boxed_slice(), + RamSize::Ram32KB => vec![0u8; 16 * 2048].into_boxed_slice(), + }; + + Cartridge { + rom: rom, + mbc_type: mbc, + bank_no: 1, + + ram_rtc_enabled: false, + ram_bank_no: 0, + ram_size: ram_size, + rom_banks: rom_banks, + + ram: ram + } + } + + pub fn read_byte(&self, addr: u16) -> u8 { + match addr { + 0x0000 ... 0x3FFF => self.rom[addr as usize], + 0x4000 ... 0x7FFF => { + let abs_addr: usize = addr as usize + self.bank_no as usize * 0x4000; + let val: u8 = self.rom[abs_addr]; + // println!("Access {:04X}{:04X} = {:02X}", self.bank_no, addr, val); + val + }, + 0xA000 ... 0xBFFF => { + // TODO: Safty checks? Or let rust handle this? + let addr = addr - 0xA000; + if self.ram_bank_no < 4 { + self.ram[self.ram_bank_no as usize * 0x2000 + addr as usize] + } else { + println!("Ignoring RTC read"); + 0 + } + } + _ => { + panic!("Cartride: Unable to read from {:04X}", addr); + } + } + } + + fn write_byte_mbc3(&mut self, addr: u16, val: u8) { + match addr { + 0x0000 ... 0x1FFF => { + match val { + 0x0A => self.ram_rtc_enabled = true, + 0x00 => self.ram_rtc_enabled = false, + _ => panic!("Unknown MBC value {:02X} for {:04X}", val, addr) + } + }, + 0x2000 ... 0x3FFF => self.bank_no = val & 0x7F, + 0x4000 ... 0x5FFF => { + // RAM bank select + match val { + 0x00 ... 0x03 => self.ram_bank_no = val, + 0x08 ... 0x0C => self.ram_bank_no = val, // RTC clock values, TODO + _ => panic!("Unknown MBC3 RAM BANK NO: {:02X}", val) + } + + }, + 0x6000 ... 0x7FFF => { + // Latch clock data, ignore. + }, + 0xA000 ... 0xBFFF => { + // TODO: Safty checks? Or let rust handle this? + let addr = addr - 0xA000; + if self.ram_bank_no < 4 { + self.ram[self.ram_bank_no as usize * 0x2000 + addr as usize] = val; + } else { + println!("Ignoring RTC write"); + } + } + _ => panic!("MBC3: Writing {:02X} to {:04X} not supported", val, addr), + } + } + + pub fn write_byte(&mut self, addr: u16, val: u8) { + match self.mbc_type { + MemoryBankControllerType::None => panic!("Cartridge: No MBC found, can not write to ROM"), + MemoryBankControllerType::MBC3 => self.write_byte_mbc3(addr, val), + _ => panic!("MBC not supported.") + } + } +} diff --git a/src/cpu.rs b/src/cpu.rs index ee00266..f089a19 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -16,7 +16,8 @@ const REG_N_H: usize = 4; const REG_N_L: usize = 5; const REG_N_HL: usize = 6; const REG_N_A: usize = 7; -const REG_NAMES: [&'static str; 8] = ["B", "C", "D", "E", "H", "L", "(HL)", "A"]; +const REG_N_F: usize = 8; +const REG_NAMES: [&'static str; 9] = ["B", "C", "D", "E", "H", "L", "(HL)", "A", "F"]; const FLAG_Z: u8 = 1 << 7; const FLAG_N: u8 = 1 << 6; @@ -33,7 +34,7 @@ pub struct CPU { interconnect: interconnect::Interconnect, - interrupts_enabled: bool, + ime: bool, debug: bool, } @@ -49,7 +50,7 @@ impl CPU { ip: 0, sp: 0xFFFE, interconnect: interconnect, - interrupts_enabled: false, // Is this correct? + ime: false, // Is this correct? debug: false, } } @@ -69,9 +70,11 @@ impl CPU { fn set_8bit_reg(&mut self, reg_id: usize, value: u8) { // Make sure that we skip the (HL) part. - if reg_id == 7 { + if reg_id == REG_N_A { self.regs[REG_A] = value; - } else if reg_id == 6 { + } else if reg_id == REG_N_F { + self.flags = value; + } else if reg_id == REG_N_HL { let addr: u16 = self.get_pair_value(REG_H, REG_L); self.interconnect.write_byte(addr, value); } else { @@ -81,9 +84,11 @@ impl CPU { fn get_8bit_reg(&self, reg_id: usize) -> u8 { // Make sure that we skip the (HL) part. - if reg_id == 7 { + if reg_id == REG_N_A { self.regs[REG_A] - } else if reg_id == 6 { + } else if reg_id == REG_N_F { + self.flags + } else if reg_id == REG_N_HL { let addr: u16 = self.get_pair_value(REG_H, REG_L); self.interconnect.read_byte(addr) } else { @@ -120,6 +125,14 @@ impl CPU { self.clear_flag(FLAG_N); self.clear_flag(FLAG_H); }, + 0x30 ... 0x37 => { + let reg_id = (instruction - 0x30) as usize; + if self.debug { + println!("SWAP {}", REG_NAMES[reg_id]); + } + let v: u8 = self.get_8bit_reg(reg_id); + self.set_8bit_reg(reg_id, v << 4 | v >> 4); + } 0x40 ... 0x47 => { // Test 0th bit let reg_id = (instruction - 0x40) as usize; @@ -225,7 +238,7 @@ impl CPU { } } _ => { - println!("Unsupported prefix instruction: {:x}", instruction); + panic!("Unsupported prefix instruction: {:x}", instruction); } } } @@ -245,12 +258,26 @@ impl CPU { } } + fn call(&mut self, dst: u16) { + let ip = self.ip; + self.push(ip); + self.ip = dst; + } + + fn rst(&mut self, val: u8) -> u8 { + // Make sure this is correct. + if self.debug { + println!("RST {:02X}", val); + } + self.call(val as u16); + 16 + } + fn pop_rr(&mut self, r1: usize, r2: usize) -> u8 { if self.debug { println!("POP {}{}", REG_NAMES[r1], REG_NAMES[r2]); } - let val: u16 = self.interconnect.read_word(self.sp); - self.sp += 2; + let val: u16 = self.pop(); self.set_pair_value(r1, r2, val); 12 } @@ -284,10 +311,16 @@ impl CPU { } fn push(&mut self, val: u16) { - self.interconnect.write_word(self.sp - 2, val); + self.interconnect.write_word(self.sp - 1, val); self.sp -= 2; } + fn pop(&mut self) -> u16 { + let v: u16 = self.interconnect.read_word(self.sp + 1); + self.sp += 2; + v + } + fn ld_r_r(&mut self, reg_dst: usize, reg_src: usize) -> u8 { if self.debug { println!("LD {}, {}", REG_NAMES[reg_dst], REG_NAMES[reg_src]) @@ -336,6 +369,13 @@ impl CPU { 4 } + fn add_rr_rr(&mut self, r1: usize, r2: usize, r3: usize, r4: usize) -> u8 { + let val1 = self.get_pair_value(r1, r2); + let val2 = self.get_pair_value(r3, r4); + self.set_pair_value(r1, r2, val1.wrapping_add(val2)); + 8 + } + fn reg_dec(&mut self, reg_id: usize) -> u8 { if self.debug { println!("DEC {}", REG_NAMES[reg_id]); @@ -369,7 +409,50 @@ impl CPU { self.flags &= !flag; } + fn int_(&mut self, val: u8){ + if self.debug { + println!("INT {:02X}", val); + } + // TODO: Clear interrupt register + self.ime = false; + self.call(val as u16); + } + + fn ret(&mut self) { + let new_ip: u16 = self.pop(); + self.ip = new_ip; + } + + fn reti(&mut self) -> u8 { + self.ret(); + self.ime = true; + 16 + } + pub fn run_instruction(&mut self) { + // self.debug = !self.interconnect.is_boot_rom(); + // Check for interrupts. + // TODO: Make this right + if self.ime { + // read pending interrupts + let pending = self.interconnect.read_byte(0xFF0F); + let enabled = self.interconnect.read_byte(0xFFFF); + let e_pending = pending & enabled; + + if e_pending & interconnect::INTERRUPT_DISPLAY_VBLANK > 0 { + println!("Handling vblank interrupt"); + self.interconnect.write_byte(0xFF0F, pending & !interconnect::INTERRUPT_DISPLAY_VBLANK); + self.int_(0x40); + return; + } else if e_pending & interconnect::INTERRUPT_DISPLAY_STAT > 0 { + println!("Handling display stat interrupt"); + self.interconnect.write_byte(0xFF0F, pending & !interconnect::INTERRUPT_DISPLAY_STAT); + self.int_(0x48); + return; + } else if e_pending > 0 { + panic!("Unknown pending interrupt: {:02X}", e_pending); + } + } // We need to double-check the flags let instruction = self.read_byte(self.ip); if self.debug { @@ -386,7 +469,8 @@ impl CPU { print!("H={} ", self.flags & FLAG_H != 0); print!("C={} ", self.flags & FLAG_C != 0); } - self.ip += 1; + + self.ip = self.ip.wrapping_add(1); let cycles: u8 = match instruction { 0x00 => { @@ -400,6 +484,16 @@ impl CPU { 0x04 => self.reg_inc(REG_N_B), 0x05 => self.reg_dec(REG_N_B), 0x06 => self.ld_r_v(REG_N_B), + + 0x08 => { + let a: u16 = to_u16(self.load_args(2)); + if self.debug{ + println!("LD ({:04X}), sp", a); + } + self.interconnect.write_word(a, self.sp); + 20 + } + 0x09 => self.add_rr_rr(REG_N_H, REG_N_L, REG_N_B, REG_N_C), 0x0B => self.dec_rr(REG_N_B, REG_N_C), 0x0C => self.reg_inc(REG_N_C), 0x0D => self.reg_dec(REG_N_C), @@ -445,6 +539,7 @@ impl CPU { } 12 }, + 0x19 => self.add_rr_rr(REG_N_H, REG_N_L, REG_N_D, REG_N_E), 0x1A => { if self.debug { println!("LD A, (DE)"); @@ -505,6 +600,7 @@ impl CPU { 8 } }, + 0x29 => self.add_rr_rr(REG_N_H, REG_N_L, REG_N_H, REG_N_L), 0x2A => { if self.debug { println!("LD A, (HL+)"); @@ -524,6 +620,23 @@ impl CPU { self.regs[REG_A] = !self.regs[REG_A]; 4 }, + 0x30 => { + let args = self.load_args(1); + if self.debug { + println!("JR NC {:02x}", args[0] as i8); + } + if self.flags & FLAG_C == 0 { + let offset = args[0] as i8; + if offset < 0 { + self.ip -= (-offset) as u16 + } else { + self.ip += offset as u16; + } + 12 + } else { + 8 + } + }, 0x31 => { let args = self.load_args(2); self.sp = to_u16(args); @@ -552,6 +665,39 @@ impl CPU { self.interconnect.write_byte(addr, args[0]); 12 }, + 0x37 => { + if self.debug { + println!("SCF"); + } + self.set_flag(FLAG_C); + 4 + }, + 0x38 => { + let args = self.load_args(1); + if self.debug { + println!("JR C {:02x}", args[0] as i8); + } + if self.flags & FLAG_C > 0 { + let offset = args[0] as i8; + if offset < 0 { + self.ip -= (-offset) as u16 + } else { + self.ip += offset as u16; + } + 12 + } else { + 8 + } + }, + 0x39 => { + if self.debug { + println!("ADD HL, SP"); + } + let sp = self.sp; + let v = self.get_pair_value(REG_N_H, REG_N_L).wrapping_add(sp); + self.set_pair_value(REG_N_H, REG_N_L, v); + 8 + } 0x3C => self.reg_inc(REG_N_A), 0x3D => self.reg_dec(REG_N_A), 0x3E => self.ld_r_v(REG_N_A), @@ -706,6 +852,17 @@ impl CPU { } 4 }, + 0xC0 => { + if self.debug { + println!("RET NZ"); + } + if self.flags & FLAG_Z == 0 { + self.ret(); + 20 + } else { + 8 + } + }, 0xC1 => self.pop_rr(REG_B, REG_C), 0xC2 => { let addr: u16 = to_u16(self.load_args(2)); @@ -733,10 +890,7 @@ impl CPU { println!("CALL NZ {:04X}", &target); } if self.flags & FLAG_Z == 0 { - // Push current IP to the stack - let ip = self.ip; - self.push(ip); - self.ip = target; + self.call(target); 24 } else { 12 @@ -751,13 +905,24 @@ impl CPU { self.regs[REG_A] = self.regs[REG_A].wrapping_add(val); 8 }, + 0xC7 => self.rst(0x00), + 0xC8 => { + if self.debug { + println!("RET Z"); + } + if self.flags & FLAG_Z > 0 { + self.ret(); + 20 + } else { + 8 + } + }, 0xC9 => { if self.debug { println!("RET"); - self.dump_stack(); } - self.ip = self.interconnect.read_word(self.sp+1); - self.sp += 2; + let new_ip: u16 = self.pop(); + self.ip = new_ip; 16 }, 0xCB => { @@ -771,10 +936,7 @@ impl CPU { 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; + self.call(target); 24 } else { 12 @@ -785,12 +947,12 @@ impl CPU { if self.debug { println!("CALL {:04X}", &target); } - // Push current IP to the stack - self.interconnect.write_word(self.sp - 1, self.ip); - self.sp -= 2; - self.ip = target; + self.call(target); 24 }, + 0xCF => self.rst(0x08), + 0xD1 => self.pop_rr(REG_D, REG_E), + 0xD5 => self.push_rr(REG_D, REG_E), 0xD6 => { let val = self.load_args(1)[0]; if self.debug { @@ -799,6 +961,8 @@ impl CPU { self.regs[REG_A] = self.regs[REG_A].wrapping_sub(val); 8 }, + 0xD7 => self.rst(0x10), + 0xDF => self.rst(0x18), 0xE0 => { let args = self.load_args(1); if self.debug { @@ -816,6 +980,7 @@ impl CPU { self.interconnect.write_byte(0xFF00 + self.regs[REG_C] as u16, self.regs[REG_A]); 8 }, + 0xE5 => self.push_rr(REG_H, REG_L), 0xE6 => { let val = self.load_args(1)[0]; if self.debug { @@ -824,6 +989,15 @@ impl CPU { self.regs[REG_A] &= val; 8 }, + 0xE7 => self.rst(0x20), + 0xE9 => { + if self.debug { + println!("JP (HL)"); + } + let new_ip = (self.ip as i16 + self.get_8bit_reg(REG_N_HL) as i16) as u16; + self.ip = new_ip; + 4 + }, 0xEA => { let addr = to_u16(self.load_args(2)); if self.debug{ @@ -832,16 +1006,7 @@ impl CPU { self.interconnect.write_byte(addr, self.regs[REG_A]); 16 }, - 0xEF => { - // Make sure this is correct. - if self.debug { - println!("RST 0x28"); - } - self.interconnect.write_word(self.sp - 1, self.ip); - self.sp -= 2; - self.ip = 0x28; - 16 - }, + 0xEF => self.rst(0x28), 0xF0 => { let args = self.load_args(1); if self.debug { @@ -850,6 +1015,7 @@ impl CPU { self.regs[REG_A] = self.interconnect.read_byte(0xFF00 + args[0] as u16); 12 }, + 0xF1 => self.pop_rr(REG_N_A, REG_N_F), 0xF2 => { if self.debug{ println!("LD A, (C)"); @@ -861,9 +1027,10 @@ impl CPU { if self.debug { println!("DI"); } - self.interrupts_enabled = false; + self.ime = false; 4 }, + 0xF5 => self.push_rr(REG_N_A, REG_N_F), 0xF6 => { let val = self.load_args(1)[0]; if self.debug { @@ -872,15 +1039,24 @@ impl CPU { self.regs[REG_A] |= val; 8 }, + 0xF7 => self.rst(0x30), 0xFB => { // Enable interrupts - TODO if self.debug { println!("EI"); } - self.interrupts_enabled = true; - println!("ENABLING INTERRUPTS - TODO"); + self.ime = true; // interrupt master enable 4 }, + 0xFA => { + let addr: u16 = to_u16(self.load_args(2)); + if self.debug { + println!("LD A, ({:04X})", addr); + } + let val: u8 = self.interconnect.read_byte(addr); + self.set_8bit_reg(REG_N_A, val); + 16 + }, 0xFE => { let args = self.load_args(1); if self.debug { @@ -900,7 +1076,8 @@ impl CPU { // TODO H 8 - } + }, + 0xFF => self.rst(0x38), _ => panic!("Unknown instruction: {:02x}", instruction) }; // self.dump_stack(); diff --git a/src/display.rs b/src/display.rs index c0c0f25..ffd1ab3 100644 --- a/src/display.rs +++ b/src/display.rs @@ -4,6 +4,9 @@ extern crate libc; // Internal ram size const VRAM_SIZE: usize = 0x2000; +// OAM size +const OAM_SIZE: usize = (0xFE9F - 0xFE00) + 1; + // Timing values const TICKS_END_SCANLINE: u16 = 80; const TICKS_END_READMODE: u16 = 172; @@ -50,14 +53,21 @@ pub struct Display { windowx: u8, windowy: u8, curline: u8, + lyc: u8, vram: Box<[u8]>, + oam: Box<[u8]>, current_ticks: u16, current_mode: DisplayMode, // TODO renderer: sdl2::render::Renderer<'static>, + + vblank_fired: bool, + stat_fired: bool, + vblank_interrupt: bool, + stat_interrupt: bool, } impl Display { @@ -78,11 +88,18 @@ impl Display { windowx: 0, windowy: 0, curline: 0, + lyc: 255, current_ticks: 0, current_mode: DisplayMode::default(), vram: vec![0; VRAM_SIZE].into_boxed_slice(), - renderer: renderer + oam: vec![0; OAM_SIZE].into_boxed_slice(), + renderer: renderer, + + vblank_fired: false, + stat_fired: false, + vblank_interrupt: false, + stat_interrupt: false, } } @@ -91,17 +108,42 @@ impl Display { self.renderer.fill_rect(sdl2::rect::Rect::new((x as i32) * SCALE as i32, (y as i32) * SCALE as i32, SCALE as u32, SCALE as u32)); } + pub fn vblank_interrupt(&mut self) -> bool { + // Returns whether or not a vblank interrupt should be done + // Yes, this is polling, and yes, this sucks.\ + if self.vblank_interrupt { + self.vblank_interrupt = false; + true + } else { + false + } + } + + pub fn stat_interrupt(&mut self) -> bool { + if self.stat_interrupt { + self.stat_interrupt = false; + true + } else { + false + } + } + pub fn write_byte(&mut self, addr: u16, val: u8) { match addr { 0x8000 ... 0x9FFF => { - println!("VRAM: Write {:02X} to {:04X}", val, addr); + // println!("VRAM: Write {:02X} to {:04X}", val, addr); self.vram[(addr - 0x8000) as usize] = val; } + 0xFE00 ... 0xFE9F => { + println!("OAM: Write {:02X} to {:04X}", val, addr); + self.oam[(addr - 0xFE00) as usize] = val; + } 0xFF40 => self.control = val, 0xFF41 => self.status = val, 0xFF42 => self.scrolly = val, 0xFF43 => self.scrollx = val, 0xFF44 => self.curline = 0, + 0xFF45 => self.lyc = val, 0xFF47 => self.background_palette = val, 0xFF48 => self.object_palette_0 = val, 0xFF49 => self.object_palette_1 = val, @@ -114,11 +156,13 @@ impl Display { pub fn read_byte(&self, addr: u16) -> u8 { match addr { 0x8000 ... 0x9FFF => self.vram[(addr - 0x8000) as usize], + 0xFE00 ... 0xFE9F => self.oam[(addr - 0xFE00) as usize], 0xFF40 => self.control, 0xFF41 => self.status, 0xFF42 => self.scrolly, 0xFF43 => self.scrollx, 0xFF44 => self.curline, + 0xFF45 => self.lyc, 0xFF47 => self.background_palette, 0xFF48 => self.object_palette_0, 0xFF49 => self.object_palette_1, @@ -131,9 +175,15 @@ impl Display { // Do we want to have a time delta here? pub fn tick(&mut self, ticks: u16) { self.current_ticks += ticks; + if self.curline == self.lyc { + self.stat_fired = true; + self.stat_interrupt = true; + } match self.current_mode { DisplayMode::Scanline => { if self.current_ticks > TICKS_END_SCANLINE { + self.vblank_fired = false; + self.stat_fired = false; self.current_ticks = 0; self.current_mode = DisplayMode::Readmode; } @@ -151,6 +201,8 @@ impl Display { self.curline += 1; if self.curline == 143 { self.current_mode = DisplayMode::VBlank; + self.vblank_interrupt = true; + self.vblank_fired = true; // We don't need this, do we? } else { self.current_mode = DisplayMode::Scanline; } @@ -257,17 +309,50 @@ impl Display { self.set_pixel(render_x, render_y, sdl2::pixels::Color::RGB(factor, factor, factor)); } + + if self.control & CTRL_BG_SPRITE_ENABLE > 0 { + // panic!("Sprites not supported"); + // Let's draw sprites. + // TODO: Sprites with smaller X coordinate should + // should be in front + // TODO: Draw only up to 10 sprites per line + for i in 0 .. 39 { + let y: u8 = self.oam[i * 4 + 0]; + let x: u8 = self.oam[i * 4 + 1]; + let t_num: u8 = self.oam[i * 4 + 2]; + let flags: u8 = self.oam[i * 4 + 3]; + + + // Is this sprite on the current line? + if (y + 8) >= render_y && y <= render_y { + let tile_offset_y: usize = render_y as usize - y as usize; + let tile_base_addr: usize = 0x1000 + t_num as usize * 16; + + let tile_line_1 = self.vram[tile_base_addr + tile_offset_y * 2]; + let tile_line_2 = self.vram[tile_base_addr + tile_offset_y * 2 + 1]; + + // We need to draw this. + for x_o in 0 .. 8 { + let b1: bool = (tile_line_1 & 1 << (7 - x_o)) > 0; + let b2: bool = (tile_line_2 & 1 << (7 - x_o)) > 0; + + let mut factor = 0; + if b1 { + factor += 64; + } + if b2 { + factor += 128; + } + + // Draw stuff. We're currently only in monochrome mode + self.set_pixel(x + x_o, render_y, sdl2::pixels::Color::RGB(factor, factor, factor)); + + } + } + } + } + } } - pub fn vblank_interrupt(&mut self) { - self.curline += 1; - if self.curline > 153 { - self.curline = 0; - } - } - - pub fn controller_interrupt(&self) { - - } } diff --git a/src/interconnect.rs b/src/interconnect.rs index 263d58c..95e0ba1 100644 --- a/src/interconnect.rs +++ b/src/interconnect.rs @@ -1,88 +1,77 @@ const RAM_SIZE: usize = 0x2000; const HIRAM_SIZE: usize = (0xFFFE - 0xFF80) + 1; -const INTERRUPT_DISPLAY: u8 = 1 << 1; -const INTERRUPT_DISPLAY_VBLANK: u8 = 1 << 0; +pub const INTERRUPT_DISPLAY_STAT: u8 = 1 << 1; +pub const INTERRUPT_DISPLAY_VBLANK: u8 = 1 << 0; use super::display; use super::sound; use super::timer; use super::serial; - -#[derive(Debug)] -enum MemoryBankControllerType { - None, - MBC1, - MBC2, - MBC3, - HuC1, -} +use super::cartridge; pub struct Interconnect { bios: Box<[u8]>, - rom: Box<[u8]>, + cartridge: cartridge::Cartridge, ram: Box<[u8]>, hiram: Box<[u8]>, sound: sound::Sound, display: display::Display, interrupt: u8, - iflags: u8, + interrupt_request_flags: u8, disable_bootrom: u8, infrared_com_port: u8, serial: serial::Serial, timer: timer::Timer, - bank_no: u16, - mbc_type: MemoryBankControllerType, } impl Interconnect { pub fn new(bios: Box<[u8]>, rom: Box<[u8]>) -> Interconnect { - let mbc: MemoryBankControllerType = match rom[0x0147] { - 0x00 | 0x08 ... 0x09 => MemoryBankControllerType::None, -// 0x01 ... 0x03 => MemoryBankControllerType::MBC1, -// 0x05 ... 0x06 => MemoryBankControllerType::MBC2, -// 0x0F ... 0x13 => MemoryBankControllerType::MBC3, -// 0xFF => MemoryBankControllerType::HuC1, - _ => panic!("Unsupported MBC type"), - }; - - println!("MBC Type: {:?}", &mbc); - Interconnect { bios: bios, - rom: rom, + cartridge: cartridge::Cartridge::new(rom), ram: vec![0; RAM_SIZE].into_boxed_slice(), hiram: vec![0; HIRAM_SIZE].into_boxed_slice(), sound: sound::Sound::new(), display: display::Display::new(), // Refactor those - iflags: 0, + interrupt_request_flags: 0, interrupt: 0, infrared_com_port: 0, disable_bootrom: 0, timer: timer::Timer::new(), serial: serial::Serial::new(), - bank_no: 0, - mbc_type: mbc, } } // Somehow we need different timers for this. pub fn tick(&mut self, cycles: u8) { self.display.tick(cycles as u16 * 4); - } - pub fn display_blank_interrupt(&mut self) { - if self.interrupt & INTERRUPT_DISPLAY_VBLANK >= 0 { - self.display.vblank_interrupt(); + if self.display.vblank_interrupt() { + self.interrupt_request_flags |= INTERRUPT_DISPLAY_VBLANK; + } + + if self.display.stat_interrupt() { + self.interrupt_request_flags |= INTERRUPT_DISPLAY_STAT; } } - pub fn display_interrupt(&mut self) { - if self.interrupt & INTERRUPT_DISPLAY > 0 { - self.display.controller_interrupt(); + pub fn is_boot_rom(&self) -> bool { + self.disable_bootrom == 0 + } + + + pub fn vblank_interrupt(&mut self) -> bool { + // This should set 0xFF0F accordingly and the CPU should + // handle the interrupt generation. So this is WRONG! + // the interrupt WAITS + if self.display.vblank_interrupt() && self.interrupt & INTERRUPT_DISPLAY_VBLANK > 0 { + true + } else { + false } } @@ -91,23 +80,16 @@ impl Interconnect { // TODO: if some flag set, use bios, otherwise only use rom // For now, just use bios match addr { - 0x0000 ... 0x3FFF => { - // TODO: Check if bios or cartridge (additional condition: isEnabled) - if addr < 0x100 && self.disable_bootrom == 0 { + 0x0000 ... 0x100 => { + if self.disable_bootrom == 0 { self.bios[addr as usize] } else { - let val: u8 = self.rom[addr as usize]; - println!("Access {:04X} = {:02X}", addr, val); - val + self.cartridge.read_byte(addr) } }, - 0x4000 ... 0x7FFF => { - let abs_addr: usize = addr as usize + self.bank_no as usize * 0x4000; - let val: u8 = self.rom[abs_addr]; - println!("Access {:04X}{:04X} = {:02X}", self.bank_no, addr, val); - val - }, + 0x100 ... 0x7FFF => self.cartridge.read_byte(addr), 0x8000 ... 0x9FFF => self.display.read_byte(addr), + 0xA000 ... 0xBFFF => self.cartridge.read_byte(addr), 0xC000 ... 0xDFFF => { self.ram[(addr - 0xC000) as usize] }, @@ -115,7 +97,7 @@ impl Interconnect { 0xFF01 ... 0xFF02 => self.serial.read_byte(addr), 0xFF04 ... 0xFF07 => self.timer.read_byte(addr), 0xFF0F => { - self.iflags + self.interrupt_request_flags }, 0xFF10 ... 0xFF26 => { self.sound.read_byte(addr) @@ -157,14 +139,17 @@ impl Interconnect { */ match addr { + 0x0000 ... 0x7FFF => self.cartridge.write_byte(addr, val), 0x8000 ... 0x9FFF => self.display.write_byte(addr, val), + 0xA000 ... 0xBFFF => self.cartridge.write_byte(addr, val), 0xC000 ... 0xDFFF => { self.ram[(addr - 0xC000) as usize] = val; }, + 0xFE00 ... 0xFE9F => self.display.write_byte(addr, val), // OAM 0xFF01 ... 0xFF02 => self.serial.write_byte(addr, val), 0xFF04 ... 0xFF07 => self.timer.write_byte(addr, val), 0xFF0F => { - self.iflags = val; + self.interrupt_request_flags = val; } 0xFF10 ... 0xFF26 => { self.sound.write_byte(addr, val); @@ -183,6 +168,7 @@ impl Interconnect { self.hiram[(addr - 0xFF80) as usize] = val; }, 0xFFFF => { + println!("Setting interrupt mask value {:02X}", val); self.interrupt = val; }, _ => { diff --git a/src/main.rs b/src/main.rs index e14b31b..b9a02de 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ use std::io::Read; use std::fs; use std::env; +mod cartridge; mod cpu; mod display; mod interconnect;