Compare commits
2 Commits
cc9cef785e
...
ddb1da4367
| Author | SHA1 | Date | |
|---|---|---|---|
| ddb1da4367 | |||
| 50471f1ec6 |
@ -2,7 +2,8 @@
|
||||
name = "gbc"
|
||||
version = "0.1.0"
|
||||
authors = ["Kevin Hamacher <kevin.hamacher@rub.de>"]
|
||||
edition = '2018'
|
||||
|
||||
[dependencies]
|
||||
sdl2 = "0.19.0"
|
||||
sdl2 = "*"
|
||||
libc = "*"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use super::mbc::mbc::MBC;
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum MemoryBankControllerType {
|
||||
None,
|
||||
MBC1,
|
||||
@ -18,17 +18,17 @@ enum RamSize {
|
||||
}
|
||||
|
||||
pub struct Cartridge {
|
||||
mbc: Box<super::mbc::mbc::MBC>,
|
||||
mbc: Box<dyn super::mbc::mbc::MBC>,
|
||||
savefile: Option<String>,
|
||||
}
|
||||
|
||||
impl Cartridge {
|
||||
pub fn new(rom: Box<[u8]>, save_file: Option<String>) -> Cartridge {
|
||||
let mbc_type: MemoryBankControllerType = match rom[0x0147] {
|
||||
0x00 | 0x08 ... 0x09 => MemoryBankControllerType::None,
|
||||
0x01 ... 0x03 => MemoryBankControllerType::MBC1,
|
||||
0x05 ... 0x06 => MemoryBankControllerType::MBC2,
|
||||
0x0F ... 0x13 => MemoryBankControllerType::MBC3,
|
||||
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]),
|
||||
};
|
||||
@ -37,11 +37,11 @@ impl Cartridge {
|
||||
|
||||
let rom_banks: u16 = match rom[0x0148] {
|
||||
0x00 => 0,
|
||||
0x01 ... 0x07 => 2u16.pow(rom[0x0148] as u32 + 1),
|
||||
0x01..=0x07 => 2u16.pow(rom[0x0148] as u32 + 1),
|
||||
0x52 => 72,
|
||||
0x53 => 80,
|
||||
0x54 => 96,
|
||||
_ => panic!("Unknown rom size: {:?}", rom[0x148])
|
||||
_ => panic!("Unknown rom size: {:?}", rom[0x148]),
|
||||
};
|
||||
|
||||
let ram_size: RamSize = match rom[0x0149] {
|
||||
@ -49,7 +49,7 @@ impl Cartridge {
|
||||
0x01 => RamSize::Ram2KB,
|
||||
0x02 => RamSize::Ram8KB,
|
||||
0x03 => RamSize::Ram32KB,
|
||||
_ => panic!("Unknown ram size: {:?}", rom[0x149])
|
||||
_ => panic!("Unknown ram size: {:?}", rom[0x149]),
|
||||
};
|
||||
|
||||
println!("Rom size: {} banks", rom_banks);
|
||||
@ -57,7 +57,7 @@ impl Cartridge {
|
||||
|
||||
let ram = Cartridge::load_savefile(&save_file, ram_size);
|
||||
|
||||
let mbc: Box<super::mbc::mbc::MBC> = match mbc_type {
|
||||
let mbc: Box<dyn super::mbc::mbc::MBC> = match mbc_type {
|
||||
MemoryBankControllerType::None => Box::new(super::mbc::mbc::NoMBC::new(rom, ram)),
|
||||
MemoryBankControllerType::MBC1 => Box::new(super::mbc::mbc1::MBC1::new(rom, ram)),
|
||||
MemoryBankControllerType::MBC2 => Box::new(super::mbc::mbc2::MBC2::new(rom, ram)),
|
||||
|
||||
264
src/cpu.rs
264
src/cpu.rs
@ -1,7 +1,7 @@
|
||||
use super::interconnect;
|
||||
|
||||
use std::time::{Duration, Instant};
|
||||
use std::thread::sleep;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
const REG_A: usize = 6;
|
||||
|
||||
@ -21,7 +21,6 @@ const FLAG_N: u8 = 1 << 6;
|
||||
const FLAG_H: u8 = 1 << 5;
|
||||
const FLAG_C: u8 = 1 << 4;
|
||||
|
||||
|
||||
pub struct CPU {
|
||||
// Registers: B, C, D, E, H, L, A
|
||||
regs: [u8; 7],
|
||||
@ -154,7 +153,10 @@ impl CPU {
|
||||
self.set_flag(FLAG_N);
|
||||
self.set_clear_flag(FLAG_Z, new == 0);
|
||||
self.set_clear_flag(FLAG_C, new > old || (new == old && c == 1));
|
||||
self.set_clear_flag(FLAG_H, ((old & 0x0F).wrapping_sub((val & 0x0F)).wrapping_sub(c)) > 0x0F);
|
||||
self.set_clear_flag(
|
||||
FLAG_H,
|
||||
((old & 0x0F).wrapping_sub(val & 0x0F).wrapping_sub(c)) > 0x0F,
|
||||
);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -190,7 +192,7 @@ impl CPU {
|
||||
self.ip += 1;
|
||||
|
||||
match instruction {
|
||||
0x00 ... 0x07 => {
|
||||
0x00..=0x07 => {
|
||||
let reg_id = (instruction - 0x00) as usize;
|
||||
let val = self.get_8bit_reg(reg_id);
|
||||
if self.debug {
|
||||
@ -209,8 +211,8 @@ impl CPU {
|
||||
self.set_clear_flag(FLAG_Z, nval == 0);
|
||||
self.clear_flag(FLAG_N);
|
||||
self.clear_flag(FLAG_H);
|
||||
},
|
||||
0x08 ... 0x0F => {
|
||||
}
|
||||
0x08..=0x0F => {
|
||||
let reg_id = (instruction - 0x08) as usize;
|
||||
let val = self.get_8bit_reg(reg_id);
|
||||
if self.debug {
|
||||
@ -227,8 +229,8 @@ impl CPU {
|
||||
self.set_clear_flag(FLAG_Z, val == 0);
|
||||
self.clear_flag(FLAG_N);
|
||||
self.clear_flag(FLAG_H);
|
||||
},
|
||||
0x10 ... 0x17 => {
|
||||
}
|
||||
0x10..=0x17 => {
|
||||
let reg_id = (instruction - 0x10) as usize;
|
||||
let val = self.get_8bit_reg(reg_id);
|
||||
if self.debug {
|
||||
@ -247,8 +249,9 @@ impl CPU {
|
||||
self.set_clear_flag(FLAG_Z, nval == 0);
|
||||
self.clear_flag(FLAG_N);
|
||||
self.clear_flag(FLAG_H);
|
||||
},
|
||||
0x18 ... 0x1F => { // RR
|
||||
}
|
||||
0x18..=0x1F => {
|
||||
// RR
|
||||
let reg_id = (instruction - 0x18) as usize;
|
||||
let val = self.get_8bit_reg(reg_id);
|
||||
if self.debug {
|
||||
@ -267,8 +270,8 @@ impl CPU {
|
||||
self.set_clear_flag(FLAG_Z, v == 0);
|
||||
self.clear_flag(FLAG_N);
|
||||
self.clear_flag(FLAG_H);
|
||||
},
|
||||
0x20 ... 0x27 => {
|
||||
}
|
||||
0x20..=0x27 => {
|
||||
let reg_id = (instruction - 0x20) as usize;
|
||||
if self.debug {
|
||||
println!("SLA {}", REG_NAMES[reg_id]);
|
||||
@ -278,8 +281,8 @@ impl CPU {
|
||||
self.set_clear_flag(FLAG_C, v & 0x80 == 0x80);
|
||||
self.set_clear_flag(FLAG_Z, v & 0x7F == 0);
|
||||
self.set_8bit_reg(reg_id, v << 1);
|
||||
},
|
||||
0x28 ... 0x2F => {
|
||||
}
|
||||
0x28..=0x2F => {
|
||||
let reg_id = (instruction - 0x28) as usize;
|
||||
if self.debug {
|
||||
println!("SRA {}", REG_NAMES[reg_id]);
|
||||
@ -290,8 +293,8 @@ impl CPU {
|
||||
self.flags = 0;
|
||||
self.set_clear_flag(FLAG_Z, nv == 0);
|
||||
self.set_clear_flag(FLAG_C, v & 1 == 1);
|
||||
},
|
||||
0x30 ... 0x37 => {
|
||||
}
|
||||
0x30..=0x37 => {
|
||||
let reg_id = (instruction - 0x30) as usize;
|
||||
if self.debug {
|
||||
println!("SWAP {}", REG_NAMES[reg_id]);
|
||||
@ -300,8 +303,8 @@ impl CPU {
|
||||
self.set_8bit_reg(reg_id, (v << 4) | (v >> 4));
|
||||
self.flags = 0;
|
||||
self.set_clear_flag(FLAG_Z, v == 0);
|
||||
},
|
||||
0x38 ... 0x3F => {
|
||||
}
|
||||
0x38..=0x3F => {
|
||||
let reg_id = (instruction - 0x38) as usize;
|
||||
if self.debug {
|
||||
println!("SRL {}", REG_NAMES[reg_id]);
|
||||
@ -312,10 +315,10 @@ impl CPU {
|
||||
self.clear_flag(FLAG_N);
|
||||
self.clear_flag(FLAG_H);
|
||||
self.set_clear_flag(FLAG_Z, (v & 0xFE) == 0);
|
||||
},
|
||||
}
|
||||
|
||||
// Bits
|
||||
0x40 ... 0x47 => {
|
||||
0x40..=0x47 => {
|
||||
// Test 0th bit
|
||||
let reg_id = (instruction - 0x40) as usize;
|
||||
let reg_content = self.get_8bit_reg(reg_id);
|
||||
@ -326,7 +329,7 @@ impl CPU {
|
||||
self.clear_flag(FLAG_N);
|
||||
self.set_flag(FLAG_H);
|
||||
}
|
||||
0x48 ... 0x4F => {
|
||||
0x48..=0x4F => {
|
||||
// Test 1th bit
|
||||
let reg_id = (instruction - 0x48) as usize;
|
||||
let reg_content = self.get_8bit_reg(reg_id);
|
||||
@ -337,7 +340,7 @@ impl CPU {
|
||||
self.clear_flag(FLAG_N);
|
||||
self.set_flag(FLAG_H);
|
||||
}
|
||||
0x50 ... 0x57 => {
|
||||
0x50..=0x57 => {
|
||||
// Test 2th bit
|
||||
let reg_id = (instruction - 0x50) as usize;
|
||||
let reg_content = self.get_8bit_reg(reg_id);
|
||||
@ -348,7 +351,7 @@ impl CPU {
|
||||
self.clear_flag(FLAG_N);
|
||||
self.set_flag(FLAG_H);
|
||||
}
|
||||
0x58 ... 0x5F => {
|
||||
0x58..=0x5F => {
|
||||
// Test 3th bit
|
||||
let reg_id = (instruction - 0x58) as usize;
|
||||
let reg_content = self.get_8bit_reg(reg_id);
|
||||
@ -359,7 +362,7 @@ impl CPU {
|
||||
self.clear_flag(FLAG_N);
|
||||
self.set_flag(FLAG_H);
|
||||
}
|
||||
0x60 ... 0x67 => {
|
||||
0x60..=0x67 => {
|
||||
// Test 4th bit
|
||||
let reg_id = (instruction - 0x60) as usize;
|
||||
let reg_content = self.get_8bit_reg(reg_id);
|
||||
@ -370,7 +373,7 @@ impl CPU {
|
||||
self.clear_flag(FLAG_N);
|
||||
self.set_flag(FLAG_H);
|
||||
}
|
||||
0x68 ... 0x6F => {
|
||||
0x68..=0x6F => {
|
||||
// Test 5th bit
|
||||
let reg_id = (instruction - 0x68) as usize;
|
||||
let reg_content = self.get_8bit_reg(reg_id);
|
||||
@ -381,7 +384,7 @@ impl CPU {
|
||||
self.clear_flag(FLAG_N);
|
||||
self.set_flag(FLAG_H);
|
||||
}
|
||||
0x70 ... 0x77 => {
|
||||
0x70..=0x77 => {
|
||||
// Test 6th bit
|
||||
let reg_id = (instruction - 0x70) as usize;
|
||||
let reg_content = self.get_8bit_reg(reg_id);
|
||||
@ -392,7 +395,7 @@ impl CPU {
|
||||
self.clear_flag(FLAG_N);
|
||||
self.set_flag(FLAG_H);
|
||||
}
|
||||
0x78 ... 0x7F => {
|
||||
0x78..=0x7F => {
|
||||
// Test 7th bit
|
||||
let reg_id = (instruction - 0x78) as usize;
|
||||
let reg_content = self.get_8bit_reg(reg_id);
|
||||
@ -405,7 +408,7 @@ impl CPU {
|
||||
}
|
||||
|
||||
// Reset bits
|
||||
0x80 ... 0x87 => {
|
||||
0x80..=0x87 => {
|
||||
// Reset 0th bit
|
||||
let reg_id = (instruction - 0x80) as usize;
|
||||
let reg_content = self.get_8bit_reg(reg_id);
|
||||
@ -414,7 +417,7 @@ impl CPU {
|
||||
}
|
||||
self.set_8bit_reg(reg_id, reg_content & !(1 << 0));
|
||||
}
|
||||
0x88 ... 0x8F => {
|
||||
0x88..=0x8F => {
|
||||
// Reset 1th bit
|
||||
let reg_id = (instruction - 0x88) as usize;
|
||||
let reg_content = self.get_8bit_reg(reg_id);
|
||||
@ -423,7 +426,7 @@ impl CPU {
|
||||
}
|
||||
self.set_8bit_reg(reg_id, reg_content & !(1 << 1));
|
||||
}
|
||||
0x90 ... 0x97 => {
|
||||
0x90..=0x97 => {
|
||||
// Reset 2nd bit
|
||||
let reg_id = (instruction - 0x90) as usize;
|
||||
let reg_content = self.get_8bit_reg(reg_id);
|
||||
@ -432,7 +435,7 @@ impl CPU {
|
||||
}
|
||||
self.set_8bit_reg(reg_id, reg_content & !(1 << 2));
|
||||
}
|
||||
0x98 ... 0x9F => {
|
||||
0x98..=0x9F => {
|
||||
// Reset 3th bit
|
||||
let reg_id = (instruction - 0x98) as usize;
|
||||
let reg_content = self.get_8bit_reg(reg_id);
|
||||
@ -441,7 +444,7 @@ impl CPU {
|
||||
}
|
||||
self.set_8bit_reg(reg_id, reg_content & !(1 << 3));
|
||||
}
|
||||
0xA0 ... 0xA7 => {
|
||||
0xA0..=0xA7 => {
|
||||
// Reset 4th bit
|
||||
let reg_id = (instruction - 0xA0) as usize;
|
||||
let reg_content = self.get_8bit_reg(reg_id);
|
||||
@ -450,7 +453,7 @@ impl CPU {
|
||||
}
|
||||
self.set_8bit_reg(reg_id, reg_content & !(1 << 4));
|
||||
}
|
||||
0xA8 ... 0xAF => {
|
||||
0xA8..=0xAF => {
|
||||
// Reset 5th bit
|
||||
let reg_id = (instruction - 0xA8) as usize;
|
||||
let reg_content = self.get_8bit_reg(reg_id);
|
||||
@ -459,7 +462,7 @@ impl CPU {
|
||||
}
|
||||
self.set_8bit_reg(reg_id, reg_content & !(1 << 5));
|
||||
}
|
||||
0xB0 ... 0xB7 => {
|
||||
0xB0..=0xB7 => {
|
||||
// Reset 6th bit
|
||||
let reg_id = (instruction - 0xB0) as usize;
|
||||
let reg_content = self.get_8bit_reg(reg_id);
|
||||
@ -468,7 +471,7 @@ impl CPU {
|
||||
}
|
||||
self.set_8bit_reg(reg_id, reg_content & !(1 << 6));
|
||||
}
|
||||
0xB8 ... 0xBF => {
|
||||
0xB8..=0xBF => {
|
||||
// Reset 7th bit
|
||||
let reg_id = (instruction - 0xB8) as usize;
|
||||
let reg_content = self.get_8bit_reg(reg_id);
|
||||
@ -479,7 +482,7 @@ impl CPU {
|
||||
}
|
||||
|
||||
// Set bits
|
||||
0xC0 ... 0xC7 => {
|
||||
0xC0..=0xC7 => {
|
||||
// Set 0th bit
|
||||
let reg_id = (instruction - 0xC0) as usize;
|
||||
let reg_content = self.get_8bit_reg(reg_id);
|
||||
@ -488,7 +491,7 @@ impl CPU {
|
||||
}
|
||||
self.set_8bit_reg(reg_id, reg_content | (1 << 0));
|
||||
}
|
||||
0xC8 ... 0xCF => {
|
||||
0xC8..=0xCF => {
|
||||
// Set 1th bit
|
||||
let reg_id = (instruction - 0xC8) as usize;
|
||||
let reg_content = self.get_8bit_reg(reg_id);
|
||||
@ -497,7 +500,7 @@ impl CPU {
|
||||
}
|
||||
self.set_8bit_reg(reg_id, reg_content | (1 << 1));
|
||||
}
|
||||
0xD0 ... 0xD7 => {
|
||||
0xD0..=0xD7 => {
|
||||
// Set 2nd bit
|
||||
let reg_id = (instruction - 0xD0) as usize;
|
||||
let reg_content = self.get_8bit_reg(reg_id);
|
||||
@ -506,7 +509,7 @@ impl CPU {
|
||||
}
|
||||
self.set_8bit_reg(reg_id, reg_content | (1 << 2));
|
||||
}
|
||||
0xD8 ... 0xDF => {
|
||||
0xD8..=0xDF => {
|
||||
// Set 3th bit
|
||||
let reg_id = (instruction - 0xD8) as usize;
|
||||
let reg_content = self.get_8bit_reg(reg_id);
|
||||
@ -515,7 +518,7 @@ impl CPU {
|
||||
}
|
||||
self.set_8bit_reg(reg_id, reg_content | (1 << 3));
|
||||
}
|
||||
0xE0 ... 0xE7 => {
|
||||
0xE0..=0xE7 => {
|
||||
// Set 4th bit
|
||||
let reg_id = (instruction - 0xE0) as usize;
|
||||
let reg_content = self.get_8bit_reg(reg_id);
|
||||
@ -524,7 +527,7 @@ impl CPU {
|
||||
}
|
||||
self.set_8bit_reg(reg_id, reg_content | (1 << 4));
|
||||
}
|
||||
0xE8 ... 0xEF => {
|
||||
0xE8..=0xEF => {
|
||||
// Set 5th bit
|
||||
let reg_id = (instruction - 0xE8) as usize;
|
||||
let reg_content = self.get_8bit_reg(reg_id);
|
||||
@ -533,7 +536,7 @@ impl CPU {
|
||||
}
|
||||
self.set_8bit_reg(reg_id, reg_content | (1 << 5));
|
||||
}
|
||||
0xF0 ... 0xF7 => {
|
||||
0xF0..=0xF7 => {
|
||||
// Set 6th bit
|
||||
let reg_id = (instruction - 0xF0) as usize;
|
||||
let reg_content = self.get_8bit_reg(reg_id);
|
||||
@ -542,7 +545,7 @@ impl CPU {
|
||||
}
|
||||
self.set_8bit_reg(reg_id, reg_content | (1 << 6));
|
||||
}
|
||||
0xF8 ... 0xFF => {
|
||||
0xF8..=0xFF => {
|
||||
// Set 7th bit
|
||||
let reg_id = (instruction - 0xF8) as usize;
|
||||
let reg_content = self.get_8bit_reg(reg_id);
|
||||
@ -766,7 +769,10 @@ impl CPU {
|
||||
#[inline]
|
||||
fn add_rr_rr(&mut self, r1: usize, r2: usize, r3: usize, r4: usize) -> u8 {
|
||||
if self.debug {
|
||||
println!("ADD {}{}, {}{}", REG_NAMES[r1], REG_NAMES[r2], REG_NAMES[r3], REG_NAMES[r4]);
|
||||
println!(
|
||||
"ADD {}{}, {}{}",
|
||||
REG_NAMES[r1], REG_NAMES[r2], REG_NAMES[r3], REG_NAMES[r4]
|
||||
);
|
||||
}
|
||||
|
||||
let val1 = self.get_pair_value(r1, r2);
|
||||
@ -830,7 +836,7 @@ impl CPU {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn int_(&mut self, val: u8){
|
||||
fn int_(&mut self, val: u8) {
|
||||
if self.debug {
|
||||
println!("INT {:02X}", val);
|
||||
}
|
||||
@ -868,14 +874,15 @@ impl CPU {
|
||||
loop {
|
||||
let mut cycles: i32 = 0;
|
||||
let start = Instant::now();
|
||||
for _ in 0 .. 1000 {
|
||||
for _ in 0..1000 {
|
||||
cycles += self.run_instruction() as i32;
|
||||
}
|
||||
|
||||
let gb_dur = cycles * 238;
|
||||
let our_dur = start.elapsed().subsec_nanos() as i32;
|
||||
let delta = gb_dur - our_dur;
|
||||
if delta > (20 * 238) { // We're at least 20 cycles faster.
|
||||
if delta > (20 * 238) {
|
||||
// We're at least 20 cycles faster.
|
||||
sleep(Duration::new(0, delta as u32));
|
||||
} else if delta < 0 {
|
||||
print!("-");
|
||||
@ -937,8 +944,11 @@ impl CPU {
|
||||
// We need to double-check the flags
|
||||
instruction = self.read_byte(self.ip);
|
||||
if self.debug {
|
||||
print!("{:#06x}: [SP: {:#04X}] i={:02X}. ", &self.ip, &self.sp, &instruction);
|
||||
for i in 0 .. 6 {
|
||||
print!(
|
||||
"{:#06x}: [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]);
|
||||
@ -959,7 +969,7 @@ impl CPU {
|
||||
println!("NOP");
|
||||
}
|
||||
4
|
||||
},
|
||||
}
|
||||
0x01 => self.ld_rr_vv(REG_N_B, REG_N_C),
|
||||
0x02 => self.ld_dref_rr_a(REG_N_B, REG_N_C),
|
||||
0x03 => self.inc_rr(REG_N_B, REG_N_C),
|
||||
@ -985,7 +995,7 @@ impl CPU {
|
||||
}
|
||||
0x08 => {
|
||||
let a: u16 = to_u16(self.load_args(2));
|
||||
if self.debug{
|
||||
if self.debug {
|
||||
println!("LD ({:04X}), sp", a);
|
||||
}
|
||||
self.interconnect.write_word(a, self.sp);
|
||||
@ -996,9 +1006,11 @@ impl CPU {
|
||||
if self.debug {
|
||||
println!("LD A, (BC)");
|
||||
}
|
||||
self.regs[REG_A] = self.interconnect.read_byte(self.get_pair_value(REG_N_B, REG_N_C));
|
||||
self.regs[REG_A] = self
|
||||
.interconnect
|
||||
.read_byte(self.get_pair_value(REG_N_B, REG_N_C));
|
||||
8
|
||||
},
|
||||
}
|
||||
0x0B => self.dec_rr(REG_N_B, REG_N_C),
|
||||
0x0C => self.reg_inc(REG_N_C),
|
||||
0x0D => self.reg_dec(REG_N_C),
|
||||
@ -1023,7 +1035,7 @@ impl CPU {
|
||||
0x10 => {
|
||||
println!("STOP 0 {:02X} not implemented.", self.load_args(1)[0]);
|
||||
4
|
||||
},
|
||||
}
|
||||
0x11 => self.ld_rr_vv(REG_N_D, REG_N_E),
|
||||
0x12 => self.ld_dref_rr_a(REG_N_D, REG_N_E),
|
||||
0x13 => self.inc_rr(REG_N_D, REG_N_E),
|
||||
@ -1046,20 +1058,22 @@ impl CPU {
|
||||
self.clear_flag(FLAG_N);
|
||||
self.clear_flag(FLAG_H);
|
||||
4
|
||||
},
|
||||
}
|
||||
0x18 => {
|
||||
let dst = self.load_args(1)[0];
|
||||
self.jmp_r(dst);
|
||||
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)");
|
||||
}
|
||||
self.regs[REG_A] = self.interconnect.read_byte(self.get_pair_value(REG_N_D, REG_N_E));
|
||||
self.regs[REG_A] = self
|
||||
.interconnect
|
||||
.read_byte(self.get_pair_value(REG_N_D, REG_N_E));
|
||||
8
|
||||
},
|
||||
}
|
||||
0x1B => self.dec_rr(REG_N_D, REG_N_E),
|
||||
0x1C => self.reg_inc(REG_N_E),
|
||||
0x1D => self.reg_dec(REG_N_E),
|
||||
@ -1080,8 +1094,7 @@ impl CPU {
|
||||
self.clear_flag(FLAG_N);
|
||||
self.clear_flag(FLAG_H);
|
||||
4
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
0x20 => {
|
||||
let c = self.flags & FLAG_Z == 0;
|
||||
@ -1096,7 +1109,7 @@ impl CPU {
|
||||
self.interconnect.write_byte(addr, self.regs[REG_A]);
|
||||
self.set_pair_value(REG_N_H, REG_N_L, addr.wrapping_add(1));
|
||||
8
|
||||
},
|
||||
}
|
||||
0x23 => self.inc_rr(REG_N_H, REG_N_L),
|
||||
0x24 => self.reg_inc(REG_N_H),
|
||||
0x25 => self.reg_dec(REG_N_H),
|
||||
@ -1138,11 +1151,11 @@ impl CPU {
|
||||
self.set_clear_flag(FLAG_Z, a == 0);
|
||||
|
||||
4
|
||||
},
|
||||
}
|
||||
0x28 => {
|
||||
let c = self.flags & FLAG_Z == FLAG_Z;
|
||||
self.jmp_r_condition("Z".to_owned(), c)
|
||||
},
|
||||
}
|
||||
0x29 => self.add_rr_rr(REG_N_H, REG_N_L, REG_N_H, REG_N_L),
|
||||
0x2A => {
|
||||
if self.debug {
|
||||
@ -1152,7 +1165,7 @@ impl CPU {
|
||||
self.regs[REG_A] = self.interconnect.read_byte(addr);
|
||||
self.set_pair_value(REG_N_H, REG_N_L, addr.wrapping_add(1));
|
||||
8
|
||||
},
|
||||
}
|
||||
0x2B => self.dec_rr(REG_N_H, REG_N_L),
|
||||
0x2C => self.reg_inc(REG_N_L),
|
||||
0x2D => self.reg_dec(REG_N_L),
|
||||
@ -1165,13 +1178,12 @@ impl CPU {
|
||||
self.set_flag(FLAG_N);
|
||||
self.set_flag(FLAG_H);
|
||||
4
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
0x30 => {
|
||||
let c = self.flags & FLAG_C == 0;
|
||||
self.jmp_r_condition("NC".to_owned(), c)
|
||||
},
|
||||
}
|
||||
0x31 => {
|
||||
let args = self.load_args(2);
|
||||
self.sp = to_u16(args);
|
||||
@ -1179,7 +1191,7 @@ impl CPU {
|
||||
println!("LD SP, {:04x}", self.sp);
|
||||
}
|
||||
12
|
||||
},
|
||||
}
|
||||
0x32 => {
|
||||
if self.debug {
|
||||
println!("LD (HL-), A");
|
||||
@ -1188,7 +1200,7 @@ impl CPU {
|
||||
self.interconnect.write_byte(addr, self.regs[REG_A]);
|
||||
self.set_pair_value(REG_N_H, REG_N_L, addr.wrapping_sub(1));
|
||||
8
|
||||
},
|
||||
}
|
||||
0x33 => {
|
||||
if self.debug {
|
||||
println!("INC SP");
|
||||
@ -1208,11 +1220,11 @@ impl CPU {
|
||||
self.clear_flag(FLAG_N);
|
||||
self.clear_flag(FLAG_H);
|
||||
4
|
||||
},
|
||||
}
|
||||
0x38 => {
|
||||
let c = self.flags & FLAG_C == FLAG_C;
|
||||
self.jmp_r_condition("C".to_owned(), c)
|
||||
},
|
||||
}
|
||||
0x39 => {
|
||||
if self.debug {
|
||||
println!("ADD HL, SP");
|
||||
@ -1226,7 +1238,7 @@ impl CPU {
|
||||
self.set_clear_flag(FLAG_C, old > v && sp > 0);
|
||||
self.set_clear_flag(FLAG_H, ((old & 0xFFF) + (sp & 0xFFF)) > 0xFFF);
|
||||
8
|
||||
},
|
||||
}
|
||||
0x3A => {
|
||||
if self.debug {
|
||||
println!("LD A, (HL-)");
|
||||
@ -1258,14 +1270,14 @@ impl CPU {
|
||||
}
|
||||
|
||||
// LDs
|
||||
0x40 ... 0x47 => self.ld_r_r(REG_N_B, (instruction - 0x40) as usize),
|
||||
0x48 ... 0x4F => self.ld_r_r(REG_N_C, (instruction - 0x48) as usize),
|
||||
0x50 ... 0x57 => self.ld_r_r(REG_N_D, (instruction - 0x50) as usize),
|
||||
0x58 ... 0x5F => self.ld_r_r(REG_N_E, (instruction - 0x58) as usize),
|
||||
0x60 ... 0x67 => self.ld_r_r(REG_N_H, (instruction - 0x60) as usize),
|
||||
0x68 ... 0x6F => self.ld_r_r(REG_N_L, (instruction - 0x68) as usize),
|
||||
0x70 ... 0x75 | 0x77 => self.ld_r_r(REG_N_HL, (instruction - 0x70) as usize),
|
||||
0x78 ... 0x7F => self.ld_r_r(REG_N_A, (instruction - 0x78) as usize),
|
||||
0x40..=0x47 => self.ld_r_r(REG_N_B, (instruction - 0x40) as usize),
|
||||
0x48..=0x4F => self.ld_r_r(REG_N_C, (instruction - 0x48) as usize),
|
||||
0x50..=0x57 => self.ld_r_r(REG_N_D, (instruction - 0x50) as usize),
|
||||
0x58..=0x5F => self.ld_r_r(REG_N_E, (instruction - 0x58) as usize),
|
||||
0x60..=0x67 => self.ld_r_r(REG_N_H, (instruction - 0x60) as usize),
|
||||
0x68..=0x6F => self.ld_r_r(REG_N_L, (instruction - 0x68) as usize),
|
||||
0x70..=0x75 | 0x77 => self.ld_r_r(REG_N_HL, (instruction - 0x70) as usize),
|
||||
0x78..=0x7F => self.ld_r_r(REG_N_A, (instruction - 0x78) as usize),
|
||||
|
||||
// HALT
|
||||
0x76 => {
|
||||
@ -1274,10 +1286,10 @@ impl CPU {
|
||||
}
|
||||
self.halted = true;
|
||||
4
|
||||
},
|
||||
}
|
||||
|
||||
// ADD
|
||||
0x80 ... 0x87 => {
|
||||
0x80..=0x87 => {
|
||||
let reg_id = (instruction - 0x80) as usize;
|
||||
if self.debug {
|
||||
println!("ADD {}", REG_NAMES[reg_id]);
|
||||
@ -1288,7 +1300,7 @@ impl CPU {
|
||||
}
|
||||
|
||||
// ADC
|
||||
0x88 ... 0x8F => {
|
||||
0x88..=0x8F => {
|
||||
let reg_id = (instruction - 0x88) as usize;
|
||||
if self.debug {
|
||||
println!("ADC {}", REG_NAMES[reg_id]);
|
||||
@ -1300,7 +1312,7 @@ impl CPU {
|
||||
}
|
||||
|
||||
// SUBs
|
||||
0x90 ... 0x97 => {
|
||||
0x90..=0x97 => {
|
||||
let reg_id = (instruction - 0x90) as usize;
|
||||
if self.debug {
|
||||
println!("SUB {}", REG_NAMES[reg_id]);
|
||||
@ -1311,7 +1323,7 @@ impl CPU {
|
||||
}
|
||||
|
||||
// SBC
|
||||
0x98 ... 0x9F => {
|
||||
0x98..=0x9F => {
|
||||
let reg_id = (instruction - 0x98) as usize;
|
||||
if self.debug {
|
||||
println!("SBC {}", REG_NAMES[reg_id]);
|
||||
@ -1322,7 +1334,7 @@ impl CPU {
|
||||
}
|
||||
|
||||
// AND
|
||||
0xA0 ... 0xA7 => {
|
||||
0xA0..=0xA7 => {
|
||||
let reg_id = (instruction - 0xA0) as usize;
|
||||
if self.debug {
|
||||
println!("AND {}", REG_NAMES[reg_id]);
|
||||
@ -1338,7 +1350,7 @@ impl CPU {
|
||||
}
|
||||
|
||||
// XOR
|
||||
0xA8 ... 0xAF => {
|
||||
0xA8..=0xAF => {
|
||||
let reg_id = (instruction - 0xA8) as usize;
|
||||
if self.debug {
|
||||
println!("XOR {}", REG_NAMES[reg_id]);
|
||||
@ -1351,10 +1363,10 @@ impl CPU {
|
||||
self.clear_flag(FLAG_N);
|
||||
self.clear_flag(FLAG_H);
|
||||
4
|
||||
},
|
||||
}
|
||||
|
||||
// OR
|
||||
0xB0 ... 0xB7 => {
|
||||
0xB0..=0xB7 => {
|
||||
let reg_id = (instruction - 0xB0) as usize;
|
||||
if self.debug {
|
||||
println!("OR {}", REG_NAMES[reg_id]);
|
||||
@ -1370,7 +1382,7 @@ impl CPU {
|
||||
}
|
||||
|
||||
// CP
|
||||
0xB8 ... 0xBF => {
|
||||
0xB8..=0xBF => {
|
||||
let reg_id = (instruction - 0xB8) as usize;
|
||||
if self.debug {
|
||||
println!("CP {}", REG_NAMES[reg_id]);
|
||||
@ -1379,17 +1391,17 @@ impl CPU {
|
||||
let v = self.get_8bit_reg(reg_id);
|
||||
self.cp_r(v);
|
||||
4
|
||||
},
|
||||
}
|
||||
|
||||
0xC0 => {
|
||||
let c = self.flags & FLAG_Z == 0;
|
||||
self.ret_condition("NZ".to_owned(), c)
|
||||
},
|
||||
}
|
||||
0xC1 => self.pop_rr(REG_N_B, REG_N_C),
|
||||
0xC2 => {
|
||||
let c = self.flags & FLAG_Z == 0;
|
||||
self.jmp_p_condition("NZ".to_owned(), c)
|
||||
},
|
||||
}
|
||||
0xC3 => {
|
||||
let dst = to_u16(self.load_args(2));
|
||||
if self.debug {
|
||||
@ -1397,7 +1409,7 @@ impl CPU {
|
||||
}
|
||||
self.jmp_p(dst);
|
||||
16
|
||||
},
|
||||
}
|
||||
0xC4 => {
|
||||
let c = self.flags & FLAG_Z == 0;
|
||||
self.call_condition("NZ".to_owned(), c)
|
||||
@ -1412,19 +1424,19 @@ impl CPU {
|
||||
self.add_r(val);
|
||||
|
||||
8
|
||||
},
|
||||
}
|
||||
0xC7 => self.rst(0x00),
|
||||
0xC8 => {
|
||||
let c = self.flags & FLAG_Z == FLAG_Z;
|
||||
self.ret_condition("Z".to_owned(), c)
|
||||
},
|
||||
}
|
||||
0xC9 => {
|
||||
if self.debug {
|
||||
println!("RET");
|
||||
}
|
||||
self.ret();
|
||||
16
|
||||
},
|
||||
}
|
||||
0xCA => {
|
||||
let c = self.flags & FLAG_Z == FLAG_Z;
|
||||
self.jmp_p_condition("Z".to_owned(), c)
|
||||
@ -1432,11 +1444,11 @@ impl CPU {
|
||||
0xCB => {
|
||||
self.run_prefix_instruction();
|
||||
12 // TODO: Verify that this is the case for all prefix instructions.
|
||||
},
|
||||
}
|
||||
0xCC => {
|
||||
let c = self.flags & FLAG_Z == FLAG_Z;
|
||||
self.call_condition("Z".to_owned(), c)
|
||||
},
|
||||
}
|
||||
0xCD => self.call_condition("".to_owned(), true),
|
||||
0xCE => {
|
||||
let arg = self.load_args(1)[0];
|
||||
@ -1445,14 +1457,13 @@ impl CPU {
|
||||
}
|
||||
self.adc_r(arg);
|
||||
8
|
||||
},
|
||||
}
|
||||
0xCF => self.rst(0x08),
|
||||
|
||||
|
||||
0xD0 => {
|
||||
let c = self.flags & FLAG_C == 0;
|
||||
self.ret_condition("NC".to_owned(), c)
|
||||
},
|
||||
}
|
||||
0xD1 => self.pop_rr(REG_N_D, REG_N_E),
|
||||
0xD2 => {
|
||||
let c = self.flags & FLAG_C == 0;
|
||||
@ -1472,12 +1483,12 @@ impl CPU {
|
||||
|
||||
self.sub_r(val);
|
||||
8
|
||||
},
|
||||
}
|
||||
0xD7 => self.rst(0x10),
|
||||
0xD8 => {
|
||||
let c = self.flags & FLAG_C == FLAG_C;
|
||||
self.ret_condition("C".to_owned(), c)
|
||||
},
|
||||
}
|
||||
0xD9 => {
|
||||
if self.debug {
|
||||
println!("RETI");
|
||||
@ -1487,7 +1498,7 @@ impl CPU {
|
||||
0xDA => {
|
||||
let c = self.flags & FLAG_C == FLAG_C;
|
||||
self.jmp_p_condition("C".to_owned(), c)
|
||||
},
|
||||
}
|
||||
0xDB => panic!("NON-EXISTING OPCODE"),
|
||||
0xDC => {
|
||||
let c = self.flags & FLAG_C == FLAG_C;
|
||||
@ -1501,7 +1512,7 @@ impl CPU {
|
||||
}
|
||||
self.sbc_r(arg);
|
||||
8
|
||||
},
|
||||
}
|
||||
0xDF => self.rst(0x18),
|
||||
|
||||
0xE0 => {
|
||||
@ -1509,9 +1520,10 @@ impl CPU {
|
||||
if self.debug {
|
||||
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]);
|
||||
12
|
||||
},
|
||||
}
|
||||
0xE1 => self.pop_rr(REG_N_H, REG_N_L),
|
||||
0xE2 => {
|
||||
if self.debug {
|
||||
@ -1520,7 +1532,7 @@ impl CPU {
|
||||
let addr: u16 = 0xFF00 + self.get_8bit_reg(REG_N_C) as u16;
|
||||
self.interconnect.write_byte(addr, self.regs[REG_A]);
|
||||
8
|
||||
},
|
||||
}
|
||||
0xE3 | 0xE4 => panic!("NON-EXISTING OPCODE"),
|
||||
0xE5 => self.push_rr(REG_N_H, REG_N_L),
|
||||
0xE6 => {
|
||||
@ -1536,7 +1548,7 @@ impl CPU {
|
||||
self.clear_flag(FLAG_C);
|
||||
|
||||
8
|
||||
},
|
||||
}
|
||||
0xE7 => self.rst(0x20),
|
||||
0xE8 => {
|
||||
let arg = self.load_args(1)[0] as i8;
|
||||
@ -1563,16 +1575,16 @@ impl CPU {
|
||||
}
|
||||
self.ip = self.get_pair_value(REG_N_H, REG_N_L);
|
||||
4
|
||||
},
|
||||
}
|
||||
0xEA => {
|
||||
let addr = to_u16(self.load_args(2));
|
||||
if self.debug{
|
||||
if self.debug {
|
||||
println!("LD ({:04X}), A", addr);
|
||||
}
|
||||
self.interconnect.write_byte(addr, self.regs[REG_A]);
|
||||
16
|
||||
},
|
||||
0xEB ... 0xED => panic!("NON-EXISTING OPCODE"),
|
||||
}
|
||||
0xEB..=0xED => panic!("NON-EXISTING OPCODE"),
|
||||
0xEE => {
|
||||
let arg = self.load_args(1)[0];
|
||||
if self.debug {
|
||||
@ -1585,7 +1597,7 @@ impl CPU {
|
||||
self.clear_flag(FLAG_C);
|
||||
self.clear_flag(FLAG_N);
|
||||
8
|
||||
},
|
||||
}
|
||||
0xEF => self.rst(0x28),
|
||||
|
||||
0xF0 => {
|
||||
@ -1595,23 +1607,23 @@ 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{
|
||||
if self.debug {
|
||||
println!("LD A, (C)");
|
||||
}
|
||||
let addr = 0xFF00 + self.get_8bit_reg(REG_N_C) as u16;
|
||||
self.regs[REG_A] = self.interconnect.read_byte(addr);
|
||||
8
|
||||
},
|
||||
}
|
||||
0xF3 => {
|
||||
if self.debug {
|
||||
println!("DI");
|
||||
}
|
||||
self.ime = false;
|
||||
4
|
||||
},
|
||||
}
|
||||
0xF4 => panic!("NON-EXISTING OPCODE"),
|
||||
0xF5 => self.push_rr(REG_N_A, REG_N_F),
|
||||
0xF6 => {
|
||||
@ -1626,7 +1638,7 @@ impl CPU {
|
||||
self.clear_flag(FLAG_N);
|
||||
self.clear_flag(FLAG_H);
|
||||
8
|
||||
},
|
||||
}
|
||||
0xF7 => self.rst(0x30),
|
||||
0xF8 => {
|
||||
let arg = self.load_args(1)[0] as i8;
|
||||
@ -1650,14 +1662,14 @@ impl CPU {
|
||||
self.set_clear_flag(FLAG_H, (sp ^ arg as u16 ^ v) & 0x10 == 0x10);
|
||||
}
|
||||
12
|
||||
},
|
||||
}
|
||||
0xF9 => {
|
||||
if self.debug {
|
||||
println!("LD SP, HL");
|
||||
}
|
||||
self.sp = self.get_pair_value(REG_N_H, REG_N_L);
|
||||
8
|
||||
},
|
||||
}
|
||||
0xFA => {
|
||||
let addr = to_u16(self.load_args(2));
|
||||
if self.debug {
|
||||
@ -1665,7 +1677,7 @@ impl CPU {
|
||||
}
|
||||
self.regs[REG_A] = self.interconnect.read_byte(addr);
|
||||
16
|
||||
},
|
||||
}
|
||||
0xFB => {
|
||||
// Enable interrupts - TODO
|
||||
if self.debug {
|
||||
@ -1673,7 +1685,7 @@ impl CPU {
|
||||
}
|
||||
self.ime = true; // interrupt master enable
|
||||
4
|
||||
},
|
||||
}
|
||||
0xFC | 0xFD => panic!("NON-EXISTING OPCODE"),
|
||||
|
||||
0xFE => {
|
||||
@ -1684,9 +1696,9 @@ impl CPU {
|
||||
|
||||
self.cp_r(args[0]);
|
||||
8
|
||||
},
|
||||
}
|
||||
0xFF => self.rst(0x38),
|
||||
_ => panic!("Unknown instruction: {:02x}", instruction)
|
||||
_ => panic!("Unknown instruction: {:02x}", instruction),
|
||||
};
|
||||
}
|
||||
self.interconnect.tick(cycles);
|
||||
|
||||
130
src/display.rs
130
src/display.rs
@ -1,5 +1,5 @@
|
||||
extern crate sdl2;
|
||||
extern crate libc;
|
||||
extern crate sdl2;
|
||||
|
||||
// Internal ram size
|
||||
const VRAM_SIZE: usize = 0x2000;
|
||||
@ -42,7 +42,7 @@ const SPRITE_OBJ_BG_PRIORITY: u8 = 1 << 7;
|
||||
const SPRITE_Y_FLIP: u8 = 1 << 6;
|
||||
const SPRITE_X_FLIP: u8 = 1 << 5;
|
||||
const SPRITE_PALETTE_NO: u8 = 1 << 4; // NonCGB only
|
||||
// const SPRITE_TILE_VRAM_BANK: u8 = 1 << 3; // CGB only
|
||||
// const SPRITE_TILE_VRAM_BANK: u8 = 1 << 3; // CGB only
|
||||
|
||||
// Display color
|
||||
/*
|
||||
@ -65,7 +65,7 @@ enum PixelOrigin {
|
||||
Empty,
|
||||
Background,
|
||||
Window,
|
||||
Sprite
|
||||
Sprite,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
@ -149,8 +149,7 @@ pub struct Display {
|
||||
current_ticks: u16,
|
||||
current_mode: DisplayMode,
|
||||
// TODO
|
||||
|
||||
renderer: sdl2::render::Renderer<'static>,
|
||||
renderer: sdl2::render::Canvas<sdl2::video::Window>,
|
||||
|
||||
pub event_pump: sdl2::EventPump,
|
||||
|
||||
@ -167,8 +166,12 @@ impl Display {
|
||||
pub fn new() -> Display {
|
||||
let sdl_ctx = sdl2::init().unwrap();
|
||||
let video_ctx = sdl_ctx.video().unwrap();
|
||||
let wnd = video_ctx.window("RustBoy", WND_RES_X as u32, WND_RES_Y as u32).position_centered().build().expect("Failed to create window :<");
|
||||
let renderer = wnd.renderer().build().expect("Could not build renderer");
|
||||
let wnd = video_ctx
|
||||
.window("RustBoy", WND_RES_X as u32, WND_RES_Y as u32)
|
||||
.position_centered()
|
||||
.build()
|
||||
.expect("Failed to create window :<");
|
||||
let renderer = wnd.into_canvas().build().expect("Could not build renderer");
|
||||
let event_pump = sdl_ctx.event_pump().expect("Getting event pump failed");
|
||||
let pixels = [Pixel::default(); (GB_PIXELS_X as usize) * (GB_PIXELS_Y as usize)];
|
||||
|
||||
@ -214,33 +217,34 @@ impl Display {
|
||||
}
|
||||
|
||||
fn render_screen(&mut self) {
|
||||
for y in 0 .. GB_PIXELS_Y {
|
||||
for x in 0 .. GB_PIXELS_X {
|
||||
for y in 0..GB_PIXELS_Y {
|
||||
for x in 0..GB_PIXELS_X {
|
||||
let f = &mut self.pixels[(x as usize) + (y as usize) * GB_PIXELS_X];
|
||||
self.renderer.set_draw_color(f.color);
|
||||
self.renderer.fill_rect(
|
||||
sdl2::rect::Rect::new(
|
||||
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,
|
||||
)
|
||||
).expect("Rendering failed");
|
||||
))
|
||||
.expect("Rendering failed");
|
||||
|
||||
// Clear origin after rendering
|
||||
f.origin = PixelOrigin::Empty;
|
||||
}
|
||||
}
|
||||
|
||||
self.renderer.set_draw_color(sdl2::pixels::Color::RGB(255, 0, 255));
|
||||
self.renderer.draw_rect(
|
||||
sdl2::rect::Rect::new(
|
||||
self.renderer
|
||||
.set_draw_color(sdl2::pixels::Color::RGB(255, 0, 255));
|
||||
self.renderer
|
||||
.draw_rect(sdl2::rect::Rect::new(
|
||||
0,
|
||||
0,
|
||||
(GB_PIXELS_X * SCALE) as u32,
|
||||
(GB_PIXELS_Y * SCALE) as u32,
|
||||
)
|
||||
).expect("Rendering failed");
|
||||
))
|
||||
.expect("Rendering failed");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -268,8 +272,8 @@ impl Display {
|
||||
#[inline]
|
||||
pub fn write_byte(&mut self, addr: u16, val: u8) {
|
||||
match addr {
|
||||
0x8000 ... 0x9FFF => self.vram[(addr - 0x8000) as usize] = val,
|
||||
0xFE00 ... 0xFE9F => self.oam[(addr - 0xFE00) as usize] = val,
|
||||
0x8000..=0x9FFF => self.vram[(addr - 0x8000) as usize] = val,
|
||||
0xFE00..=0xFE9F => self.oam[(addr - 0xFE00) as usize] = val,
|
||||
0xFF40 => self.control = val,
|
||||
0xFF41 => self.status = val,
|
||||
0xFF42 => self.scrolly = val,
|
||||
@ -298,8 +302,8 @@ impl Display {
|
||||
#[inline]
|
||||
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],
|
||||
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,
|
||||
@ -329,15 +333,17 @@ impl Display {
|
||||
self.current_ticks += ticks;
|
||||
|
||||
match self.current_mode {
|
||||
DisplayMode::ReadOAMMemory => { // Mode 2, Reading OAM memory, RAM may be accessed.
|
||||
DisplayMode::ReadOAMMemory => {
|
||||
// Mode 2, Reading OAM memory, RAM may be accessed.
|
||||
if self.current_ticks > TICKS_END_SCANLINE {
|
||||
self.current_ticks = 0;
|
||||
self.current_mode = DisplayMode::ReadFullMemory;
|
||||
}
|
||||
self.status |= 2;
|
||||
},
|
||||
DisplayMode::ReadFullMemory => { // Mode 3, reading OAM, VMEM and palette data.
|
||||
// Nothing may be accessed.
|
||||
}
|
||||
DisplayMode::ReadFullMemory => {
|
||||
// Mode 3, reading OAM, VMEM and palette data.
|
||||
// Nothing may be accessed.
|
||||
if self.current_ticks > TICKS_END_READMODE {
|
||||
self.current_ticks = 0;
|
||||
self.current_mode = DisplayMode::HBlank;
|
||||
@ -349,8 +355,9 @@ impl Display {
|
||||
}
|
||||
}
|
||||
self.status |= 3;
|
||||
},
|
||||
DisplayMode::HBlank => { // Mode 0, H-Blank, Memory (RAM, OAM) may be accessed.
|
||||
}
|
||||
DisplayMode::HBlank => {
|
||||
// Mode 0, H-Blank, Memory (RAM, OAM) may be accessed.
|
||||
if self.current_ticks > TICKS_END_HBLANK {
|
||||
self.current_ticks = 0;
|
||||
self.curline += 1;
|
||||
@ -369,9 +376,10 @@ impl Display {
|
||||
}
|
||||
self.status &= 0xFC;
|
||||
self.status |= 0;
|
||||
},
|
||||
DisplayMode::VBlank => { // Mode 1, V-Blank (or display disabled), Memory (RAM, OAM)
|
||||
// may be accessed
|
||||
}
|
||||
DisplayMode::VBlank => {
|
||||
// Mode 1, V-Blank (or display disabled), Memory (RAM, OAM)
|
||||
// may be accessed
|
||||
if self.current_ticks > TICKS_END_VBLANK {
|
||||
self.current_ticks = 0;
|
||||
self.curline += 1;
|
||||
@ -383,7 +391,8 @@ impl Display {
|
||||
if self.frameskip < self.frame_no {
|
||||
self.render_screen();
|
||||
self.renderer.present();
|
||||
self.renderer.set_draw_color(sdl2::pixels::Color::RGB(255, 255, 255));
|
||||
self.renderer
|
||||
.set_draw_color(sdl2::pixels::Color::RGB(255, 255, 255));
|
||||
self.renderer.clear();
|
||||
self.frame_no = 0;
|
||||
} else {
|
||||
@ -413,7 +422,7 @@ impl Display {
|
||||
fn render_sprites(&mut self, sprites: &Vec<Sprite>) {
|
||||
if self.control & CTRL_BG_SPRITE_ENABLE > 0 {
|
||||
let mut num_rendered: u8 = 0;
|
||||
for i in 0 .. 39 {
|
||||
for i in 0..39 {
|
||||
// Gameboy limitation
|
||||
if num_rendered > 10 {
|
||||
break;
|
||||
@ -425,7 +434,6 @@ impl Display {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Calculate correct coords
|
||||
let x: u8 = sprite.x.wrapping_sub(8);
|
||||
let y: u8 = sprite.y.wrapping_sub(16);
|
||||
@ -438,7 +446,7 @@ impl Display {
|
||||
// Flip sprite, TODO: Validate
|
||||
let y_o: u8 = match sprite.is_y_flipped() {
|
||||
true => y ^ 7,
|
||||
false => y
|
||||
false => y,
|
||||
};
|
||||
let tile_offset_y: usize = render_y as usize - y_o as usize;
|
||||
let tile_base_addr: usize = sprite.tile as usize * 16; // Should this be twice as wide in wide mode?
|
||||
@ -456,9 +464,9 @@ impl Display {
|
||||
}
|
||||
let limit = match wide_mode {
|
||||
true => 16,
|
||||
false => 8
|
||||
false => 8,
|
||||
};
|
||||
for x_o in 0 .. limit {
|
||||
for x_o in 0..limit {
|
||||
let b1: bool;
|
||||
let b2: bool;
|
||||
|
||||
@ -513,7 +521,12 @@ impl Display {
|
||||
let entry = MONOCHROME_PALETTE[lookup[c as usize] as usize];
|
||||
|
||||
// Draw stuff. We're currently only in monochrome mode
|
||||
self.set_pixel(x.wrapping_add(x_o), render_y, sdl2::pixels::Color::RGB(entry[0], entry[1], entry[2]), PixelOrigin::Sprite);
|
||||
self.set_pixel(
|
||||
x.wrapping_add(x_o),
|
||||
render_y,
|
||||
sdl2::pixels::Color::RGB(entry[0], entry[1], entry[2]),
|
||||
PixelOrigin::Sprite,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -557,8 +570,8 @@ impl Display {
|
||||
let render_y: u8 = self.curline;
|
||||
// Order sprites by priority
|
||||
let mut queue: Vec<Sprite> = Vec::new();
|
||||
for i in 0 .. 39 {
|
||||
queue.push(Sprite{
|
||||
for i in 0..39 {
|
||||
queue.push(Sprite {
|
||||
y: self.oam[i * 4 + 0],
|
||||
x: self.oam[i * 4 + 1],
|
||||
tile: self.oam[i * 4 + 2],
|
||||
@ -571,20 +584,19 @@ impl Display {
|
||||
use std::cmp;
|
||||
queue.sort_by(|x, y| {
|
||||
if x.x > y.x {
|
||||
cmp::Ordering::Greater
|
||||
} else if x.x < y.x {
|
||||
cmp::Ordering::Less
|
||||
} else {
|
||||
cmp::Ordering::Equal
|
||||
}
|
||||
cmp::Ordering::Greater
|
||||
} else if x.x < y.x {
|
||||
cmp::Ordering::Less
|
||||
} else {
|
||||
cmp::Ordering::Equal
|
||||
}
|
||||
});
|
||||
queue.reverse();
|
||||
|
||||
|
||||
// Render background
|
||||
if (self.control & CTRL_BG_DISPLAY) == CTRL_BG_DISPLAY {
|
||||
// Render pixels (20 tiles)
|
||||
for render_x in 0 .. 160 {
|
||||
for render_x in 0..160 {
|
||||
// Absolute render coordinates
|
||||
let render_abs_x = map_offset_x.wrapping_add(render_x);
|
||||
let render_abs_y = map_offset_y.wrapping_add(render_y);
|
||||
@ -595,7 +607,8 @@ impl Display {
|
||||
let tile_index_y: u8 = render_abs_y >> 3;
|
||||
let tile_offset_y: u8 = render_abs_y & 7;
|
||||
|
||||
let vram_offset: usize = background_map + ((tile_index_y as usize) * 32) + tile_index_x as usize;
|
||||
let vram_offset: usize =
|
||||
background_map + ((tile_index_y as usize) * 32) + tile_index_x as usize;
|
||||
|
||||
// Obtain tile ID in this area
|
||||
let tile_id = self.vram[vram_offset];
|
||||
@ -624,7 +637,12 @@ impl Display {
|
||||
0 => PixelOrigin::Empty, // Hack so that objects will be in front of it.
|
||||
_ => PixelOrigin::Background,
|
||||
};
|
||||
self.set_pixel(render_x, render_y, sdl2::pixels::Color::RGB(entry[0], entry[1], entry[2]), origin);
|
||||
self.set_pixel(
|
||||
render_x,
|
||||
render_y,
|
||||
sdl2::pixels::Color::RGB(entry[0], entry[1], entry[2]),
|
||||
origin,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -635,7 +653,7 @@ impl Display {
|
||||
//let rx = self.windowx.wrapping_add(7);
|
||||
let rx = 7u8.wrapping_sub(self.windowx);
|
||||
let ry = render_y.wrapping_add(self.windowy);
|
||||
for r_x in 0 .. 160u8 {
|
||||
for r_x in 0..160u8 {
|
||||
let render_x = r_x.wrapping_add(rx);
|
||||
// Absolute render coordinates
|
||||
let tile_index_x: u8 = render_x >> 3;
|
||||
@ -643,7 +661,8 @@ impl Display {
|
||||
let tile_index_y: u8 = ry >> 3;
|
||||
let tile_offset_y: u8 = ry & 7;
|
||||
|
||||
let vram_offset: usize = window_map + (tile_index_y as usize) * 32 + tile_index_x as usize;
|
||||
let vram_offset: usize =
|
||||
window_map + (tile_index_y as usize) * 32 + tile_index_x as usize;
|
||||
|
||||
// Obtain tile ID in this area
|
||||
let tile_id = self.vram[vram_offset];
|
||||
@ -672,7 +691,12 @@ impl Display {
|
||||
0 => PixelOrigin::Empty, // Hack so that objects will be in front of it.
|
||||
_ => PixelOrigin::Window,
|
||||
};
|
||||
self.set_pixel(render_x, render_y, sdl2::pixels::Color::RGB(entry[0], entry[1], entry[2]), origin);
|
||||
self.set_pixel(
|
||||
render_x,
|
||||
render_y,
|
||||
sdl2::pixels::Color::RGB(entry[0], entry[1], entry[2]),
|
||||
origin,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,11 +21,11 @@ const KEY_SELECT: u8 = 1 << 2;
|
||||
const KEY_B: u8 = 1 << 1;
|
||||
const KEY_A: u8 = 1 << 0;
|
||||
|
||||
use super::cartridge;
|
||||
use super::display;
|
||||
use super::serial;
|
||||
use super::sound;
|
||||
use super::timer;
|
||||
use super::serial;
|
||||
use super::cartridge;
|
||||
|
||||
extern crate sdl2;
|
||||
|
||||
@ -34,7 +34,14 @@ use self::sdl2::keyboard::Keycode;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Key {
|
||||
UP, LEFT, DOWN, RIGHT, START, SELECT, A, B
|
||||
UP,
|
||||
LEFT,
|
||||
DOWN,
|
||||
RIGHT,
|
||||
START,
|
||||
SELECT,
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
pub struct Interconnect {
|
||||
@ -153,29 +160,81 @@ impl Interconnect {
|
||||
// Make sure the window is responsive:
|
||||
if self.cycles > 500 {
|
||||
loop {
|
||||
if let Some(event) = self.display.event_pump.poll_event(){
|
||||
if let Some(event) = self.display.event_pump.poll_event() {
|
||||
match event {
|
||||
Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
|
||||
Event::Quit { .. }
|
||||
| Event::KeyDown {
|
||||
keycode: Some(Keycode::Escape),
|
||||
..
|
||||
} => {
|
||||
self.cartridge.save();
|
||||
panic!("TODO: Proper shutdown");
|
||||
},
|
||||
Event::KeyDown { keycode: Some(Keycode::Left), .. } => self.press_key(Key::LEFT),
|
||||
Event::KeyDown { keycode: Some(Keycode::Down), .. } => self.press_key(Key::DOWN),
|
||||
Event::KeyDown { keycode: Some(Keycode::Up), .. } => self.press_key(Key::UP),
|
||||
Event::KeyDown { keycode: Some(Keycode::Right), .. } => self.press_key(Key::RIGHT),
|
||||
Event::KeyDown { keycode: Some(Keycode::A), .. } => self.press_key(Key::START),
|
||||
Event::KeyDown { keycode: Some(Keycode::S), .. } => self.press_key(Key::SELECT),
|
||||
Event::KeyDown { keycode: Some(Keycode::Z), .. } => self.press_key(Key::A),
|
||||
Event::KeyDown { keycode: Some(Keycode::X), .. } => self.press_key(Key::B),
|
||||
}
|
||||
Event::KeyDown {
|
||||
keycode: Some(Keycode::Left),
|
||||
..
|
||||
} => self.press_key(Key::LEFT),
|
||||
Event::KeyDown {
|
||||
keycode: Some(Keycode::Down),
|
||||
..
|
||||
} => self.press_key(Key::DOWN),
|
||||
Event::KeyDown {
|
||||
keycode: Some(Keycode::Up),
|
||||
..
|
||||
} => self.press_key(Key::UP),
|
||||
Event::KeyDown {
|
||||
keycode: Some(Keycode::Right),
|
||||
..
|
||||
} => self.press_key(Key::RIGHT),
|
||||
Event::KeyDown {
|
||||
keycode: Some(Keycode::A),
|
||||
..
|
||||
} => self.press_key(Key::START),
|
||||
Event::KeyDown {
|
||||
keycode: Some(Keycode::S),
|
||||
..
|
||||
} => self.press_key(Key::SELECT),
|
||||
Event::KeyDown {
|
||||
keycode: Some(Keycode::Z),
|
||||
..
|
||||
} => self.press_key(Key::A),
|
||||
Event::KeyDown {
|
||||
keycode: Some(Keycode::X),
|
||||
..
|
||||
} => self.press_key(Key::B),
|
||||
|
||||
Event::KeyUp { keycode: Some(Keycode::Left), .. } => self.release_key(Key::LEFT),
|
||||
Event::KeyUp { keycode: Some(Keycode::Down), .. } => self.release_key(Key::DOWN),
|
||||
Event::KeyUp { keycode: Some(Keycode::Up), .. } => self.release_key(Key::UP),
|
||||
Event::KeyUp { keycode: Some(Keycode::Right), .. } => self.release_key(Key::RIGHT),
|
||||
Event::KeyUp { keycode: Some(Keycode::A), .. } => self.release_key(Key::START),
|
||||
Event::KeyUp { keycode: Some(Keycode::S), .. } => self.release_key(Key::SELECT),
|
||||
Event::KeyUp { keycode: Some(Keycode::Z), .. } => self.release_key(Key::A),
|
||||
Event::KeyUp { keycode: Some(Keycode::X), .. } => self.release_key(Key::B),
|
||||
Event::KeyUp {
|
||||
keycode: Some(Keycode::Left),
|
||||
..
|
||||
} => self.release_key(Key::LEFT),
|
||||
Event::KeyUp {
|
||||
keycode: Some(Keycode::Down),
|
||||
..
|
||||
} => self.release_key(Key::DOWN),
|
||||
Event::KeyUp {
|
||||
keycode: Some(Keycode::Up),
|
||||
..
|
||||
} => self.release_key(Key::UP),
|
||||
Event::KeyUp {
|
||||
keycode: Some(Keycode::Right),
|
||||
..
|
||||
} => self.release_key(Key::RIGHT),
|
||||
Event::KeyUp {
|
||||
keycode: Some(Keycode::A),
|
||||
..
|
||||
} => self.release_key(Key::START),
|
||||
Event::KeyUp {
|
||||
keycode: Some(Keycode::S),
|
||||
..
|
||||
} => self.release_key(Key::SELECT),
|
||||
Event::KeyUp {
|
||||
keycode: Some(Keycode::Z),
|
||||
..
|
||||
} => self.release_key(Key::A),
|
||||
Event::KeyUp {
|
||||
keycode: Some(Keycode::X),
|
||||
..
|
||||
} => self.release_key(Key::B),
|
||||
_ => {}
|
||||
}
|
||||
} else {
|
||||
@ -199,25 +258,21 @@ impl Interconnect {
|
||||
// TODO: if some flag set, use bios, otherwise only use rom
|
||||
// For now, just use bios
|
||||
match addr {
|
||||
0x0000 ... 0x100 => {
|
||||
0x0000..=0x100 => {
|
||||
if self.disable_bootrom == 0 {
|
||||
self.bios[addr as usize]
|
||||
} else {
|
||||
self.cartridge.read_byte(addr)
|
||||
}
|
||||
},
|
||||
0x100 ... 0x7FFF => self.cartridge.read_byte(addr),
|
||||
0x8000 ... 0x9FFF => self.display.read_byte(addr),
|
||||
0xA000 ... 0xBFFF => self.cartridge.read_byte(addr),
|
||||
0xC000 ... 0xCFFF => {
|
||||
self.ram[(addr - 0xC000) as usize]
|
||||
},
|
||||
0xD000 ... 0xDFFF => {
|
||||
self.ram[(addr - 0xD000) as usize + self.wram_bank as usize * 0x1000]
|
||||
},
|
||||
0xE000 ... 0xEFFF => {
|
||||
self.ram[(addr - 0xE000) as usize]
|
||||
}
|
||||
0x100..=0x7FFF => self.cartridge.read_byte(addr),
|
||||
0x8000..=0x9FFF => self.display.read_byte(addr),
|
||||
0xA000..=0xBFFF => self.cartridge.read_byte(addr),
|
||||
0xC000..=0xCFFF => self.ram[(addr - 0xC000) as usize],
|
||||
0xD000..=0xDFFF => {
|
||||
self.ram[(addr - 0xD000) as usize + self.wram_bank as usize * 0x1000]
|
||||
}
|
||||
0xE000..=0xEFFF => self.ram[(addr - 0xE000) as usize],
|
||||
0xFF00 => {
|
||||
if self.joy_switch & KEY_REGULAR == 0 {
|
||||
self.joy_regular_keys
|
||||
@ -228,39 +283,25 @@ impl Interconnect {
|
||||
0x3F
|
||||
}
|
||||
}
|
||||
0xFF01 ... 0xFF02 => self.serial.read_byte(addr),
|
||||
0xFF04 ... 0xFF07 => self.timer.read_byte(addr),
|
||||
0xFF01..=0xFF02 => self.serial.read_byte(addr),
|
||||
0xFF04..=0xFF07 => self.timer.read_byte(addr),
|
||||
0xFF0F => {
|
||||
// println!("Reading IF: {:02X}", self.interrupt_request_flags);
|
||||
self.interrupt_request_flags
|
||||
},
|
||||
0xFF10 ... 0xFF26 => {
|
||||
self.sound.read_byte(addr)
|
||||
},
|
||||
0xFF30 ... 0xFF3F => self.sound.read_byte(addr),
|
||||
0xFF40 ... 0xFF4B => {
|
||||
self.display.read_byte(addr)
|
||||
},
|
||||
0xFF50 => {
|
||||
self.disable_bootrom
|
||||
},
|
||||
}
|
||||
0xFF10..=0xFF26 => self.sound.read_byte(addr),
|
||||
0xFF30..=0xFF3F => self.sound.read_byte(addr),
|
||||
0xFF40..=0xFF4B => self.display.read_byte(addr),
|
||||
0xFF50 => self.disable_bootrom,
|
||||
0xFF51 => self.vram_dma_source_high,
|
||||
0xFF52 => self.vram_dma_source_low,
|
||||
0xFF53 => self.vram_dma_destination_high,
|
||||
0xFF54 => self.vram_dma_destination_low,
|
||||
0xFF55 => {
|
||||
self.vram_dma_length
|
||||
}
|
||||
0xFF56 => {
|
||||
self.infrared_com_port
|
||||
},
|
||||
0xFF55 => self.vram_dma_length,
|
||||
0xFF56 => self.infrared_com_port,
|
||||
0xFF70 => self.wram_bank,
|
||||
0xFF80 ... 0xFFFE => {
|
||||
self.hiram[(addr - 0xFF80) as usize]
|
||||
},
|
||||
0xFFFF => {
|
||||
self.interrupt
|
||||
}
|
||||
0xFF80..=0xFFFE => self.hiram[(addr - 0xFF80) as usize],
|
||||
0xFFFF => self.interrupt,
|
||||
_ => {
|
||||
println!("Read from {:04X} not supported.", addr);
|
||||
0
|
||||
@ -285,36 +326,36 @@ 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 ... 0xCFFF => {
|
||||
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..=0xCFFF => {
|
||||
self.ram[(addr - 0xC000) as usize] = val;
|
||||
},
|
||||
0xD000 ... 0xDFFF => {
|
||||
}
|
||||
0xD000..=0xDFFF => {
|
||||
self.ram[(addr - 0xD000) as usize + self.wram_bank as usize * 0x1000] = val;
|
||||
}
|
||||
0xFE00 ... 0xFE9F => self.display.write_byte(addr, val), // OAM
|
||||
0xFE00..=0xFE9F => self.display.write_byte(addr, val), // OAM
|
||||
0xFF00 => {
|
||||
// Joystick select
|
||||
self.joy_switch = val;
|
||||
}
|
||||
0xFF01 ... 0xFF02 => self.serial.write_byte(addr, val),
|
||||
0xFF04 ... 0xFF07 => self.timer.write_byte(addr, val),
|
||||
0xFF01..=0xFF02 => self.serial.write_byte(addr, val),
|
||||
0xFF04..=0xFF07 => self.timer.write_byte(addr, val),
|
||||
0xFF0F => {
|
||||
self.interrupt_request_flags = val;
|
||||
}
|
||||
0xFF10 ... 0xFF26 => {
|
||||
0xFF10..=0xFF26 => {
|
||||
self.sound.write_byte(addr, val);
|
||||
},
|
||||
0xFF30 ... 0xFF3F => self.sound.write_byte(addr, val),
|
||||
}
|
||||
0xFF30..=0xFF3F => self.sound.write_byte(addr, val),
|
||||
// Exclude DMA transfer, we will do this below
|
||||
0xFF40 ... 0xFF45 | 0xFF47 ... 0xFF4B => {
|
||||
0xFF40..=0xFF45 | 0xFF47..=0xFF4B => {
|
||||
self.display.write_byte(addr, val);
|
||||
},
|
||||
}
|
||||
0xFF46 => {
|
||||
// println!("OAM DMA transfer");
|
||||
for x in 0x00 .. 0x9F {
|
||||
for x in 0x00..0x9F {
|
||||
let dma_b = self.read_byte(((val as u16) << 8) | x);
|
||||
self.write_byte(0xFE00 | x, dma_b);
|
||||
}
|
||||
@ -322,17 +363,22 @@ impl Interconnect {
|
||||
0xFF50 => {
|
||||
println!("Disabling boot rom.");
|
||||
self.disable_bootrom = val;
|
||||
},
|
||||
}
|
||||
0xFF51 => self.vram_dma_source_high = val,
|
||||
0xFF52 => self.vram_dma_source_low = val & 0xF0,
|
||||
0xFF53 => self.vram_dma_destination_high = val & 0x1F,
|
||||
0xFF54 => self.vram_dma_destination_low = val & 0xF0,
|
||||
0xFF55 => {
|
||||
let src: u16 = ((self.vram_dma_source_high as u16) << 8) | self.vram_dma_source_low as u16;
|
||||
let mut dst: u16 = ((self.vram_dma_destination_high as u16) << 8) | self.vram_dma_destination_low as u16;
|
||||
let src: u16 =
|
||||
((self.vram_dma_source_high as u16) << 8) | self.vram_dma_source_low as u16;
|
||||
let mut dst: u16 = ((self.vram_dma_destination_high as u16) << 8)
|
||||
| self.vram_dma_destination_low as u16;
|
||||
|
||||
dst += 0x8000;
|
||||
println!("VRAM DMA transfer from {:04X} to {:04X}; {:02X}", src, dst, val);
|
||||
println!(
|
||||
"VRAM DMA transfer from {:04X} to {:04X}; {:02X}",
|
||||
src, dst, val
|
||||
);
|
||||
let len: u16 = ((val & 0x7F) + 1) as u16 * 0x10 - 1;
|
||||
for i in 0..len {
|
||||
let v = self.read_byte(src.wrapping_add(i));
|
||||
@ -344,7 +390,7 @@ impl Interconnect {
|
||||
}
|
||||
0xFF56 => {
|
||||
self.infrared_com_port = val;
|
||||
},
|
||||
}
|
||||
0xFF70 => {
|
||||
if self.wram_bank != val {
|
||||
println!("Switching wram bank to {:02X}", val);
|
||||
@ -358,13 +404,13 @@ impl Interconnect {
|
||||
}
|
||||
}
|
||||
}
|
||||
0xFF80 ... 0xFFFE => {
|
||||
0xFF80..=0xFFFE => {
|
||||
self.hiram[(addr - 0xFF80) as usize] = val;
|
||||
},
|
||||
}
|
||||
0xFFFF => {
|
||||
println!("Setting interrupt mask value {:02X}", val);
|
||||
self.interrupt = val;
|
||||
},
|
||||
}
|
||||
_ => {
|
||||
println!("Write {:02X} to {:04X} not supported.", val, addr);
|
||||
}
|
||||
|
||||
19
src/main.rs
19
src/main.rs
@ -1,22 +1,21 @@
|
||||
// let's try to write our own, awesome emulator.
|
||||
// gameboy (color?)
|
||||
|
||||
use std::path::Path;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::io::Read;
|
||||
use std::io::Write;
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
|
||||
mod cartridge;
|
||||
mod cpu;
|
||||
mod display;
|
||||
mod interconnect;
|
||||
mod mbc;
|
||||
mod serial;
|
||||
mod sound;
|
||||
mod timer;
|
||||
mod serial;
|
||||
mod mbc;
|
||||
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
@ -44,14 +43,14 @@ fn main() {
|
||||
}
|
||||
|
||||
pub fn read_file<P: AsRef<Path>>(rom_path: P) -> Result<Box<[u8]>, io::Error> {
|
||||
let mut file = try!(fs::File::open(rom_path));
|
||||
let mut file = r#try!(fs::File::open(rom_path));
|
||||
let mut buf = Vec::new();
|
||||
try!(file.read_to_end(&mut buf));
|
||||
r#try!(file.read_to_end(&mut buf));
|
||||
Ok(buf.into_boxed_slice())
|
||||
}
|
||||
|
||||
pub fn write_file<P: AsRef<Path>>(path: P, data: &Box<[u8]>) -> Result<(), io::Error> {
|
||||
let mut file = try!(fs::File::create(path));
|
||||
try!(file.write(&data));
|
||||
let mut file = r#try!(fs::File::create(path));
|
||||
r#try!(file.write(&data));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -12,31 +12,34 @@ pub struct NoMBC {
|
||||
|
||||
impl NoMBC {
|
||||
pub fn new(rom: Box<[u8]>, ram: Box<[u8]>) -> NoMBC {
|
||||
NoMBC {
|
||||
rom: rom,
|
||||
ram: ram,
|
||||
}
|
||||
NoMBC { rom: rom, ram: ram }
|
||||
}
|
||||
}
|
||||
|
||||
impl MBC for NoMBC {
|
||||
fn dump_ram(&self, file: &String){
|
||||
fn dump_ram(&self, file: &String) {
|
||||
super::super::write_file(&file, &self.ram).expect("Saving failed");
|
||||
}
|
||||
|
||||
fn write_byte(&mut self, addr: u16, val: u8) {
|
||||
println!("Writing not supported for cartridges without MBC. (Tried to set {:04X} to {:02X})", addr, val);
|
||||
println!(
|
||||
"Writing not supported for cartridges without MBC. (Tried to set {:04X} to {:02X})",
|
||||
addr, val
|
||||
);
|
||||
}
|
||||
|
||||
fn read_byte(&self, addr: u16) -> u8 {
|
||||
match addr {
|
||||
0x0000 ... 0x7FFF => self.rom[addr as usize],
|
||||
0xA000 ... 0xBFFF => {
|
||||
0x0000..=0x7FFF => self.rom[addr as usize],
|
||||
0xA000..=0xBFFF => {
|
||||
// TODO: Check for ram
|
||||
let addr = (addr as usize) - 0xA000;
|
||||
|
||||
if addr >= self.ram.len() {
|
||||
println!("Tried to access {:04X}, however the memory is not present.", addr + 0xA000);
|
||||
println!(
|
||||
"Tried to access {:04X}, however the memory is not present.",
|
||||
addr + 0xA000
|
||||
);
|
||||
0
|
||||
} else {
|
||||
self.ram[addr]
|
||||
|
||||
@ -30,33 +30,33 @@ impl MBC1 {
|
||||
fn active_rom_bank(&self) -> u8 {
|
||||
match self.bank_mode {
|
||||
BankMode::RomBankMode => self.rom_bank_no | (self.bank_no_high << 5),
|
||||
BankMode::RamBankMode => self.rom_bank_no
|
||||
BankMode::RamBankMode => self.rom_bank_no,
|
||||
}
|
||||
}
|
||||
|
||||
fn active_ram_bank(&self) -> u8 {
|
||||
match self.bank_mode {
|
||||
BankMode::RomBankMode => 0,
|
||||
BankMode::RamBankMode => self.bank_no_high
|
||||
BankMode::RamBankMode => self.bank_no_high,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MBC for MBC1 {
|
||||
fn dump_ram(&self, file: &String){
|
||||
fn dump_ram(&self, file: &String) {
|
||||
super::super::write_file(&file, &self.ram).expect("Saving failed");
|
||||
}
|
||||
|
||||
fn read_byte(&self, addr: u16) -> u8 {
|
||||
match addr {
|
||||
0x0000 ... 0x3FFF => self.rom[addr as usize],
|
||||
0x4000 ... 0x7FFF => {
|
||||
0x0000..=0x3FFF => self.rom[addr as usize],
|
||||
0x4000..=0x7FFF => {
|
||||
let addr = addr - 0x4000;
|
||||
let abs_addr: usize = addr as usize + self.active_rom_bank() as usize * 0x4000;
|
||||
let val: u8 = self.rom[abs_addr];
|
||||
val
|
||||
},
|
||||
0xA000 ... 0xBFFF => {
|
||||
}
|
||||
0xA000..=0xBFFF => {
|
||||
let addr = addr - 0xA000;
|
||||
println!("Access [{:02X}] {:04X}", self.active_ram_bank(), addr);
|
||||
self.ram[self.active_ram_bank() as usize * 0x2000 + addr as usize]
|
||||
@ -69,14 +69,12 @@ impl MBC for MBC1 {
|
||||
|
||||
fn write_byte(&mut self, addr: u16, val: u8) {
|
||||
match addr {
|
||||
0x0000 ... 0x1FFF => {
|
||||
match val {
|
||||
0x0A => self.ram_enable = true,
|
||||
0x00 => self.ram_enable = false,
|
||||
_ => println!("Unknown MBC1 value {:02X} for {:04X}", val, addr)
|
||||
}
|
||||
0x0000..=0x1FFF => match val {
|
||||
0x0A => self.ram_enable = true,
|
||||
0x00 => self.ram_enable = false,
|
||||
_ => println!("Unknown MBC1 value {:02X} for {:04X}", val, addr),
|
||||
},
|
||||
0x2000 ... 0x3FFF => {
|
||||
0x2000..=0x3FFF => {
|
||||
if val != 0 {
|
||||
self.rom_bank_no = val & 0x1F;
|
||||
} else {
|
||||
@ -84,18 +82,18 @@ impl MBC for MBC1 {
|
||||
}
|
||||
println!("MBC1: Selecting bank {:02X}", self.rom_bank_no);
|
||||
}
|
||||
0x4000 ... 0x5FFF => {
|
||||
0x4000..=0x5FFF => {
|
||||
// Upper ROM bank / RAM bank select
|
||||
self.bank_no_high = val & 3;
|
||||
},
|
||||
0x6000 ... 0x7FFF => {
|
||||
}
|
||||
0x6000..=0x7FFF => {
|
||||
// Select upper ROM bytes or RAM bytes
|
||||
match val {
|
||||
0 => self.bank_mode = BankMode::RomBankMode,
|
||||
1 => self.bank_mode = BankMode::RamBankMode,
|
||||
_ => panic!("Invalid bank mode {:02X}", val),
|
||||
}
|
||||
},
|
||||
}
|
||||
_ => panic!("MBC1: Writing {:02X} to {:04X} not supported", val, addr),
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,23 +20,23 @@ impl MBC2 {
|
||||
fn active_rom_bank(&self) -> u8 {
|
||||
self.rom_bank_no
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MBC for MBC2 {
|
||||
fn dump_ram(&self, file: &String){
|
||||
fn dump_ram(&self, file: &String) {
|
||||
super::super::write_file(&file, &self.ram).expect("Saving failed");
|
||||
}
|
||||
|
||||
fn read_byte(&self, addr: u16) -> u8 {
|
||||
match addr {
|
||||
0x0000 ... 0x3FFF => self.rom[addr as usize],
|
||||
0x4000 ... 0x7FFF => {
|
||||
0x0000..=0x3FFF => self.rom[addr as usize],
|
||||
0x4000..=0x7FFF => {
|
||||
let addr = addr - 0x4000;
|
||||
let abs_addr: usize = addr as usize + self.active_rom_bank() as usize * 0x4000;
|
||||
let val: u8 = self.rom[abs_addr];
|
||||
val
|
||||
},
|
||||
0xA000 ... 0xA1FF => {
|
||||
}
|
||||
0xA000..=0xA1FF => {
|
||||
let addr = addr - 0xA000;
|
||||
self.ram[addr as usize] & 0x0F
|
||||
}
|
||||
@ -48,19 +48,19 @@ impl MBC for MBC2 {
|
||||
|
||||
fn write_byte(&mut self, addr: u16, val: u8) {
|
||||
match addr {
|
||||
0x0000 ... 0x1FFF => {
|
||||
0x0000..=0x1FFF => {
|
||||
// To enable the ram, the LSB of the higher byte must be 0
|
||||
if addr & 0x0100 == 0 {
|
||||
match val {
|
||||
0x0A => self.ram_enable = true,
|
||||
0x00 => self.ram_enable = false,
|
||||
_ => println!("Unknown MBC2 value {:02X} for {:04X}", val, addr)
|
||||
_ => println!("Unknown MBC2 value {:02X} for {:04X}", val, addr),
|
||||
}
|
||||
} else {
|
||||
println!("MBC2: Write {:02X} to {:04X} has no effect", val, addr);
|
||||
}
|
||||
},
|
||||
0x2000 ... 0x3FFF => {
|
||||
}
|
||||
0x2000..=0x3FFF => {
|
||||
if addr & 0x0100 == 1 {
|
||||
self.rom_bank_no = val & 0x0F;
|
||||
println!("MBC2: Selecting bank {:02X}", self.rom_bank_no);
|
||||
|
||||
@ -25,35 +25,37 @@ impl MBC3 {
|
||||
fn active_ram_bank(&self) -> u8 {
|
||||
self.ram_bank_no
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl MBC for MBC3 {
|
||||
fn dump_ram(&self, file: &String){
|
||||
fn dump_ram(&self, file: &String) {
|
||||
super::super::write_file(&file, &self.ram).expect("Saving failed");
|
||||
}
|
||||
|
||||
fn read_byte(&self, addr: u16) -> u8 {
|
||||
match addr {
|
||||
0x0000 ... 0x3FFF => self.rom[addr as usize],
|
||||
0x4000 ... 0x7FFF => {
|
||||
0x0000..=0x3FFF => self.rom[addr as usize],
|
||||
0x4000..=0x7FFF => {
|
||||
let addr = addr - 0x4000;
|
||||
let abs_addr: usize = addr as usize + self.active_rom_bank() as usize * 0x4000;
|
||||
let val: u8 = self.rom[abs_addr];
|
||||
val
|
||||
},
|
||||
0xA000 ... 0xBFFF => {
|
||||
}
|
||||
0xA000..=0xBFFF => {
|
||||
let addr = addr - 0xA000;
|
||||
match self.active_ram_bank() {
|
||||
0x00 ... 0x03 => {
|
||||
0x00..=0x03 => {
|
||||
self.ram[self.active_ram_bank() as usize * 0x2000 + addr as usize]
|
||||
}
|
||||
0x08 ... 0x0C => {
|
||||
0x08..=0x0C => {
|
||||
// TODO
|
||||
println!("MBC3: Ignoring RTC read");
|
||||
0
|
||||
}
|
||||
_ => panic!("MBC3: Accessing unknown RAM bank {:02X}", self.active_ram_bank())
|
||||
_ => panic!(
|
||||
"MBC3: Accessing unknown RAM bank {:02X}",
|
||||
self.active_ram_bank()
|
||||
),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
@ -64,42 +66,43 @@ impl MBC for MBC3 {
|
||||
|
||||
fn write_byte(&mut self, addr: u16, val: u8) {
|
||||
match addr {
|
||||
0x0000 ... 0x1FFF => {
|
||||
match val {
|
||||
0x0A => self.ram_rtc_enabled = true,
|
||||
0x00 => self.ram_rtc_enabled = false,
|
||||
_ => println!("MBC3: Unknown MBC value {:02X} for {:04X}", val, addr)
|
||||
}
|
||||
0x0000..=0x1FFF => match val {
|
||||
0x0A => self.ram_rtc_enabled = true,
|
||||
0x00 => self.ram_rtc_enabled = false,
|
||||
_ => println!("MBC3: Unknown MBC value {:02X} for {:04X}", val, addr),
|
||||
},
|
||||
0x2000 ... 0x3FFF => self.rom_bank_no = val & 0x7F,
|
||||
0x4000 ... 0x5FFF => {
|
||||
0x2000..=0x3FFF => self.rom_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,
|
||||
_ => panic!("MBC3: Unknown RAM bank {:02X}", val)
|
||||
0x00..=0x03 => self.ram_bank_no = val,
|
||||
0x08..=0x0C => self.ram_bank_no = val,
|
||||
_ => panic!("MBC3: Unknown RAM bank {:02X}", val),
|
||||
}
|
||||
},
|
||||
0x6000 ... 0x7FFF => {
|
||||
}
|
||||
0x6000..=0x7FFF => {
|
||||
// Latch clock data
|
||||
match val {
|
||||
0x00 => println!("latch = 0"),
|
||||
0x01 => println!("latch = 1"), // TODO: This should copy the current clock to the register
|
||||
_ => panic!("MBC3: Unknown latch value {:02X}", val)
|
||||
_ => panic!("MBC3: Unknown latch value {:02X}", val),
|
||||
}
|
||||
},
|
||||
0xA000 ... 0xBFFF => {
|
||||
}
|
||||
0xA000..=0xBFFF => {
|
||||
let addr = addr - 0xA000;
|
||||
match self.active_ram_bank() {
|
||||
0x00 ... 0x03 => {
|
||||
0x00..=0x03 => {
|
||||
let active_bank = self.active_ram_bank() as usize;
|
||||
self.ram[active_bank * 0x2000 + addr as usize] = val;
|
||||
}
|
||||
0x08 ... 0x0C => {
|
||||
0x08..=0x0C => {
|
||||
// TODO
|
||||
println!("MBC3: Ignoring RTC write ({:02X})", val);
|
||||
}
|
||||
_ => panic!("MBC3: Writing unknown RAM bank {:02X}", self.active_ram_bank())
|
||||
_ => panic!(
|
||||
"MBC3: Writing unknown RAM bank {:02X}",
|
||||
self.active_ram_bank()
|
||||
),
|
||||
}
|
||||
}
|
||||
_ => panic!("MBC3: Writing {:02X} to {:04X} not supported", val, addr),
|
||||
|
||||
@ -36,7 +36,7 @@ impl Sound {
|
||||
0xFF26 => self.enabled = val,
|
||||
_ => {
|
||||
// println!("Sound: Write {:02X} to {:04X} unsupported", val, addr);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -27,7 +27,8 @@ impl Timer {
|
||||
self.div_tick_counter -= TIMER_SPEED[3];
|
||||
}
|
||||
|
||||
if (self.tac & TIMER_ENABLE) == TIMER_ENABLE { // Is timer enabled?
|
||||
if (self.tac & TIMER_ENABLE) == TIMER_ENABLE {
|
||||
// Is timer enabled?
|
||||
self.tick_counter += 1;
|
||||
let req_ticks = TIMER_SPEED[(self.tac & 3) as usize];
|
||||
|
||||
@ -72,7 +73,7 @@ impl Timer {
|
||||
_ => {
|
||||
println!("Timer: Read from {:04X} unsupported", addr);
|
||||
0
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user