Add cartridge mod; Add sprite support

This commit is contained in:
Kevin Hamacher 2016-05-28 12:57:06 +02:00
parent f10744b2a6
commit e3fce0bcc5
5 changed files with 504 additions and 104 deletions

151
src/cartridge.rs Normal file
View File

@ -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.")
}
}
}

View File

@ -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();

View File

@ -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) {
}
}

View File

@ -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;
},
_ => {

View File

@ -6,6 +6,7 @@ use std::io::Read;
use std::fs;
use std::env;
mod cartridge;
mod cpu;
mod display;
mod interconnect;