Merge branch 'master' into audio
This commit is contained in:
commit
57b8438144
@ -4,6 +4,6 @@ version = "0.1.0"
|
|||||||
authors = ["Kevin Hamacher <kevin.hamacher@rub.de>"]
|
authors = ["Kevin Hamacher <kevin.hamacher@rub.de>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
sdl2 = "0.19.0"
|
sdl2 = "*"
|
||||||
libc = "0.2.14"
|
libc = "*"
|
||||||
pulse-simple = "1.0.0"
|
pulse-simple = "*"
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
use super::mbc::mbc::MBC;
|
use super::mbc::mbc::MBC;
|
||||||
|
|
||||||
#[derive(Debug,PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
enum MemoryBankControllerType {
|
enum MemoryBankControllerType {
|
||||||
|
None,
|
||||||
MBC1,
|
MBC1,
|
||||||
MBC2,
|
MBC2,
|
||||||
MBC3,
|
MBC3,
|
||||||
@ -17,17 +18,17 @@ enum RamSize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Cartridge {
|
pub struct Cartridge {
|
||||||
mbc: Box<super::mbc::mbc::MBC>,
|
mbc: Box<dyn super::mbc::mbc::MBC>,
|
||||||
savefile: Option<String>,
|
savefile: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cartridge {
|
impl Cartridge {
|
||||||
pub fn new(rom: Box<[u8]>, save_file: Option<String>) -> Cartridge {
|
pub fn new(rom: Box<[u8]>, save_file: Option<String>) -> Cartridge {
|
||||||
let mbc_type = match rom[0x0147] {
|
let mbc_type: MemoryBankControllerType = match rom[0x0147] {
|
||||||
0x00 | 0x08 ... 0x09 => None,
|
0x00 | 0x08..=0x09 => MemoryBankControllerType::None,
|
||||||
0x01 ... 0x03 => Some(MemoryBankControllerType::MBC1),
|
0x01..=0x03 => MemoryBankControllerType::MBC1,
|
||||||
0x05 ... 0x06 => Some(MemoryBankControllerType::MBC2),
|
0x05..=0x06 => MemoryBankControllerType::MBC2,
|
||||||
0x0F ... 0x13 => Some(MemoryBankControllerType::MBC3),
|
0x0F..=0x13 => MemoryBankControllerType::MBC3,
|
||||||
// 0xFF => MemoryBankControllerType::HuC1,
|
// 0xFF => MemoryBankControllerType::HuC1,
|
||||||
_ => panic!("Unsupported MBC type: {:02X}", rom[0x0147]),
|
_ => panic!("Unsupported MBC type: {:02X}", rom[0x0147]),
|
||||||
};
|
};
|
||||||
@ -36,11 +37,11 @@ impl Cartridge {
|
|||||||
|
|
||||||
let rom_banks: u16 = match rom[0x0148] {
|
let rom_banks: u16 = match rom[0x0148] {
|
||||||
0x00 => 0,
|
0x00 => 0,
|
||||||
0x01 ... 0x07 => 2u16.pow(rom[0x0148] as u32 + 1),
|
0x01..=0x07 => 2u16.pow(rom[0x0148] as u32 + 1),
|
||||||
0x52 => 72,
|
0x52 => 72,
|
||||||
0x53 => 80,
|
0x53 => 80,
|
||||||
0x54 => 96,
|
0x54 => 96,
|
||||||
_ => panic!("Unknown rom size: {:?}", rom[0x148])
|
_ => panic!("Unknown rom size: {:?}", rom[0x148]),
|
||||||
};
|
};
|
||||||
|
|
||||||
let ram_size: RamSize = match rom[0x0149] {
|
let ram_size: RamSize = match rom[0x0149] {
|
||||||
@ -48,7 +49,7 @@ impl Cartridge {
|
|||||||
0x01 => RamSize::Ram2KB,
|
0x01 => RamSize::Ram2KB,
|
||||||
0x02 => RamSize::Ram8KB,
|
0x02 => RamSize::Ram8KB,
|
||||||
0x03 => RamSize::Ram32KB,
|
0x03 => RamSize::Ram32KB,
|
||||||
_ => panic!("Unknown ram size: {:?}", rom[0x149])
|
_ => panic!("Unknown ram size: {:?}", rom[0x149]),
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("Rom size: {} banks", rom_banks);
|
println!("Rom size: {} banks", rom_banks);
|
||||||
@ -56,11 +57,11 @@ impl Cartridge {
|
|||||||
|
|
||||||
let ram = Cartridge::load_savefile(&save_file, ram_size);
|
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 {
|
||||||
None => Box::new(super::mbc::mbc::NoMBC::new(rom, ram)),
|
MemoryBankControllerType::None => Box::new(super::mbc::mbc::NoMBC::new(rom, ram)),
|
||||||
Some(MemoryBankControllerType::MBC1) => Box::new(super::mbc::mbc1::MBC1::new(rom, ram)),
|
MemoryBankControllerType::MBC1 => Box::new(super::mbc::mbc1::MBC1::new(rom, ram)),
|
||||||
Some(MemoryBankControllerType::MBC2) => Box::new(super::mbc::mbc2::MBC2::new(rom, ram)),
|
MemoryBankControllerType::MBC2 => Box::new(super::mbc::mbc2::MBC2::new(rom, ram)),
|
||||||
Some(MemoryBankControllerType::MBC3) => Box::new(super::mbc::mbc3::MBC3::new(rom, ram)),
|
MemoryBankControllerType::MBC3 => Box::new(super::mbc::mbc3::MBC3::new(rom, ram)),
|
||||||
};
|
};
|
||||||
|
|
||||||
Cartridge {
|
Cartridge {
|
||||||
|
|||||||
273
src/cpu.rs
273
src/cpu.rs
@ -1,7 +1,7 @@
|
|||||||
use super::interconnect;
|
use super::interconnect;
|
||||||
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
const REG_A: usize = 6;
|
const REG_A: usize = 6;
|
||||||
|
|
||||||
@ -21,7 +21,6 @@ const FLAG_N: u8 = 1 << 6;
|
|||||||
const FLAG_H: u8 = 1 << 5;
|
const FLAG_H: u8 = 1 << 5;
|
||||||
const FLAG_C: u8 = 1 << 4;
|
const FLAG_C: u8 = 1 << 4;
|
||||||
|
|
||||||
|
|
||||||
pub struct CPU {
|
pub struct CPU {
|
||||||
// Registers: B, C, D, E, H, L, A
|
// Registers: B, C, D, E, H, L, A
|
||||||
regs: [u8; 7],
|
regs: [u8; 7],
|
||||||
@ -40,7 +39,7 @@ pub struct CPU {
|
|||||||
fn to_u16(args: Args) -> u16 {
|
fn to_u16(args: Args) -> u16 {
|
||||||
match args {
|
match args {
|
||||||
Args::Double(a, b) => (a as u16) | ((b as u16) << 8),
|
Args::Double(a, b) => (a as u16) | ((b as u16) << 8),
|
||||||
_ => panic!("to_u16 only works with Args::Double")
|
_ => panic!("to_u16 only works with Args::Double"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +91,7 @@ impl CPU {
|
|||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
Args::Double(b1, b2)
|
Args::Double(b1, b2)
|
||||||
}
|
}
|
||||||
_ => panic!("load_args only supports two bytes")
|
_ => panic!("load_args only supports two bytes"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +176,10 @@ impl CPU {
|
|||||||
self.set_flag(FLAG_N);
|
self.set_flag(FLAG_N);
|
||||||
self.set_clear_flag(FLAG_Z, new == 0);
|
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_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]
|
#[inline]
|
||||||
@ -211,7 +213,7 @@ impl CPU {
|
|||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
|
|
||||||
match instruction {
|
match instruction {
|
||||||
0x00 ... 0x07 => {
|
0x00..=0x07 => {
|
||||||
let reg_id = (instruction - 0x00) as usize;
|
let reg_id = (instruction - 0x00) as usize;
|
||||||
let val = self.get_8bit_reg(reg_id);
|
let val = self.get_8bit_reg(reg_id);
|
||||||
if self.debug {
|
if self.debug {
|
||||||
@ -230,8 +232,8 @@ impl CPU {
|
|||||||
self.set_clear_flag(FLAG_Z, nval == 0);
|
self.set_clear_flag(FLAG_Z, nval == 0);
|
||||||
self.clear_flag(FLAG_N);
|
self.clear_flag(FLAG_N);
|
||||||
self.clear_flag(FLAG_H);
|
self.clear_flag(FLAG_H);
|
||||||
},
|
}
|
||||||
0x08 ... 0x0F => {
|
0x08..=0x0F => {
|
||||||
let reg_id = (instruction - 0x08) as usize;
|
let reg_id = (instruction - 0x08) as usize;
|
||||||
let val = self.get_8bit_reg(reg_id);
|
let val = self.get_8bit_reg(reg_id);
|
||||||
if self.debug {
|
if self.debug {
|
||||||
@ -248,8 +250,8 @@ impl CPU {
|
|||||||
self.set_clear_flag(FLAG_Z, val == 0);
|
self.set_clear_flag(FLAG_Z, val == 0);
|
||||||
self.clear_flag(FLAG_N);
|
self.clear_flag(FLAG_N);
|
||||||
self.clear_flag(FLAG_H);
|
self.clear_flag(FLAG_H);
|
||||||
},
|
}
|
||||||
0x10 ... 0x17 => {
|
0x10..=0x17 => {
|
||||||
let reg_id = (instruction - 0x10) as usize;
|
let reg_id = (instruction - 0x10) as usize;
|
||||||
let val = self.get_8bit_reg(reg_id);
|
let val = self.get_8bit_reg(reg_id);
|
||||||
if self.debug {
|
if self.debug {
|
||||||
@ -268,8 +270,9 @@ impl CPU {
|
|||||||
self.set_clear_flag(FLAG_Z, nval == 0);
|
self.set_clear_flag(FLAG_Z, nval == 0);
|
||||||
self.clear_flag(FLAG_N);
|
self.clear_flag(FLAG_N);
|
||||||
self.clear_flag(FLAG_H);
|
self.clear_flag(FLAG_H);
|
||||||
},
|
}
|
||||||
0x18 ... 0x1F => { // RR
|
0x18..=0x1F => {
|
||||||
|
// RR
|
||||||
let reg_id = (instruction - 0x18) as usize;
|
let reg_id = (instruction - 0x18) as usize;
|
||||||
let val = self.get_8bit_reg(reg_id);
|
let val = self.get_8bit_reg(reg_id);
|
||||||
if self.debug {
|
if self.debug {
|
||||||
@ -288,8 +291,8 @@ impl CPU {
|
|||||||
self.set_clear_flag(FLAG_Z, v == 0);
|
self.set_clear_flag(FLAG_Z, v == 0);
|
||||||
self.clear_flag(FLAG_N);
|
self.clear_flag(FLAG_N);
|
||||||
self.clear_flag(FLAG_H);
|
self.clear_flag(FLAG_H);
|
||||||
},
|
}
|
||||||
0x20 ... 0x27 => {
|
0x20..=0x27 => {
|
||||||
let reg_id = (instruction - 0x20) as usize;
|
let reg_id = (instruction - 0x20) as usize;
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("SLA {}", REG_NAMES[reg_id]);
|
println!("SLA {}", REG_NAMES[reg_id]);
|
||||||
@ -299,8 +302,8 @@ impl CPU {
|
|||||||
self.set_clear_flag(FLAG_C, v & 0x80 == 0x80);
|
self.set_clear_flag(FLAG_C, v & 0x80 == 0x80);
|
||||||
self.set_clear_flag(FLAG_Z, v & 0x7F == 0);
|
self.set_clear_flag(FLAG_Z, v & 0x7F == 0);
|
||||||
self.set_8bit_reg(reg_id, v << 1);
|
self.set_8bit_reg(reg_id, v << 1);
|
||||||
},
|
}
|
||||||
0x28 ... 0x2F => {
|
0x28..=0x2F => {
|
||||||
let reg_id = (instruction - 0x28) as usize;
|
let reg_id = (instruction - 0x28) as usize;
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("SRA {}", REG_NAMES[reg_id]);
|
println!("SRA {}", REG_NAMES[reg_id]);
|
||||||
@ -311,8 +314,8 @@ impl CPU {
|
|||||||
self.flags = 0;
|
self.flags = 0;
|
||||||
self.set_clear_flag(FLAG_Z, nv == 0);
|
self.set_clear_flag(FLAG_Z, nv == 0);
|
||||||
self.set_clear_flag(FLAG_C, v & 1 == 1);
|
self.set_clear_flag(FLAG_C, v & 1 == 1);
|
||||||
},
|
}
|
||||||
0x30 ... 0x37 => {
|
0x30..=0x37 => {
|
||||||
let reg_id = (instruction - 0x30) as usize;
|
let reg_id = (instruction - 0x30) as usize;
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("SWAP {}", REG_NAMES[reg_id]);
|
println!("SWAP {}", REG_NAMES[reg_id]);
|
||||||
@ -321,8 +324,8 @@ impl CPU {
|
|||||||
self.set_8bit_reg(reg_id, (v << 4) | (v >> 4));
|
self.set_8bit_reg(reg_id, (v << 4) | (v >> 4));
|
||||||
self.flags = 0;
|
self.flags = 0;
|
||||||
self.set_clear_flag(FLAG_Z, v == 0);
|
self.set_clear_flag(FLAG_Z, v == 0);
|
||||||
},
|
}
|
||||||
0x38 ... 0x3F => {
|
0x38..=0x3F => {
|
||||||
let reg_id = (instruction - 0x38) as usize;
|
let reg_id = (instruction - 0x38) as usize;
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("SRL {}", REG_NAMES[reg_id]);
|
println!("SRL {}", REG_NAMES[reg_id]);
|
||||||
@ -333,10 +336,10 @@ impl CPU {
|
|||||||
self.clear_flag(FLAG_N);
|
self.clear_flag(FLAG_N);
|
||||||
self.clear_flag(FLAG_H);
|
self.clear_flag(FLAG_H);
|
||||||
self.set_clear_flag(FLAG_Z, (v & 0xFE) == 0);
|
self.set_clear_flag(FLAG_Z, (v & 0xFE) == 0);
|
||||||
},
|
}
|
||||||
|
|
||||||
// Bits
|
// Bits
|
||||||
0x40 ... 0x47 => {
|
0x40..=0x47 => {
|
||||||
// Test 0th bit
|
// Test 0th bit
|
||||||
let reg_id = (instruction - 0x40) as usize;
|
let reg_id = (instruction - 0x40) as usize;
|
||||||
let reg_content = self.get_8bit_reg(reg_id);
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
@ -347,7 +350,7 @@ impl CPU {
|
|||||||
self.clear_flag(FLAG_N);
|
self.clear_flag(FLAG_N);
|
||||||
self.set_flag(FLAG_H);
|
self.set_flag(FLAG_H);
|
||||||
}
|
}
|
||||||
0x48 ... 0x4F => {
|
0x48..=0x4F => {
|
||||||
// Test 1th bit
|
// Test 1th bit
|
||||||
let reg_id = (instruction - 0x48) as usize;
|
let reg_id = (instruction - 0x48) as usize;
|
||||||
let reg_content = self.get_8bit_reg(reg_id);
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
@ -358,7 +361,7 @@ impl CPU {
|
|||||||
self.clear_flag(FLAG_N);
|
self.clear_flag(FLAG_N);
|
||||||
self.set_flag(FLAG_H);
|
self.set_flag(FLAG_H);
|
||||||
}
|
}
|
||||||
0x50 ... 0x57 => {
|
0x50..=0x57 => {
|
||||||
// Test 2th bit
|
// Test 2th bit
|
||||||
let reg_id = (instruction - 0x50) as usize;
|
let reg_id = (instruction - 0x50) as usize;
|
||||||
let reg_content = self.get_8bit_reg(reg_id);
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
@ -369,7 +372,7 @@ impl CPU {
|
|||||||
self.clear_flag(FLAG_N);
|
self.clear_flag(FLAG_N);
|
||||||
self.set_flag(FLAG_H);
|
self.set_flag(FLAG_H);
|
||||||
}
|
}
|
||||||
0x58 ... 0x5F => {
|
0x58..=0x5F => {
|
||||||
// Test 3th bit
|
// Test 3th bit
|
||||||
let reg_id = (instruction - 0x58) as usize;
|
let reg_id = (instruction - 0x58) as usize;
|
||||||
let reg_content = self.get_8bit_reg(reg_id);
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
@ -380,7 +383,7 @@ impl CPU {
|
|||||||
self.clear_flag(FLAG_N);
|
self.clear_flag(FLAG_N);
|
||||||
self.set_flag(FLAG_H);
|
self.set_flag(FLAG_H);
|
||||||
}
|
}
|
||||||
0x60 ... 0x67 => {
|
0x60..=0x67 => {
|
||||||
// Test 4th bit
|
// Test 4th bit
|
||||||
let reg_id = (instruction - 0x60) as usize;
|
let reg_id = (instruction - 0x60) as usize;
|
||||||
let reg_content = self.get_8bit_reg(reg_id);
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
@ -391,7 +394,7 @@ impl CPU {
|
|||||||
self.clear_flag(FLAG_N);
|
self.clear_flag(FLAG_N);
|
||||||
self.set_flag(FLAG_H);
|
self.set_flag(FLAG_H);
|
||||||
}
|
}
|
||||||
0x68 ... 0x6F => {
|
0x68..=0x6F => {
|
||||||
// Test 5th bit
|
// Test 5th bit
|
||||||
let reg_id = (instruction - 0x68) as usize;
|
let reg_id = (instruction - 0x68) as usize;
|
||||||
let reg_content = self.get_8bit_reg(reg_id);
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
@ -402,7 +405,7 @@ impl CPU {
|
|||||||
self.clear_flag(FLAG_N);
|
self.clear_flag(FLAG_N);
|
||||||
self.set_flag(FLAG_H);
|
self.set_flag(FLAG_H);
|
||||||
}
|
}
|
||||||
0x70 ... 0x77 => {
|
0x70..=0x77 => {
|
||||||
// Test 6th bit
|
// Test 6th bit
|
||||||
let reg_id = (instruction - 0x70) as usize;
|
let reg_id = (instruction - 0x70) as usize;
|
||||||
let reg_content = self.get_8bit_reg(reg_id);
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
@ -413,7 +416,7 @@ impl CPU {
|
|||||||
self.clear_flag(FLAG_N);
|
self.clear_flag(FLAG_N);
|
||||||
self.set_flag(FLAG_H);
|
self.set_flag(FLAG_H);
|
||||||
}
|
}
|
||||||
0x78 ... 0x7F => {
|
0x78..=0x7F => {
|
||||||
// Test 7th bit
|
// Test 7th bit
|
||||||
let reg_id = (instruction - 0x78) as usize;
|
let reg_id = (instruction - 0x78) as usize;
|
||||||
let reg_content = self.get_8bit_reg(reg_id);
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
@ -426,7 +429,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reset bits
|
// Reset bits
|
||||||
0x80 ... 0x87 => {
|
0x80..=0x87 => {
|
||||||
// Reset 0th bit
|
// Reset 0th bit
|
||||||
let reg_id = (instruction - 0x80) as usize;
|
let reg_id = (instruction - 0x80) as usize;
|
||||||
let reg_content = self.get_8bit_reg(reg_id);
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
@ -435,7 +438,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
self.set_8bit_reg(reg_id, reg_content & !(1 << 0));
|
self.set_8bit_reg(reg_id, reg_content & !(1 << 0));
|
||||||
}
|
}
|
||||||
0x88 ... 0x8F => {
|
0x88..=0x8F => {
|
||||||
// Reset 1th bit
|
// Reset 1th bit
|
||||||
let reg_id = (instruction - 0x88) as usize;
|
let reg_id = (instruction - 0x88) as usize;
|
||||||
let reg_content = self.get_8bit_reg(reg_id);
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
@ -444,7 +447,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
self.set_8bit_reg(reg_id, reg_content & !(1 << 1));
|
self.set_8bit_reg(reg_id, reg_content & !(1 << 1));
|
||||||
}
|
}
|
||||||
0x90 ... 0x97 => {
|
0x90..=0x97 => {
|
||||||
// Reset 2nd bit
|
// Reset 2nd bit
|
||||||
let reg_id = (instruction - 0x90) as usize;
|
let reg_id = (instruction - 0x90) as usize;
|
||||||
let reg_content = self.get_8bit_reg(reg_id);
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
@ -453,7 +456,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
self.set_8bit_reg(reg_id, reg_content & !(1 << 2));
|
self.set_8bit_reg(reg_id, reg_content & !(1 << 2));
|
||||||
}
|
}
|
||||||
0x98 ... 0x9F => {
|
0x98..=0x9F => {
|
||||||
// Reset 3th bit
|
// Reset 3th bit
|
||||||
let reg_id = (instruction - 0x98) as usize;
|
let reg_id = (instruction - 0x98) as usize;
|
||||||
let reg_content = self.get_8bit_reg(reg_id);
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
@ -462,7 +465,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
self.set_8bit_reg(reg_id, reg_content & !(1 << 3));
|
self.set_8bit_reg(reg_id, reg_content & !(1 << 3));
|
||||||
}
|
}
|
||||||
0xA0 ... 0xA7 => {
|
0xA0..=0xA7 => {
|
||||||
// Reset 4th bit
|
// Reset 4th bit
|
||||||
let reg_id = (instruction - 0xA0) as usize;
|
let reg_id = (instruction - 0xA0) as usize;
|
||||||
let reg_content = self.get_8bit_reg(reg_id);
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
@ -471,7 +474,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
self.set_8bit_reg(reg_id, reg_content & !(1 << 4));
|
self.set_8bit_reg(reg_id, reg_content & !(1 << 4));
|
||||||
}
|
}
|
||||||
0xA8 ... 0xAF => {
|
0xA8..=0xAF => {
|
||||||
// Reset 5th bit
|
// Reset 5th bit
|
||||||
let reg_id = (instruction - 0xA8) as usize;
|
let reg_id = (instruction - 0xA8) as usize;
|
||||||
let reg_content = self.get_8bit_reg(reg_id);
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
@ -480,7 +483,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
self.set_8bit_reg(reg_id, reg_content & !(1 << 5));
|
self.set_8bit_reg(reg_id, reg_content & !(1 << 5));
|
||||||
}
|
}
|
||||||
0xB0 ... 0xB7 => {
|
0xB0..=0xB7 => {
|
||||||
// Reset 6th bit
|
// Reset 6th bit
|
||||||
let reg_id = (instruction - 0xB0) as usize;
|
let reg_id = (instruction - 0xB0) as usize;
|
||||||
let reg_content = self.get_8bit_reg(reg_id);
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
@ -489,7 +492,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
self.set_8bit_reg(reg_id, reg_content & !(1 << 6));
|
self.set_8bit_reg(reg_id, reg_content & !(1 << 6));
|
||||||
}
|
}
|
||||||
0xB8 ... 0xBF => {
|
0xB8..=0xBF => {
|
||||||
// Reset 7th bit
|
// Reset 7th bit
|
||||||
let reg_id = (instruction - 0xB8) as usize;
|
let reg_id = (instruction - 0xB8) as usize;
|
||||||
let reg_content = self.get_8bit_reg(reg_id);
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
@ -500,7 +503,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set bits
|
// Set bits
|
||||||
0xC0 ... 0xC7 => {
|
0xC0..=0xC7 => {
|
||||||
// Set 0th bit
|
// Set 0th bit
|
||||||
let reg_id = (instruction - 0xC0) as usize;
|
let reg_id = (instruction - 0xC0) as usize;
|
||||||
let reg_content = self.get_8bit_reg(reg_id);
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
@ -509,7 +512,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
self.set_8bit_reg(reg_id, reg_content | (1 << 0));
|
self.set_8bit_reg(reg_id, reg_content | (1 << 0));
|
||||||
}
|
}
|
||||||
0xC8 ... 0xCF => {
|
0xC8..=0xCF => {
|
||||||
// Set 1th bit
|
// Set 1th bit
|
||||||
let reg_id = (instruction - 0xC8) as usize;
|
let reg_id = (instruction - 0xC8) as usize;
|
||||||
let reg_content = self.get_8bit_reg(reg_id);
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
@ -518,7 +521,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
self.set_8bit_reg(reg_id, reg_content | (1 << 1));
|
self.set_8bit_reg(reg_id, reg_content | (1 << 1));
|
||||||
}
|
}
|
||||||
0xD0 ... 0xD7 => {
|
0xD0..=0xD7 => {
|
||||||
// Set 2nd bit
|
// Set 2nd bit
|
||||||
let reg_id = (instruction - 0xD0) as usize;
|
let reg_id = (instruction - 0xD0) as usize;
|
||||||
let reg_content = self.get_8bit_reg(reg_id);
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
@ -527,7 +530,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
self.set_8bit_reg(reg_id, reg_content | (1 << 2));
|
self.set_8bit_reg(reg_id, reg_content | (1 << 2));
|
||||||
}
|
}
|
||||||
0xD8 ... 0xDF => {
|
0xD8..=0xDF => {
|
||||||
// Set 3th bit
|
// Set 3th bit
|
||||||
let reg_id = (instruction - 0xD8) as usize;
|
let reg_id = (instruction - 0xD8) as usize;
|
||||||
let reg_content = self.get_8bit_reg(reg_id);
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
@ -536,7 +539,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
self.set_8bit_reg(reg_id, reg_content | (1 << 3));
|
self.set_8bit_reg(reg_id, reg_content | (1 << 3));
|
||||||
}
|
}
|
||||||
0xE0 ... 0xE7 => {
|
0xE0..=0xE7 => {
|
||||||
// Set 4th bit
|
// Set 4th bit
|
||||||
let reg_id = (instruction - 0xE0) as usize;
|
let reg_id = (instruction - 0xE0) as usize;
|
||||||
let reg_content = self.get_8bit_reg(reg_id);
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
@ -545,7 +548,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
self.set_8bit_reg(reg_id, reg_content | (1 << 4));
|
self.set_8bit_reg(reg_id, reg_content | (1 << 4));
|
||||||
}
|
}
|
||||||
0xE8 ... 0xEF => {
|
0xE8..=0xEF => {
|
||||||
// Set 5th bit
|
// Set 5th bit
|
||||||
let reg_id = (instruction - 0xE8) as usize;
|
let reg_id = (instruction - 0xE8) as usize;
|
||||||
let reg_content = self.get_8bit_reg(reg_id);
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
@ -554,7 +557,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
self.set_8bit_reg(reg_id, reg_content | (1 << 5));
|
self.set_8bit_reg(reg_id, reg_content | (1 << 5));
|
||||||
}
|
}
|
||||||
0xF0 ... 0xF7 => {
|
0xF0..=0xF7 => {
|
||||||
// Set 6th bit
|
// Set 6th bit
|
||||||
let reg_id = (instruction - 0xF0) as usize;
|
let reg_id = (instruction - 0xF0) as usize;
|
||||||
let reg_content = self.get_8bit_reg(reg_id);
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
@ -563,7 +566,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
self.set_8bit_reg(reg_id, reg_content | (1 << 6));
|
self.set_8bit_reg(reg_id, reg_content | (1 << 6));
|
||||||
}
|
}
|
||||||
0xF8 ... 0xFF => {
|
0xF8..=0xFF => {
|
||||||
// Set 7th bit
|
// Set 7th bit
|
||||||
let reg_id = (instruction - 0xF8) as usize;
|
let reg_id = (instruction - 0xF8) as usize;
|
||||||
let reg_content = self.get_8bit_reg(reg_id);
|
let reg_content = self.get_8bit_reg(reg_id);
|
||||||
@ -787,7 +790,10 @@ impl CPU {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn add_rr_rr(&mut self, r1: usize, r2: usize, r3: usize, r4: usize) -> u8 {
|
fn add_rr_rr(&mut self, r1: usize, r2: usize, r3: usize, r4: usize) -> u8 {
|
||||||
if self.debug {
|
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);
|
let val1 = self.get_pair_value(r1, r2);
|
||||||
@ -851,7 +857,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn int_(&mut self, val: u8){
|
fn int_(&mut self, val: u8) {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("INT {:02X}", val);
|
println!("INT {:02X}", val);
|
||||||
}
|
}
|
||||||
@ -913,7 +919,10 @@ impl CPU {
|
|||||||
// println!("[+] [{}] {} cycles, gb: {}, we: {}", running_sum, cycles_executed, gb_dur, our_dur);
|
// println!("[+] [{}] {} cycles, gb: {}, we: {}", running_sum, cycles_executed, gb_dur, our_dur);
|
||||||
sleep(Duration::new(0, (full_cycles - 10) as u32 * 238 as u32));
|
sleep(Duration::new(0, (full_cycles - 10) as u32 * 238 as u32));
|
||||||
} else {
|
} else {
|
||||||
println!("[-] [{}] {} cycles, gb: {}, we: {}", running_sum, cycles_executed, gb_dur, our_dur);
|
println!(
|
||||||
|
"[-] [{}] {} cycles, gb: {}, we: {}",
|
||||||
|
running_sum, cycles_executed, gb_dur, our_dur
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -984,8 +993,11 @@ impl CPU {
|
|||||||
// We need to double-check the flags
|
// We need to double-check the flags
|
||||||
instruction = self.read_byte(self.ip);
|
instruction = self.read_byte(self.ip);
|
||||||
if self.debug {
|
if self.debug {
|
||||||
print!("{:#06x}: [SP: {:#04X}] i={:02X}. ", &self.ip, &self.sp, &instruction);
|
print!(
|
||||||
for i in 0 .. 6 {
|
"{:#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!("{}: {:02X} ", REG_NAMES[i], self.get_8bit_reg(i));
|
||||||
}
|
}
|
||||||
print!("A: {:02X} ", self.regs[REG_A]);
|
print!("A: {:02X} ", self.regs[REG_A]);
|
||||||
@ -1006,7 +1018,7 @@ impl CPU {
|
|||||||
println!("NOP");
|
println!("NOP");
|
||||||
}
|
}
|
||||||
4
|
4
|
||||||
},
|
}
|
||||||
0x01 => self.ld_rr_vv(REG_N_B, REG_N_C),
|
0x01 => self.ld_rr_vv(REG_N_B, REG_N_C),
|
||||||
0x02 => self.ld_dref_rr_a(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),
|
0x03 => self.inc_rr(REG_N_B, REG_N_C),
|
||||||
@ -1032,7 +1044,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
0x08 => {
|
0x08 => {
|
||||||
let a: u16 = to_u16(self.load_args(2));
|
let a: u16 = to_u16(self.load_args(2));
|
||||||
if self.debug{
|
if self.debug {
|
||||||
println!("LD ({:04X}), sp", a);
|
println!("LD ({:04X}), sp", a);
|
||||||
}
|
}
|
||||||
self.interconnect.write_word(a, self.sp);
|
self.interconnect.write_word(a, self.sp);
|
||||||
@ -1043,9 +1055,11 @@ impl CPU {
|
|||||||
if self.debug {
|
if self.debug {
|
||||||
println!("LD A, (BC)");
|
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
|
8
|
||||||
},
|
}
|
||||||
0x0B => self.dec_rr(REG_N_B, REG_N_C),
|
0x0B => self.dec_rr(REG_N_B, REG_N_C),
|
||||||
0x0C => self.reg_inc(REG_N_C),
|
0x0C => self.reg_inc(REG_N_C),
|
||||||
0x0D => self.reg_dec(REG_N_C),
|
0x0D => self.reg_dec(REG_N_C),
|
||||||
@ -1068,9 +1082,12 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
0x10 => {
|
0x10 => {
|
||||||
println!("STOP 0 {:02X} not implemented.", self.load_args(1).single_val());
|
println!(
|
||||||
|
"STOP 0 {:02X} not implemented.",
|
||||||
|
self.load_args(1).single_val()
|
||||||
|
);
|
||||||
4
|
4
|
||||||
},
|
}
|
||||||
0x11 => self.ld_rr_vv(REG_N_D, REG_N_E),
|
0x11 => self.ld_rr_vv(REG_N_D, REG_N_E),
|
||||||
0x12 => self.ld_dref_rr_a(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),
|
0x13 => self.inc_rr(REG_N_D, REG_N_E),
|
||||||
@ -1093,7 +1110,7 @@ impl CPU {
|
|||||||
self.clear_flag(FLAG_N);
|
self.clear_flag(FLAG_N);
|
||||||
self.clear_flag(FLAG_H);
|
self.clear_flag(FLAG_H);
|
||||||
4
|
4
|
||||||
},
|
}
|
||||||
0x18 => {
|
0x18 => {
|
||||||
let dst = self.load_args(1).single_val();
|
let dst = self.load_args(1).single_val();
|
||||||
self.jmp_r(dst);
|
self.jmp_r(dst);
|
||||||
@ -1101,15 +1118,17 @@ impl CPU {
|
|||||||
println!("JMPR {:02X}", dst);
|
println!("JMPR {:02X}", dst);
|
||||||
}
|
}
|
||||||
12
|
12
|
||||||
},
|
}
|
||||||
0x19 => self.add_rr_rr(REG_N_H, REG_N_L, REG_N_D, REG_N_E),
|
0x19 => self.add_rr_rr(REG_N_H, REG_N_L, REG_N_D, REG_N_E),
|
||||||
0x1A => {
|
0x1A => {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("LD A, (DE)");
|
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
|
8
|
||||||
},
|
}
|
||||||
0x1B => self.dec_rr(REG_N_D, REG_N_E),
|
0x1B => self.dec_rr(REG_N_D, REG_N_E),
|
||||||
0x1C => self.reg_inc(REG_N_E),
|
0x1C => self.reg_inc(REG_N_E),
|
||||||
0x1D => self.reg_dec(REG_N_E),
|
0x1D => self.reg_dec(REG_N_E),
|
||||||
@ -1130,8 +1149,7 @@ impl CPU {
|
|||||||
self.clear_flag(FLAG_N);
|
self.clear_flag(FLAG_N);
|
||||||
self.clear_flag(FLAG_H);
|
self.clear_flag(FLAG_H);
|
||||||
4
|
4
|
||||||
},
|
}
|
||||||
|
|
||||||
|
|
||||||
0x20 => {
|
0x20 => {
|
||||||
let c = self.flags & FLAG_Z == 0;
|
let c = self.flags & FLAG_Z == 0;
|
||||||
@ -1146,7 +1164,7 @@ impl CPU {
|
|||||||
self.interconnect.write_byte(addr, self.regs[REG_A]);
|
self.interconnect.write_byte(addr, self.regs[REG_A]);
|
||||||
self.set_pair_value(REG_N_H, REG_N_L, addr.wrapping_add(1));
|
self.set_pair_value(REG_N_H, REG_N_L, addr.wrapping_add(1));
|
||||||
8
|
8
|
||||||
},
|
}
|
||||||
0x23 => self.inc_rr(REG_N_H, REG_N_L),
|
0x23 => self.inc_rr(REG_N_H, REG_N_L),
|
||||||
0x24 => self.reg_inc(REG_N_H),
|
0x24 => self.reg_inc(REG_N_H),
|
||||||
0x25 => self.reg_dec(REG_N_H),
|
0x25 => self.reg_dec(REG_N_H),
|
||||||
@ -1188,11 +1206,11 @@ impl CPU {
|
|||||||
self.set_clear_flag(FLAG_Z, a == 0);
|
self.set_clear_flag(FLAG_Z, a == 0);
|
||||||
|
|
||||||
4
|
4
|
||||||
},
|
}
|
||||||
0x28 => {
|
0x28 => {
|
||||||
let c = self.flags & FLAG_Z == FLAG_Z;
|
let c = self.flags & FLAG_Z == FLAG_Z;
|
||||||
self.jmp_r_condition("Z", c)
|
self.jmp_r_condition("Z", c)
|
||||||
},
|
}
|
||||||
0x29 => self.add_rr_rr(REG_N_H, REG_N_L, REG_N_H, REG_N_L),
|
0x29 => self.add_rr_rr(REG_N_H, REG_N_L, REG_N_H, REG_N_L),
|
||||||
0x2A => {
|
0x2A => {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
@ -1202,7 +1220,7 @@ impl CPU {
|
|||||||
self.regs[REG_A] = self.interconnect.read_byte(addr);
|
self.regs[REG_A] = self.interconnect.read_byte(addr);
|
||||||
self.set_pair_value(REG_N_H, REG_N_L, addr.wrapping_add(1));
|
self.set_pair_value(REG_N_H, REG_N_L, addr.wrapping_add(1));
|
||||||
8
|
8
|
||||||
},
|
}
|
||||||
0x2B => self.dec_rr(REG_N_H, REG_N_L),
|
0x2B => self.dec_rr(REG_N_H, REG_N_L),
|
||||||
0x2C => self.reg_inc(REG_N_L),
|
0x2C => self.reg_inc(REG_N_L),
|
||||||
0x2D => self.reg_dec(REG_N_L),
|
0x2D => self.reg_dec(REG_N_L),
|
||||||
@ -1215,13 +1233,12 @@ impl CPU {
|
|||||||
self.set_flag(FLAG_N);
|
self.set_flag(FLAG_N);
|
||||||
self.set_flag(FLAG_H);
|
self.set_flag(FLAG_H);
|
||||||
4
|
4
|
||||||
},
|
}
|
||||||
|
|
||||||
|
|
||||||
0x30 => {
|
0x30 => {
|
||||||
let c = self.flags & FLAG_C == 0;
|
let c = self.flags & FLAG_C == 0;
|
||||||
self.jmp_r_condition("NC", c)
|
self.jmp_r_condition("NC", c)
|
||||||
},
|
}
|
||||||
0x31 => {
|
0x31 => {
|
||||||
let args = self.load_args(2);
|
let args = self.load_args(2);
|
||||||
self.sp = to_u16(args);
|
self.sp = to_u16(args);
|
||||||
@ -1229,7 +1246,7 @@ impl CPU {
|
|||||||
println!("LD SP, {:04x}", self.sp);
|
println!("LD SP, {:04x}", self.sp);
|
||||||
}
|
}
|
||||||
12
|
12
|
||||||
},
|
}
|
||||||
0x32 => {
|
0x32 => {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("LD (HL-), A");
|
println!("LD (HL-), A");
|
||||||
@ -1238,7 +1255,7 @@ impl CPU {
|
|||||||
self.interconnect.write_byte(addr, self.regs[REG_A]);
|
self.interconnect.write_byte(addr, self.regs[REG_A]);
|
||||||
self.set_pair_value(REG_N_H, REG_N_L, addr.wrapping_sub(1));
|
self.set_pair_value(REG_N_H, REG_N_L, addr.wrapping_sub(1));
|
||||||
8
|
8
|
||||||
},
|
}
|
||||||
0x33 => {
|
0x33 => {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("INC SP");
|
println!("INC SP");
|
||||||
@ -1258,11 +1275,11 @@ impl CPU {
|
|||||||
self.clear_flag(FLAG_N);
|
self.clear_flag(FLAG_N);
|
||||||
self.clear_flag(FLAG_H);
|
self.clear_flag(FLAG_H);
|
||||||
4
|
4
|
||||||
},
|
}
|
||||||
0x38 => {
|
0x38 => {
|
||||||
let c = self.flags & FLAG_C == FLAG_C;
|
let c = self.flags & FLAG_C == FLAG_C;
|
||||||
self.jmp_r_condition("C", c)
|
self.jmp_r_condition("C", c)
|
||||||
},
|
}
|
||||||
0x39 => {
|
0x39 => {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("ADD HL, SP");
|
println!("ADD HL, SP");
|
||||||
@ -1276,7 +1293,7 @@ impl CPU {
|
|||||||
self.set_clear_flag(FLAG_C, old > v && sp > 0);
|
self.set_clear_flag(FLAG_C, old > v && sp > 0);
|
||||||
self.set_clear_flag(FLAG_H, ((old & 0xFFF) + (sp & 0xFFF)) > 0xFFF);
|
self.set_clear_flag(FLAG_H, ((old & 0xFFF) + (sp & 0xFFF)) > 0xFFF);
|
||||||
8
|
8
|
||||||
},
|
}
|
||||||
0x3A => {
|
0x3A => {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("LD A, (HL-)");
|
println!("LD A, (HL-)");
|
||||||
@ -1308,14 +1325,14 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LDs
|
// LDs
|
||||||
0x40 ... 0x47 => self.ld_r_r(REG_N_B, (instruction - 0x40) 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),
|
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),
|
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),
|
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),
|
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),
|
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),
|
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),
|
0x78..=0x7F => self.ld_r_r(REG_N_A, (instruction - 0x78) as usize),
|
||||||
|
|
||||||
// HALT
|
// HALT
|
||||||
0x76 => {
|
0x76 => {
|
||||||
@ -1324,10 +1341,10 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
self.halted = true;
|
self.halted = true;
|
||||||
4
|
4
|
||||||
},
|
}
|
||||||
|
|
||||||
// ADD
|
// ADD
|
||||||
0x80 ... 0x87 => {
|
0x80..=0x87 => {
|
||||||
let reg_id = (instruction - 0x80) as usize;
|
let reg_id = (instruction - 0x80) as usize;
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("ADD {}", REG_NAMES[reg_id]);
|
println!("ADD {}", REG_NAMES[reg_id]);
|
||||||
@ -1338,7 +1355,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ADC
|
// ADC
|
||||||
0x88 ... 0x8F => {
|
0x88..=0x8F => {
|
||||||
let reg_id = (instruction - 0x88) as usize;
|
let reg_id = (instruction - 0x88) as usize;
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("ADC {}", REG_NAMES[reg_id]);
|
println!("ADC {}", REG_NAMES[reg_id]);
|
||||||
@ -1350,7 +1367,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SUBs
|
// SUBs
|
||||||
0x90 ... 0x97 => {
|
0x90..=0x97 => {
|
||||||
let reg_id = (instruction - 0x90) as usize;
|
let reg_id = (instruction - 0x90) as usize;
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("SUB {}", REG_NAMES[reg_id]);
|
println!("SUB {}", REG_NAMES[reg_id]);
|
||||||
@ -1361,7 +1378,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SBC
|
// SBC
|
||||||
0x98 ... 0x9F => {
|
0x98..=0x9F => {
|
||||||
let reg_id = (instruction - 0x98) as usize;
|
let reg_id = (instruction - 0x98) as usize;
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("SBC {}", REG_NAMES[reg_id]);
|
println!("SBC {}", REG_NAMES[reg_id]);
|
||||||
@ -1372,7 +1389,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AND
|
// AND
|
||||||
0xA0 ... 0xA7 => {
|
0xA0..=0xA7 => {
|
||||||
let reg_id = (instruction - 0xA0) as usize;
|
let reg_id = (instruction - 0xA0) as usize;
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("AND {}", REG_NAMES[reg_id]);
|
println!("AND {}", REG_NAMES[reg_id]);
|
||||||
@ -1388,7 +1405,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// XOR
|
// XOR
|
||||||
0xA8 ... 0xAF => {
|
0xA8..=0xAF => {
|
||||||
let reg_id = (instruction - 0xA8) as usize;
|
let reg_id = (instruction - 0xA8) as usize;
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("XOR {}", REG_NAMES[reg_id]);
|
println!("XOR {}", REG_NAMES[reg_id]);
|
||||||
@ -1401,10 +1418,10 @@ impl CPU {
|
|||||||
self.clear_flag(FLAG_N);
|
self.clear_flag(FLAG_N);
|
||||||
self.clear_flag(FLAG_H);
|
self.clear_flag(FLAG_H);
|
||||||
4
|
4
|
||||||
},
|
}
|
||||||
|
|
||||||
// OR
|
// OR
|
||||||
0xB0 ... 0xB7 => {
|
0xB0..=0xB7 => {
|
||||||
let reg_id = (instruction - 0xB0) as usize;
|
let reg_id = (instruction - 0xB0) as usize;
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("OR {}", REG_NAMES[reg_id]);
|
println!("OR {}", REG_NAMES[reg_id]);
|
||||||
@ -1420,7 +1437,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CP
|
// CP
|
||||||
0xB8 ... 0xBF => {
|
0xB8..=0xBF => {
|
||||||
let reg_id = (instruction - 0xB8) as usize;
|
let reg_id = (instruction - 0xB8) as usize;
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("CP {}", REG_NAMES[reg_id]);
|
println!("CP {}", REG_NAMES[reg_id]);
|
||||||
@ -1429,17 +1446,17 @@ impl CPU {
|
|||||||
let v = self.get_8bit_reg(reg_id);
|
let v = self.get_8bit_reg(reg_id);
|
||||||
self.cp_r(v);
|
self.cp_r(v);
|
||||||
4
|
4
|
||||||
},
|
}
|
||||||
|
|
||||||
0xC0 => {
|
0xC0 => {
|
||||||
let c = self.flags & FLAG_Z == 0;
|
let c = self.flags & FLAG_Z == 0;
|
||||||
self.ret_condition("NZ", c)
|
self.ret_condition("NZ", c)
|
||||||
},
|
}
|
||||||
0xC1 => self.pop_rr(REG_N_B, REG_N_C),
|
0xC1 => self.pop_rr(REG_N_B, REG_N_C),
|
||||||
0xC2 => {
|
0xC2 => {
|
||||||
let c = self.flags & FLAG_Z == 0;
|
let c = self.flags & FLAG_Z == 0;
|
||||||
self.jmp_p_condition("NZ", c)
|
self.jmp_p_condition("NZ", c)
|
||||||
},
|
}
|
||||||
0xC3 => {
|
0xC3 => {
|
||||||
let dst = to_u16(self.load_args(2));
|
let dst = to_u16(self.load_args(2));
|
||||||
if self.debug {
|
if self.debug {
|
||||||
@ -1447,7 +1464,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
self.jmp_p(dst);
|
self.jmp_p(dst);
|
||||||
16
|
16
|
||||||
},
|
}
|
||||||
0xC4 => {
|
0xC4 => {
|
||||||
let c = self.flags & FLAG_Z == 0;
|
let c = self.flags & FLAG_Z == 0;
|
||||||
self.call_condition("NZ", c)
|
self.call_condition("NZ", c)
|
||||||
@ -1462,19 +1479,19 @@ impl CPU {
|
|||||||
self.add_r(val);
|
self.add_r(val);
|
||||||
|
|
||||||
8
|
8
|
||||||
},
|
}
|
||||||
0xC7 => self.rst(0x00),
|
0xC7 => self.rst(0x00),
|
||||||
0xC8 => {
|
0xC8 => {
|
||||||
let c = self.flags & FLAG_Z == FLAG_Z;
|
let c = self.flags & FLAG_Z == FLAG_Z;
|
||||||
self.ret_condition("Z", c)
|
self.ret_condition("Z", c)
|
||||||
},
|
}
|
||||||
0xC9 => {
|
0xC9 => {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("RET");
|
println!("RET");
|
||||||
}
|
}
|
||||||
self.ret();
|
self.ret();
|
||||||
16
|
16
|
||||||
},
|
}
|
||||||
0xCA => {
|
0xCA => {
|
||||||
let c = self.flags & FLAG_Z == FLAG_Z;
|
let c = self.flags & FLAG_Z == FLAG_Z;
|
||||||
self.jmp_p_condition("Z", c)
|
self.jmp_p_condition("Z", c)
|
||||||
@ -1482,11 +1499,11 @@ impl CPU {
|
|||||||
0xCB => {
|
0xCB => {
|
||||||
self.run_prefix_instruction();
|
self.run_prefix_instruction();
|
||||||
12 // TODO: Verify that this is the case for all prefix instructions.
|
12 // TODO: Verify that this is the case for all prefix instructions.
|
||||||
},
|
}
|
||||||
0xCC => {
|
0xCC => {
|
||||||
let c = self.flags & FLAG_Z == FLAG_Z;
|
let c = self.flags & FLAG_Z == FLAG_Z;
|
||||||
self.call_condition("Z", c)
|
self.call_condition("Z", c)
|
||||||
},
|
}
|
||||||
0xCD => self.call_condition("", true),
|
0xCD => self.call_condition("", true),
|
||||||
0xCE => {
|
0xCE => {
|
||||||
let arg = self.load_args(1).single_val();
|
let arg = self.load_args(1).single_val();
|
||||||
@ -1495,14 +1512,13 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
self.adc_r(arg);
|
self.adc_r(arg);
|
||||||
8
|
8
|
||||||
},
|
}
|
||||||
0xCF => self.rst(0x08),
|
0xCF => self.rst(0x08),
|
||||||
|
|
||||||
|
|
||||||
0xD0 => {
|
0xD0 => {
|
||||||
let c = self.flags & FLAG_C == 0;
|
let c = self.flags & FLAG_C == 0;
|
||||||
self.ret_condition("NC", c)
|
self.ret_condition("NC", c)
|
||||||
},
|
}
|
||||||
0xD1 => self.pop_rr(REG_N_D, REG_N_E),
|
0xD1 => self.pop_rr(REG_N_D, REG_N_E),
|
||||||
0xD2 => {
|
0xD2 => {
|
||||||
let c = self.flags & FLAG_C == 0;
|
let c = self.flags & FLAG_C == 0;
|
||||||
@ -1522,12 +1538,12 @@ impl CPU {
|
|||||||
|
|
||||||
self.sub_r(val);
|
self.sub_r(val);
|
||||||
8
|
8
|
||||||
},
|
}
|
||||||
0xD7 => self.rst(0x10),
|
0xD7 => self.rst(0x10),
|
||||||
0xD8 => {
|
0xD8 => {
|
||||||
let c = self.flags & FLAG_C == FLAG_C;
|
let c = self.flags & FLAG_C == FLAG_C;
|
||||||
self.ret_condition("C", c)
|
self.ret_condition("C", c)
|
||||||
},
|
}
|
||||||
0xD9 => {
|
0xD9 => {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("RETI");
|
println!("RETI");
|
||||||
@ -1537,7 +1553,7 @@ impl CPU {
|
|||||||
0xDA => {
|
0xDA => {
|
||||||
let c = self.flags & FLAG_C == FLAG_C;
|
let c = self.flags & FLAG_C == FLAG_C;
|
||||||
self.jmp_p_condition("C", c)
|
self.jmp_p_condition("C", c)
|
||||||
},
|
}
|
||||||
0xDB => panic!("NON-EXISTING OPCODE"),
|
0xDB => panic!("NON-EXISTING OPCODE"),
|
||||||
0xDC => {
|
0xDC => {
|
||||||
let c = self.flags & FLAG_C == FLAG_C;
|
let c = self.flags & FLAG_C == FLAG_C;
|
||||||
@ -1551,7 +1567,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
self.sbc_r(arg);
|
self.sbc_r(arg);
|
||||||
8
|
8
|
||||||
},
|
}
|
||||||
0xDF => self.rst(0x18),
|
0xDF => self.rst(0x18),
|
||||||
|
|
||||||
0xE0 => {
|
0xE0 => {
|
||||||
@ -1559,9 +1575,10 @@ impl CPU {
|
|||||||
if self.debug {
|
if self.debug {
|
||||||
println!("LDH {:02X}, A", arg);
|
println!("LDH {:02X}, A", arg);
|
||||||
}
|
}
|
||||||
self.interconnect.write_byte(0xFF00 + arg as u16, self.regs[REG_A]);
|
self.interconnect
|
||||||
|
.write_byte(0xFF00 + arg as u16, self.regs[REG_A]);
|
||||||
12
|
12
|
||||||
},
|
}
|
||||||
0xE1 => self.pop_rr(REG_N_H, REG_N_L),
|
0xE1 => self.pop_rr(REG_N_H, REG_N_L),
|
||||||
0xE2 => {
|
0xE2 => {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
@ -1570,7 +1587,7 @@ impl CPU {
|
|||||||
let addr: u16 = 0xFF00 + self.get_8bit_reg(REG_N_C) as u16;
|
let addr: u16 = 0xFF00 + self.get_8bit_reg(REG_N_C) as u16;
|
||||||
self.interconnect.write_byte(addr, self.regs[REG_A]);
|
self.interconnect.write_byte(addr, self.regs[REG_A]);
|
||||||
8
|
8
|
||||||
},
|
}
|
||||||
0xE3 | 0xE4 => panic!("NON-EXISTING OPCODE"),
|
0xE3 | 0xE4 => panic!("NON-EXISTING OPCODE"),
|
||||||
0xE5 => self.push_rr(REG_N_H, REG_N_L),
|
0xE5 => self.push_rr(REG_N_H, REG_N_L),
|
||||||
0xE6 => {
|
0xE6 => {
|
||||||
@ -1586,7 +1603,7 @@ impl CPU {
|
|||||||
self.clear_flag(FLAG_C);
|
self.clear_flag(FLAG_C);
|
||||||
|
|
||||||
8
|
8
|
||||||
},
|
}
|
||||||
0xE7 => self.rst(0x20),
|
0xE7 => self.rst(0x20),
|
||||||
0xE8 => {
|
0xE8 => {
|
||||||
let arg = self.load_args(1).single_val() as i8;
|
let arg = self.load_args(1).single_val() as i8;
|
||||||
@ -1613,16 +1630,16 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
self.ip = self.get_pair_value(REG_N_H, REG_N_L);
|
self.ip = self.get_pair_value(REG_N_H, REG_N_L);
|
||||||
4
|
4
|
||||||
},
|
}
|
||||||
0xEA => {
|
0xEA => {
|
||||||
let addr = to_u16(self.load_args(2));
|
let addr = to_u16(self.load_args(2));
|
||||||
if self.debug{
|
if self.debug {
|
||||||
println!("LD ({:04X}), A", addr);
|
println!("LD ({:04X}), A", addr);
|
||||||
}
|
}
|
||||||
self.interconnect.write_byte(addr, self.regs[REG_A]);
|
self.interconnect.write_byte(addr, self.regs[REG_A]);
|
||||||
16
|
16
|
||||||
},
|
}
|
||||||
0xEB ... 0xED => panic!("NON-EXISTING OPCODE"),
|
0xEB..=0xED => panic!("NON-EXISTING OPCODE"),
|
||||||
0xEE => {
|
0xEE => {
|
||||||
let arg = self.load_args(1).single_val();
|
let arg = self.load_args(1).single_val();
|
||||||
if self.debug {
|
if self.debug {
|
||||||
@ -1635,7 +1652,7 @@ impl CPU {
|
|||||||
self.clear_flag(FLAG_C);
|
self.clear_flag(FLAG_C);
|
||||||
self.clear_flag(FLAG_N);
|
self.clear_flag(FLAG_N);
|
||||||
8
|
8
|
||||||
},
|
}
|
||||||
0xEF => self.rst(0x28),
|
0xEF => self.rst(0x28),
|
||||||
|
|
||||||
0xF0 => {
|
0xF0 => {
|
||||||
@ -1645,23 +1662,23 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
self.regs[REG_A] = self.interconnect.read_byte(0xFF00 + arg as u16);
|
self.regs[REG_A] = self.interconnect.read_byte(0xFF00 + arg as u16);
|
||||||
12
|
12
|
||||||
},
|
}
|
||||||
0xF1 => self.pop_rr(REG_N_A, REG_N_F),
|
0xF1 => self.pop_rr(REG_N_A, REG_N_F),
|
||||||
0xF2 => {
|
0xF2 => {
|
||||||
if self.debug{
|
if self.debug {
|
||||||
println!("LD A, (C)");
|
println!("LD A, (C)");
|
||||||
}
|
}
|
||||||
let addr = 0xFF00 + self.get_8bit_reg(REG_N_C) as u16;
|
let addr = 0xFF00 + self.get_8bit_reg(REG_N_C) as u16;
|
||||||
self.regs[REG_A] = self.interconnect.read_byte(addr);
|
self.regs[REG_A] = self.interconnect.read_byte(addr);
|
||||||
8
|
8
|
||||||
},
|
}
|
||||||
0xF3 => {
|
0xF3 => {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("DI");
|
println!("DI");
|
||||||
}
|
}
|
||||||
self.ime = false;
|
self.ime = false;
|
||||||
4
|
4
|
||||||
},
|
}
|
||||||
0xF4 => panic!("NON-EXISTING OPCODE"),
|
0xF4 => panic!("NON-EXISTING OPCODE"),
|
||||||
0xF5 => self.push_rr(REG_N_A, REG_N_F),
|
0xF5 => self.push_rr(REG_N_A, REG_N_F),
|
||||||
0xF6 => {
|
0xF6 => {
|
||||||
@ -1676,7 +1693,7 @@ impl CPU {
|
|||||||
self.clear_flag(FLAG_N);
|
self.clear_flag(FLAG_N);
|
||||||
self.clear_flag(FLAG_H);
|
self.clear_flag(FLAG_H);
|
||||||
8
|
8
|
||||||
},
|
}
|
||||||
0xF7 => self.rst(0x30),
|
0xF7 => self.rst(0x30),
|
||||||
0xF8 => {
|
0xF8 => {
|
||||||
let arg = self.load_args(1).single_val() as i8;
|
let arg = self.load_args(1).single_val() as i8;
|
||||||
@ -1700,14 +1717,14 @@ impl CPU {
|
|||||||
self.set_clear_flag(FLAG_H, (sp ^ arg as u16 ^ v) & 0x10 == 0x10);
|
self.set_clear_flag(FLAG_H, (sp ^ arg as u16 ^ v) & 0x10 == 0x10);
|
||||||
}
|
}
|
||||||
12
|
12
|
||||||
},
|
}
|
||||||
0xF9 => {
|
0xF9 => {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("LD SP, HL");
|
println!("LD SP, HL");
|
||||||
}
|
}
|
||||||
self.sp = self.get_pair_value(REG_N_H, REG_N_L);
|
self.sp = self.get_pair_value(REG_N_H, REG_N_L);
|
||||||
8
|
8
|
||||||
},
|
}
|
||||||
0xFA => {
|
0xFA => {
|
||||||
let addr = to_u16(self.load_args(2));
|
let addr = to_u16(self.load_args(2));
|
||||||
if self.debug {
|
if self.debug {
|
||||||
@ -1715,14 +1732,14 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
self.regs[REG_A] = self.interconnect.read_byte(addr);
|
self.regs[REG_A] = self.interconnect.read_byte(addr);
|
||||||
16
|
16
|
||||||
},
|
}
|
||||||
0xFB => {
|
0xFB => {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("EI");
|
println!("EI");
|
||||||
}
|
}
|
||||||
self.ime = true; // interrupt master enable
|
self.ime = true; // interrupt master enable
|
||||||
4
|
4
|
||||||
},
|
}
|
||||||
0xFC | 0xFD => panic!("NON-EXISTING OPCODE"),
|
0xFC | 0xFD => panic!("NON-EXISTING OPCODE"),
|
||||||
|
|
||||||
0xFE => {
|
0xFE => {
|
||||||
@ -1733,9 +1750,9 @@ impl CPU {
|
|||||||
|
|
||||||
self.cp_r(arg);
|
self.cp_r(arg);
|
||||||
8
|
8
|
||||||
},
|
}
|
||||||
0xFF => self.rst(0x38),
|
0xFF => self.rst(0x38),
|
||||||
_ => panic!("Unknown instruction: {:02x}", instruction)
|
_ => panic!("Unknown instruction: {:02x}", instruction),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
self.interconnect.tick(cycles);
|
self.interconnect.tick(cycles);
|
||||||
|
|||||||
130
src/display.rs
130
src/display.rs
@ -1,5 +1,5 @@
|
|||||||
extern crate sdl2;
|
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
|
extern crate sdl2;
|
||||||
|
|
||||||
// Internal ram size
|
// Internal ram size
|
||||||
const VRAM_SIZE: usize = 0x2000;
|
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_Y_FLIP: u8 = 1 << 6;
|
||||||
const SPRITE_X_FLIP: u8 = 1 << 5;
|
const SPRITE_X_FLIP: u8 = 1 << 5;
|
||||||
const SPRITE_PALETTE_NO: u8 = 1 << 4; // NonCGB only
|
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
|
// Display color
|
||||||
/*
|
/*
|
||||||
@ -65,7 +65,7 @@ enum PixelOrigin {
|
|||||||
Empty,
|
Empty,
|
||||||
Background,
|
Background,
|
||||||
Window,
|
Window,
|
||||||
Sprite
|
Sprite,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
@ -149,8 +149,7 @@ pub struct Display {
|
|||||||
current_ticks: u16,
|
current_ticks: u16,
|
||||||
current_mode: DisplayMode,
|
current_mode: DisplayMode,
|
||||||
// TODO
|
// TODO
|
||||||
|
renderer: sdl2::render::Canvas<sdl2::video::Window>,
|
||||||
renderer: sdl2::render::Renderer<'static>,
|
|
||||||
|
|
||||||
pub event_pump: sdl2::EventPump,
|
pub event_pump: sdl2::EventPump,
|
||||||
|
|
||||||
@ -167,8 +166,12 @@ impl Display {
|
|||||||
pub fn new() -> Display {
|
pub fn new() -> Display {
|
||||||
let sdl_ctx = sdl2::init().unwrap();
|
let sdl_ctx = sdl2::init().unwrap();
|
||||||
let video_ctx = sdl_ctx.video().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 wnd = video_ctx
|
||||||
let renderer = wnd.renderer().build().expect("Could not build renderer");
|
.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 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)];
|
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) {
|
fn render_screen(&mut self) {
|
||||||
for y in 0 .. GB_PIXELS_Y {
|
for y in 0..GB_PIXELS_Y {
|
||||||
for x in 0 .. GB_PIXELS_X {
|
for x in 0..GB_PIXELS_X {
|
||||||
let f = &mut self.pixels[(x as usize) + (y as usize) * 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.set_draw_color(f.color);
|
||||||
self.renderer.fill_rect(
|
self.renderer
|
||||||
sdl2::rect::Rect::new(
|
.fill_rect(sdl2::rect::Rect::new(
|
||||||
x as i32 * SCALE as i32,
|
x as i32 * SCALE as i32,
|
||||||
y as i32 * SCALE as i32,
|
y as i32 * SCALE as i32,
|
||||||
SCALE as u32,
|
SCALE as u32,
|
||||||
SCALE as u32,
|
SCALE as u32,
|
||||||
)
|
))
|
||||||
).expect("Rendering failed");
|
.expect("Rendering failed");
|
||||||
|
|
||||||
// Clear origin after rendering
|
// Clear origin after rendering
|
||||||
f.origin = PixelOrigin::Empty;
|
f.origin = PixelOrigin::Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.renderer.set_draw_color(sdl2::pixels::Color::RGB(255, 0, 255));
|
self.renderer
|
||||||
self.renderer.draw_rect(
|
.set_draw_color(sdl2::pixels::Color::RGB(255, 0, 255));
|
||||||
sdl2::rect::Rect::new(
|
self.renderer
|
||||||
|
.draw_rect(sdl2::rect::Rect::new(
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
(GB_PIXELS_X * SCALE) as u32,
|
(GB_PIXELS_X * SCALE) as u32,
|
||||||
(GB_PIXELS_Y * SCALE) as u32,
|
(GB_PIXELS_Y * SCALE) as u32,
|
||||||
)
|
))
|
||||||
).expect("Rendering failed");
|
.expect("Rendering failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -268,8 +272,8 @@ impl Display {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn write_byte(&mut self, addr: u16, val: u8) {
|
pub fn write_byte(&mut self, addr: u16, val: u8) {
|
||||||
match addr {
|
match addr {
|
||||||
0x8000 ... 0x9FFF => self.vram[(addr - 0x8000) as usize] = val,
|
0x8000..=0x9FFF => self.vram[(addr - 0x8000) as usize] = val,
|
||||||
0xFE00 ... 0xFE9F => self.oam[(addr - 0xFE00) as usize] = val,
|
0xFE00..=0xFE9F => self.oam[(addr - 0xFE00) as usize] = val,
|
||||||
0xFF40 => self.control = val,
|
0xFF40 => self.control = val,
|
||||||
0xFF41 => self.status = val,
|
0xFF41 => self.status = val,
|
||||||
0xFF42 => self.scrolly = val,
|
0xFF42 => self.scrolly = val,
|
||||||
@ -298,8 +302,8 @@ impl Display {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn read_byte(&self, addr: u16) -> u8 {
|
pub fn read_byte(&self, addr: u16) -> u8 {
|
||||||
match addr {
|
match addr {
|
||||||
0x8000 ... 0x9FFF => self.vram[(addr - 0x8000) as usize],
|
0x8000..=0x9FFF => self.vram[(addr - 0x8000) as usize],
|
||||||
0xFE00 ... 0xFE9F => self.oam[(addr - 0xFE00) as usize],
|
0xFE00..=0xFE9F => self.oam[(addr - 0xFE00) as usize],
|
||||||
0xFF40 => self.control,
|
0xFF40 => self.control,
|
||||||
0xFF41 => self.status,
|
0xFF41 => self.status,
|
||||||
0xFF42 => self.scrolly,
|
0xFF42 => self.scrolly,
|
||||||
@ -329,15 +333,17 @@ impl Display {
|
|||||||
self.current_ticks += ticks;
|
self.current_ticks += ticks;
|
||||||
|
|
||||||
match self.current_mode {
|
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 {
|
if self.current_ticks > TICKS_END_SCANLINE {
|
||||||
self.current_ticks = 0;
|
self.current_ticks = 0;
|
||||||
self.current_mode = DisplayMode::ReadFullMemory;
|
self.current_mode = DisplayMode::ReadFullMemory;
|
||||||
}
|
}
|
||||||
self.status |= 2;
|
self.status |= 2;
|
||||||
},
|
}
|
||||||
DisplayMode::ReadFullMemory => { // Mode 3, reading OAM, VMEM and palette data.
|
DisplayMode::ReadFullMemory => {
|
||||||
// Nothing may be accessed.
|
// Mode 3, reading OAM, VMEM and palette data.
|
||||||
|
// Nothing may be accessed.
|
||||||
if self.current_ticks > TICKS_END_READMODE {
|
if self.current_ticks > TICKS_END_READMODE {
|
||||||
self.current_ticks = 0;
|
self.current_ticks = 0;
|
||||||
self.current_mode = DisplayMode::HBlank;
|
self.current_mode = DisplayMode::HBlank;
|
||||||
@ -349,8 +355,9 @@ impl Display {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.status |= 3;
|
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 {
|
if self.current_ticks > TICKS_END_HBLANK {
|
||||||
self.current_ticks = 0;
|
self.current_ticks = 0;
|
||||||
self.curline += 1;
|
self.curline += 1;
|
||||||
@ -369,9 +376,10 @@ impl Display {
|
|||||||
}
|
}
|
||||||
self.status &= 0xFC;
|
self.status &= 0xFC;
|
||||||
self.status |= 0;
|
self.status |= 0;
|
||||||
},
|
}
|
||||||
DisplayMode::VBlank => { // Mode 1, V-Blank (or display disabled), Memory (RAM, OAM)
|
DisplayMode::VBlank => {
|
||||||
// may be accessed
|
// Mode 1, V-Blank (or display disabled), Memory (RAM, OAM)
|
||||||
|
// may be accessed
|
||||||
if self.current_ticks > TICKS_END_VBLANK {
|
if self.current_ticks > TICKS_END_VBLANK {
|
||||||
self.current_ticks = 0;
|
self.current_ticks = 0;
|
||||||
self.curline += 1;
|
self.curline += 1;
|
||||||
@ -383,7 +391,8 @@ impl Display {
|
|||||||
if self.frameskip < self.frame_no {
|
if self.frameskip < self.frame_no {
|
||||||
self.render_screen();
|
self.render_screen();
|
||||||
self.renderer.present();
|
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.renderer.clear();
|
||||||
self.frame_no = 0;
|
self.frame_no = 0;
|
||||||
} else {
|
} else {
|
||||||
@ -413,7 +422,7 @@ impl Display {
|
|||||||
fn render_sprites(&mut self, sprites: &Vec<Sprite>) {
|
fn render_sprites(&mut self, sprites: &Vec<Sprite>) {
|
||||||
if self.control & CTRL_BG_SPRITE_ENABLE > 0 {
|
if self.control & CTRL_BG_SPRITE_ENABLE > 0 {
|
||||||
let mut num_rendered: u8 = 0;
|
let mut num_rendered: u8 = 0;
|
||||||
for i in 0 .. 39 {
|
for i in 0..39 {
|
||||||
// Gameboy limitation
|
// Gameboy limitation
|
||||||
if num_rendered > 10 {
|
if num_rendered > 10 {
|
||||||
break;
|
break;
|
||||||
@ -425,7 +434,6 @@ impl Display {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Calculate correct coords
|
// Calculate correct coords
|
||||||
let x: u8 = sprite.x.wrapping_sub(8);
|
let x: u8 = sprite.x.wrapping_sub(8);
|
||||||
let y: u8 = sprite.y.wrapping_sub(16);
|
let y: u8 = sprite.y.wrapping_sub(16);
|
||||||
@ -438,7 +446,7 @@ impl Display {
|
|||||||
// Flip sprite, TODO: Validate
|
// Flip sprite, TODO: Validate
|
||||||
let y_o: u8 = match sprite.is_y_flipped() {
|
let y_o: u8 = match sprite.is_y_flipped() {
|
||||||
true => y ^ 7,
|
true => y ^ 7,
|
||||||
false => y
|
false => y,
|
||||||
};
|
};
|
||||||
let tile_offset_y: usize = render_y as usize - y_o as usize;
|
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?
|
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 {
|
let limit = match wide_mode {
|
||||||
true => 16,
|
true => 16,
|
||||||
false => 8
|
false => 8,
|
||||||
};
|
};
|
||||||
for x_o in 0 .. limit {
|
for x_o in 0..limit {
|
||||||
let b1: bool;
|
let b1: bool;
|
||||||
let b2: bool;
|
let b2: bool;
|
||||||
|
|
||||||
@ -513,7 +521,12 @@ impl Display {
|
|||||||
let entry = MONOCHROME_PALETTE[lookup[c as usize] as usize];
|
let entry = MONOCHROME_PALETTE[lookup[c as usize] as usize];
|
||||||
|
|
||||||
// Draw stuff. We're currently only in monochrome mode
|
// 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;
|
let render_y: u8 = self.curline;
|
||||||
// Order sprites by priority
|
// Order sprites by priority
|
||||||
let mut queue: Vec<Sprite> = Vec::new();
|
let mut queue: Vec<Sprite> = Vec::new();
|
||||||
for i in 0 .. 39 {
|
for i in 0..39 {
|
||||||
queue.push(Sprite{
|
queue.push(Sprite {
|
||||||
y: self.oam[i * 4 + 0],
|
y: self.oam[i * 4 + 0],
|
||||||
x: self.oam[i * 4 + 1],
|
x: self.oam[i * 4 + 1],
|
||||||
tile: self.oam[i * 4 + 2],
|
tile: self.oam[i * 4 + 2],
|
||||||
@ -571,20 +584,19 @@ impl Display {
|
|||||||
use std::cmp;
|
use std::cmp;
|
||||||
queue.sort_by(|x, y| {
|
queue.sort_by(|x, y| {
|
||||||
if x.x > y.x {
|
if x.x > y.x {
|
||||||
cmp::Ordering::Greater
|
cmp::Ordering::Greater
|
||||||
} else if x.x < y.x {
|
} else if x.x < y.x {
|
||||||
cmp::Ordering::Less
|
cmp::Ordering::Less
|
||||||
} else {
|
} else {
|
||||||
cmp::Ordering::Equal
|
cmp::Ordering::Equal
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
queue.reverse();
|
queue.reverse();
|
||||||
|
|
||||||
|
|
||||||
// Render background
|
// Render background
|
||||||
if (self.control & CTRL_BG_DISPLAY) == CTRL_BG_DISPLAY {
|
if (self.control & CTRL_BG_DISPLAY) == CTRL_BG_DISPLAY {
|
||||||
// Render pixels (20 tiles)
|
// Render pixels (20 tiles)
|
||||||
for render_x in 0 .. 160 {
|
for render_x in 0..160 {
|
||||||
// Absolute render coordinates
|
// Absolute render coordinates
|
||||||
let render_abs_x = map_offset_x.wrapping_add(render_x);
|
let render_abs_x = map_offset_x.wrapping_add(render_x);
|
||||||
let render_abs_y = map_offset_y.wrapping_add(render_y);
|
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_index_y: u8 = render_abs_y >> 3;
|
||||||
let tile_offset_y: u8 = render_abs_y & 7;
|
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
|
// Obtain tile ID in this area
|
||||||
let tile_id = self.vram[vram_offset];
|
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.
|
0 => PixelOrigin::Empty, // Hack so that objects will be in front of it.
|
||||||
_ => PixelOrigin::Background,
|
_ => 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 = self.windowx.wrapping_add(7);
|
||||||
let rx = 7u8.wrapping_sub(self.windowx);
|
let rx = 7u8.wrapping_sub(self.windowx);
|
||||||
let ry = render_y.wrapping_add(self.windowy);
|
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);
|
let render_x = r_x.wrapping_add(rx);
|
||||||
// Absolute render coordinates
|
// Absolute render coordinates
|
||||||
let tile_index_x: u8 = render_x >> 3;
|
let tile_index_x: u8 = render_x >> 3;
|
||||||
@ -643,7 +661,8 @@ impl Display {
|
|||||||
let tile_index_y: u8 = ry >> 3;
|
let tile_index_y: u8 = ry >> 3;
|
||||||
let tile_offset_y: u8 = ry & 7;
|
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
|
// Obtain tile ID in this area
|
||||||
let tile_id = self.vram[vram_offset];
|
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.
|
0 => PixelOrigin::Empty, // Hack so that objects will be in front of it.
|
||||||
_ => PixelOrigin::Window,
|
_ => 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_B: u8 = 1 << 1;
|
||||||
const KEY_A: u8 = 1 << 0;
|
const KEY_A: u8 = 1 << 0;
|
||||||
|
|
||||||
|
use super::cartridge;
|
||||||
use super::display;
|
use super::display;
|
||||||
|
use super::serial;
|
||||||
use super::sound;
|
use super::sound;
|
||||||
use super::timer;
|
use super::timer;
|
||||||
use super::serial;
|
|
||||||
use super::cartridge;
|
|
||||||
|
|
||||||
extern crate sdl2;
|
extern crate sdl2;
|
||||||
|
|
||||||
@ -34,7 +34,14 @@ use self::sdl2::keyboard::Keycode;
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Key {
|
enum Key {
|
||||||
UP, LEFT, DOWN, RIGHT, START, SELECT, A, B
|
UP,
|
||||||
|
LEFT,
|
||||||
|
DOWN,
|
||||||
|
RIGHT,
|
||||||
|
START,
|
||||||
|
SELECT,
|
||||||
|
A,
|
||||||
|
B,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Interconnect {
|
pub struct Interconnect {
|
||||||
@ -153,29 +160,81 @@ impl Interconnect {
|
|||||||
// Make sure the window is responsive:
|
// Make sure the window is responsive:
|
||||||
if self.cycles > 500 {
|
if self.cycles > 500 {
|
||||||
loop {
|
loop {
|
||||||
if let Some(event) = self.display.event_pump.poll_event(){
|
if let Some(event) = self.display.event_pump.poll_event() {
|
||||||
match event {
|
match event {
|
||||||
Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
|
Event::Quit { .. }
|
||||||
|
| Event::KeyDown {
|
||||||
|
keycode: Some(Keycode::Escape),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
self.cartridge.save();
|
self.cartridge.save();
|
||||||
panic!("TODO: Proper shutdown");
|
panic!("TODO: Proper shutdown");
|
||||||
},
|
}
|
||||||
Event::KeyDown { keycode: Some(Keycode::Left), .. } => self.press_key(Key::LEFT),
|
Event::KeyDown {
|
||||||
Event::KeyDown { keycode: Some(Keycode::Down), .. } => self.press_key(Key::DOWN),
|
keycode: Some(Keycode::Left),
|
||||||
Event::KeyDown { keycode: Some(Keycode::Up), .. } => self.press_key(Key::UP),
|
..
|
||||||
Event::KeyDown { keycode: Some(Keycode::Right), .. } => self.press_key(Key::RIGHT),
|
} => self.press_key(Key::LEFT),
|
||||||
Event::KeyDown { keycode: Some(Keycode::A), .. } => self.press_key(Key::START),
|
Event::KeyDown {
|
||||||
Event::KeyDown { keycode: Some(Keycode::S), .. } => self.press_key(Key::SELECT),
|
keycode: Some(Keycode::Down),
|
||||||
Event::KeyDown { keycode: Some(Keycode::Z), .. } => self.press_key(Key::A),
|
..
|
||||||
Event::KeyDown { keycode: Some(Keycode::X), .. } => self.press_key(Key::B),
|
} => 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 {
|
||||||
Event::KeyUp { keycode: Some(Keycode::Down), .. } => self.release_key(Key::DOWN),
|
keycode: Some(Keycode::Left),
|
||||||
Event::KeyUp { keycode: Some(Keycode::Up), .. } => self.release_key(Key::UP),
|
..
|
||||||
Event::KeyUp { keycode: Some(Keycode::Right), .. } => self.release_key(Key::RIGHT),
|
} => self.release_key(Key::LEFT),
|
||||||
Event::KeyUp { keycode: Some(Keycode::A), .. } => self.release_key(Key::START),
|
Event::KeyUp {
|
||||||
Event::KeyUp { keycode: Some(Keycode::S), .. } => self.release_key(Key::SELECT),
|
keycode: Some(Keycode::Down),
|
||||||
Event::KeyUp { keycode: Some(Keycode::Z), .. } => self.release_key(Key::A),
|
..
|
||||||
Event::KeyUp { keycode: Some(Keycode::X), .. } => self.release_key(Key::B),
|
} => 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 {
|
} else {
|
||||||
@ -199,25 +258,21 @@ impl Interconnect {
|
|||||||
// TODO: if some flag set, use bios, otherwise only use rom
|
// TODO: if some flag set, use bios, otherwise only use rom
|
||||||
// For now, just use bios
|
// For now, just use bios
|
||||||
match addr {
|
match addr {
|
||||||
0x0000 ... 0xFF => {
|
0x0000..=0x100 => {
|
||||||
if self.disable_bootrom == 0 {
|
if self.disable_bootrom == 0 {
|
||||||
self.bios[addr as usize]
|
self.bios[addr as usize]
|
||||||
} else {
|
} else {
|
||||||
self.cartridge.read_byte(addr)
|
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 => {
|
0xFF00 => {
|
||||||
if self.joy_switch & KEY_REGULAR == 0 {
|
if self.joy_switch & KEY_REGULAR == 0 {
|
||||||
self.joy_regular_keys
|
self.joy_regular_keys
|
||||||
@ -228,39 +283,25 @@ impl Interconnect {
|
|||||||
0x3F
|
0x3F
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0xFF01 ... 0xFF02 => self.serial.read_byte(addr),
|
0xFF01..=0xFF02 => self.serial.read_byte(addr),
|
||||||
0xFF04 ... 0xFF07 => self.timer.read_byte(addr),
|
0xFF04..=0xFF07 => self.timer.read_byte(addr),
|
||||||
0xFF0F => {
|
0xFF0F => {
|
||||||
// println!("Reading IF: {:02X}", self.interrupt_request_flags);
|
// println!("Reading IF: {:02X}", self.interrupt_request_flags);
|
||||||
self.interrupt_request_flags
|
self.interrupt_request_flags
|
||||||
},
|
}
|
||||||
0xFF10 ... 0xFF26 => {
|
0xFF10...0xFF26 => self.sound.sound_object.lock().unwrap().read_byte(addr),
|
||||||
self.sound.sound_object.lock().unwrap().read_byte(addr)
|
0xFF30...0xFF3F => self.sound.sound_object.lock().unwrap().read_byte(addr),
|
||||||
},
|
0xFF40...0xFF4B => self.display.read_byte(addr),
|
||||||
0xFF30 ... 0xFF3F => self.sound.sound_object.lock().unwrap().read_byte(addr),
|
0xFF50 => self.disable_bootrom,
|
||||||
0xFF40 ... 0xFF4B => {
|
|
||||||
self.display.read_byte(addr)
|
|
||||||
},
|
|
||||||
0xFF50 => {
|
|
||||||
self.disable_bootrom
|
|
||||||
},
|
|
||||||
0xFF51 => self.vram_dma_source_high,
|
0xFF51 => self.vram_dma_source_high,
|
||||||
0xFF52 => self.vram_dma_source_low,
|
0xFF52 => self.vram_dma_source_low,
|
||||||
0xFF53 => self.vram_dma_destination_high,
|
0xFF53 => self.vram_dma_destination_high,
|
||||||
0xFF54 => self.vram_dma_destination_low,
|
0xFF54 => self.vram_dma_destination_low,
|
||||||
0xFF55 => {
|
0xFF55 => self.vram_dma_length,
|
||||||
self.vram_dma_length
|
0xFF56 => self.infrared_com_port,
|
||||||
}
|
|
||||||
0xFF56 => {
|
|
||||||
self.infrared_com_port
|
|
||||||
},
|
|
||||||
0xFF70 => self.wram_bank,
|
0xFF70 => self.wram_bank,
|
||||||
0xFF80 ... 0xFFFE => {
|
0xFF80..=0xFFFE => self.hiram[(addr - 0xFF80) as usize],
|
||||||
self.hiram[(addr - 0xFF80) as usize]
|
0xFFFF => self.interrupt,
|
||||||
},
|
|
||||||
0xFFFF => {
|
|
||||||
self.interrupt
|
|
||||||
}
|
|
||||||
_ => {
|
_ => {
|
||||||
println!("Read from {:04X} not supported.", addr);
|
println!("Read from {:04X} not supported.", addr);
|
||||||
0
|
0
|
||||||
@ -285,36 +326,45 @@ impl Interconnect {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
match addr {
|
match addr {
|
||||||
0x0000 ... 0x7FFF => self.cartridge.write_byte(addr, val),
|
0x0000..=0x7FFF => self.cartridge.write_byte(addr, val),
|
||||||
0x8000 ... 0x9FFF => self.display.write_byte(addr, val),
|
0x8000..=0x9FFF => self.display.write_byte(addr, val),
|
||||||
0xA000 ... 0xBFFF => self.cartridge.write_byte(addr, val),
|
0xA000..=0xBFFF => self.cartridge.write_byte(addr, val),
|
||||||
0xC000 ... 0xCFFF => {
|
0xC000..=0xCFFF => {
|
||||||
self.ram[(addr - 0xC000) as usize] = val;
|
self.ram[(addr - 0xC000) as usize] = val;
|
||||||
},
|
}
|
||||||
0xD000 ... 0xDFFF => {
|
0xD000..=0xDFFF => {
|
||||||
self.ram[(addr - 0xD000) as usize + self.wram_bank as usize * 0x1000] = val;
|
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 => {
|
0xFF00 => {
|
||||||
// Joystick select
|
// Joystick select
|
||||||
self.joy_switch = val;
|
self.joy_switch = val;
|
||||||
}
|
}
|
||||||
0xFF01 ... 0xFF02 => self.serial.write_byte(addr, val),
|
0xFF01..=0xFF02 => self.serial.write_byte(addr, val),
|
||||||
0xFF04 ... 0xFF07 => self.timer.write_byte(addr, val),
|
0xFF04..=0xFF07 => self.timer.write_byte(addr, val),
|
||||||
0xFF0F => {
|
0xFF0F => {
|
||||||
self.interrupt_request_flags = val;
|
self.interrupt_request_flags = val;
|
||||||
}
|
}
|
||||||
0xFF10 ... 0xFF26 => {
|
0xFF10...0xFF26 => {
|
||||||
self.sound.sound_object.lock().unwrap().write_byte(addr, val);
|
self.sound
|
||||||
},
|
.sound_object
|
||||||
0xFF30 ... 0xFF3F => self.sound.sound_object.lock().unwrap().write_byte(addr, val),
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.write_byte(addr, val);
|
||||||
|
}
|
||||||
|
0xFF30...0xFF3F => self
|
||||||
|
.sound
|
||||||
|
.sound_object
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.write_byte(addr, val),
|
||||||
// Exclude DMA transfer, we will do this below
|
// Exclude DMA transfer, we will do this below
|
||||||
0xFF40 ... 0xFF45 | 0xFF47 ... 0xFF4B => {
|
0xFF40..=0xFF45 | 0xFF47..=0xFF4B => {
|
||||||
self.display.write_byte(addr, val);
|
self.display.write_byte(addr, val);
|
||||||
},
|
}
|
||||||
0xFF46 => {
|
0xFF46 => {
|
||||||
// println!("OAM DMA transfer");
|
// 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);
|
let dma_b = self.read_byte(((val as u16) << 8) | x);
|
||||||
self.write_byte(0xFE00 | x, dma_b);
|
self.write_byte(0xFE00 | x, dma_b);
|
||||||
}
|
}
|
||||||
@ -322,17 +372,22 @@ impl Interconnect {
|
|||||||
0xFF50 => {
|
0xFF50 => {
|
||||||
println!("Disabling boot rom.");
|
println!("Disabling boot rom.");
|
||||||
self.disable_bootrom = val;
|
self.disable_bootrom = val;
|
||||||
},
|
}
|
||||||
0xFF51 => self.vram_dma_source_high = val,
|
0xFF51 => self.vram_dma_source_high = val,
|
||||||
0xFF52 => self.vram_dma_source_low = val & 0xF0,
|
0xFF52 => self.vram_dma_source_low = val & 0xF0,
|
||||||
0xFF53 => self.vram_dma_destination_high = val & 0x1F,
|
0xFF53 => self.vram_dma_destination_high = val & 0x1F,
|
||||||
0xFF54 => self.vram_dma_destination_low = val & 0xF0,
|
0xFF54 => self.vram_dma_destination_low = val & 0xF0,
|
||||||
0xFF55 => {
|
0xFF55 => {
|
||||||
let src: u16 = ((self.vram_dma_source_high as u16) << 8) | self.vram_dma_source_low as u16;
|
let src: u16 =
|
||||||
let mut dst: u16 = ((self.vram_dma_destination_high as u16) << 8) | self.vram_dma_destination_low as 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;
|
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;
|
let len: u16 = ((val & 0x7F) + 1) as u16 * 0x10 - 1;
|
||||||
for i in 0..len {
|
for i in 0..len {
|
||||||
let v = self.read_byte(src.wrapping_add(i));
|
let v = self.read_byte(src.wrapping_add(i));
|
||||||
@ -344,7 +399,7 @@ impl Interconnect {
|
|||||||
}
|
}
|
||||||
0xFF56 => {
|
0xFF56 => {
|
||||||
self.infrared_com_port = val;
|
self.infrared_com_port = val;
|
||||||
},
|
}
|
||||||
0xFF70 => {
|
0xFF70 => {
|
||||||
if self.wram_bank != val {
|
if self.wram_bank != val {
|
||||||
println!("Switching wram bank to {:02X}", val);
|
println!("Switching wram bank to {:02X}", val);
|
||||||
@ -358,13 +413,13 @@ impl Interconnect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0xFF80 ... 0xFFFE => {
|
0xFF80..=0xFFFE => {
|
||||||
self.hiram[(addr - 0xFF80) as usize] = val;
|
self.hiram[(addr - 0xFF80) as usize] = val;
|
||||||
},
|
}
|
||||||
0xFFFF => {
|
0xFFFF => {
|
||||||
println!("Setting interrupt mask value {:02X}", val);
|
println!("Setting interrupt mask value {:02X}", val);
|
||||||
self.interrupt = val;
|
self.interrupt = val;
|
||||||
},
|
}
|
||||||
_ => {
|
_ => {
|
||||||
println!("Write {:02X} to {:04X} not supported.", val, addr);
|
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.
|
// let's try to write our own, awesome emulator.
|
||||||
// gameboy (color?)
|
// gameboy (color?)
|
||||||
|
|
||||||
use std::path::Path;
|
use std::env;
|
||||||
|
use std::fs;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::fs;
|
use std::path::Path;
|
||||||
use std::env;
|
|
||||||
|
|
||||||
mod cartridge;
|
mod cartridge;
|
||||||
mod cpu;
|
mod cpu;
|
||||||
mod display;
|
mod display;
|
||||||
mod interconnect;
|
mod interconnect;
|
||||||
|
mod mbc;
|
||||||
|
mod serial;
|
||||||
mod sound;
|
mod sound;
|
||||||
mod timer;
|
mod timer;
|
||||||
mod serial;
|
|
||||||
mod mbc;
|
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args: Vec<String> = env::args().collect();
|
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> {
|
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();
|
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())
|
Ok(buf.into_boxed_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_file<P: AsRef<Path>>(path: P, data: &Box<[u8]>) -> Result<(), io::Error> {
|
pub fn write_file<P: AsRef<Path>>(path: P, data: &Box<[u8]>) -> Result<(), io::Error> {
|
||||||
let mut file = try!(fs::File::create(path));
|
let mut file = r#try!(fs::File::create(path));
|
||||||
try!(file.write(&data));
|
r#try!(file.write(&data));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,31 +12,34 @@ pub struct NoMBC {
|
|||||||
|
|
||||||
impl NoMBC {
|
impl NoMBC {
|
||||||
pub fn new(rom: Box<[u8]>, ram: Box<[u8]>) -> NoMBC {
|
pub fn new(rom: Box<[u8]>, ram: Box<[u8]>) -> NoMBC {
|
||||||
NoMBC {
|
NoMBC { rom: rom, ram: ram }
|
||||||
rom: rom,
|
|
||||||
ram: ram,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MBC for NoMBC {
|
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");
|
super::super::write_file(&file, &self.ram).expect("Saving failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_byte(&mut self, addr: u16, val: u8) {
|
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 {
|
fn read_byte(&self, addr: u16) -> u8 {
|
||||||
match addr {
|
match addr {
|
||||||
0x0000 ... 0x7FFF => self.rom[addr as usize],
|
0x0000..=0x7FFF => self.rom[addr as usize],
|
||||||
0xA000 ... 0xBFFF => {
|
0xA000..=0xBFFF => {
|
||||||
// TODO: Check for ram
|
// TODO: Check for ram
|
||||||
let addr = (addr as usize) - 0xA000;
|
let addr = (addr as usize) - 0xA000;
|
||||||
|
|
||||||
if addr >= self.ram.len() {
|
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
|
0
|
||||||
} else {
|
} else {
|
||||||
self.ram[addr]
|
self.ram[addr]
|
||||||
|
|||||||
@ -30,33 +30,33 @@ impl MBC1 {
|
|||||||
fn active_rom_bank(&self) -> u8 {
|
fn active_rom_bank(&self) -> u8 {
|
||||||
match self.bank_mode {
|
match self.bank_mode {
|
||||||
BankMode::RomBankMode => self.rom_bank_no | (self.bank_no_high << 5),
|
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 {
|
fn active_ram_bank(&self) -> u8 {
|
||||||
match self.bank_mode {
|
match self.bank_mode {
|
||||||
BankMode::RomBankMode => 0,
|
BankMode::RomBankMode => 0,
|
||||||
BankMode::RamBankMode => self.bank_no_high
|
BankMode::RamBankMode => self.bank_no_high,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MBC for MBC1 {
|
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");
|
super::super::write_file(&file, &self.ram).expect("Saving failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_byte(&self, addr: u16) -> u8 {
|
fn read_byte(&self, addr: u16) -> u8 {
|
||||||
match addr {
|
match addr {
|
||||||
0x0000 ... 0x3FFF => self.rom[addr as usize],
|
0x0000..=0x3FFF => self.rom[addr as usize],
|
||||||
0x4000 ... 0x7FFF => {
|
0x4000..=0x7FFF => {
|
||||||
let addr = addr - 0x4000;
|
let addr = addr - 0x4000;
|
||||||
let abs_addr: usize = addr as usize + self.active_rom_bank() as usize * 0x4000;
|
let abs_addr: usize = addr as usize + self.active_rom_bank() as usize * 0x4000;
|
||||||
let val: u8 = self.rom[abs_addr];
|
let val: u8 = self.rom[abs_addr];
|
||||||
val
|
val
|
||||||
},
|
}
|
||||||
0xA000 ... 0xBFFF => {
|
0xA000..=0xBFFF => {
|
||||||
let addr = addr - 0xA000;
|
let addr = addr - 0xA000;
|
||||||
println!("Access [{:02X}] {:04X}", self.active_ram_bank(), addr);
|
println!("Access [{:02X}] {:04X}", self.active_ram_bank(), addr);
|
||||||
self.ram[self.active_ram_bank() as usize * 0x2000 + addr as usize]
|
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) {
|
fn write_byte(&mut self, addr: u16, val: u8) {
|
||||||
match addr {
|
match addr {
|
||||||
0x0000 ... 0x1FFF => {
|
0x0000..=0x1FFF => match val {
|
||||||
match val {
|
0x0A => self.ram_enable = true,
|
||||||
0x0A => self.ram_enable = true,
|
0x00 => self.ram_enable = false,
|
||||||
0x00 => self.ram_enable = false,
|
_ => println!("Unknown MBC1 value {:02X} for {:04X}", val, addr),
|
||||||
_ => println!("Unknown MBC1 value {:02X} for {:04X}", val, addr)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
0x2000 ... 0x3FFF => {
|
0x2000..=0x3FFF => {
|
||||||
if val != 0 {
|
if val != 0 {
|
||||||
self.rom_bank_no = val & 0x1F;
|
self.rom_bank_no = val & 0x1F;
|
||||||
} else {
|
} else {
|
||||||
@ -84,18 +82,18 @@ impl MBC for MBC1 {
|
|||||||
}
|
}
|
||||||
println!("MBC1: Selecting bank {:02X}", self.rom_bank_no);
|
println!("MBC1: Selecting bank {:02X}", self.rom_bank_no);
|
||||||
}
|
}
|
||||||
0x4000 ... 0x5FFF => {
|
0x4000..=0x5FFF => {
|
||||||
// Upper ROM bank / RAM bank select
|
// Upper ROM bank / RAM bank select
|
||||||
self.bank_no_high = val & 3;
|
self.bank_no_high = val & 3;
|
||||||
},
|
}
|
||||||
0x6000 ... 0x7FFF => {
|
0x6000..=0x7FFF => {
|
||||||
// Select upper ROM bytes or RAM bytes
|
// Select upper ROM bytes or RAM bytes
|
||||||
match val {
|
match val {
|
||||||
0 => self.bank_mode = BankMode::RomBankMode,
|
0 => self.bank_mode = BankMode::RomBankMode,
|
||||||
1 => self.bank_mode = BankMode::RamBankMode,
|
1 => self.bank_mode = BankMode::RamBankMode,
|
||||||
_ => panic!("Invalid bank mode {:02X}", val),
|
_ => panic!("Invalid bank mode {:02X}", val),
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_ => panic!("MBC1: Writing {:02X} to {:04X} not supported", val, addr),
|
_ => panic!("MBC1: Writing {:02X} to {:04X} not supported", val, addr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,23 +20,23 @@ impl MBC2 {
|
|||||||
fn active_rom_bank(&self) -> u8 {
|
fn active_rom_bank(&self) -> u8 {
|
||||||
self.rom_bank_no
|
self.rom_bank_no
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MBC for MBC2 {
|
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");
|
super::super::write_file(&file, &self.ram).expect("Saving failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_byte(&self, addr: u16) -> u8 {
|
fn read_byte(&self, addr: u16) -> u8 {
|
||||||
match addr {
|
match addr {
|
||||||
0x0000 ... 0x3FFF => self.rom[addr as usize],
|
0x0000..=0x3FFF => self.rom[addr as usize],
|
||||||
0x4000 ... 0x7FFF => {
|
0x4000..=0x7FFF => {
|
||||||
let addr = addr - 0x4000;
|
let addr = addr - 0x4000;
|
||||||
let abs_addr: usize = addr as usize + self.active_rom_bank() as usize * 0x4000;
|
let abs_addr: usize = addr as usize + self.active_rom_bank() as usize * 0x4000;
|
||||||
let val: u8 = self.rom[abs_addr];
|
let val: u8 = self.rom[abs_addr];
|
||||||
val
|
val
|
||||||
},
|
}
|
||||||
0xA000 ... 0xA1FF => {
|
0xA000..=0xA1FF => {
|
||||||
let addr = addr - 0xA000;
|
let addr = addr - 0xA000;
|
||||||
self.ram[addr as usize] & 0x0F
|
self.ram[addr as usize] & 0x0F
|
||||||
}
|
}
|
||||||
@ -48,19 +48,19 @@ impl MBC for MBC2 {
|
|||||||
|
|
||||||
fn write_byte(&mut self, addr: u16, val: u8) {
|
fn write_byte(&mut self, addr: u16, val: u8) {
|
||||||
match addr {
|
match addr {
|
||||||
0x0000 ... 0x1FFF => {
|
0x0000..=0x1FFF => {
|
||||||
// To enable the ram, the LSB of the higher byte must be 0
|
// To enable the ram, the LSB of the higher byte must be 0
|
||||||
if addr & 0x0100 == 0 {
|
if addr & 0x0100 == 0 {
|
||||||
match val {
|
match val {
|
||||||
0x0A => self.ram_enable = true,
|
0x0A => self.ram_enable = true,
|
||||||
0x00 => self.ram_enable = false,
|
0x00 => self.ram_enable = false,
|
||||||
_ => println!("Unknown MBC2 value {:02X} for {:04X}", val, addr)
|
_ => println!("Unknown MBC2 value {:02X} for {:04X}", val, addr),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
println!("MBC2: Write {:02X} to {:04X} has no effect", val, addr);
|
println!("MBC2: Write {:02X} to {:04X} has no effect", val, addr);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
0x2000 ... 0x3FFF => {
|
0x2000..=0x3FFF => {
|
||||||
if addr & 0x0100 == 1 {
|
if addr & 0x0100 == 1 {
|
||||||
self.rom_bank_no = val & 0x0F;
|
self.rom_bank_no = val & 0x0F;
|
||||||
println!("MBC2: Selecting bank {:02X}", self.rom_bank_no);
|
println!("MBC2: Selecting bank {:02X}", self.rom_bank_no);
|
||||||
|
|||||||
@ -25,35 +25,37 @@ impl MBC3 {
|
|||||||
fn active_ram_bank(&self) -> u8 {
|
fn active_ram_bank(&self) -> u8 {
|
||||||
self.ram_bank_no
|
self.ram_bank_no
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MBC for MBC3 {
|
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");
|
super::super::write_file(&file, &self.ram).expect("Saving failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_byte(&self, addr: u16) -> u8 {
|
fn read_byte(&self, addr: u16) -> u8 {
|
||||||
match addr {
|
match addr {
|
||||||
0x0000 ... 0x3FFF => self.rom[addr as usize],
|
0x0000..=0x3FFF => self.rom[addr as usize],
|
||||||
0x4000 ... 0x7FFF => {
|
0x4000..=0x7FFF => {
|
||||||
let addr = addr - 0x4000;
|
let addr = addr - 0x4000;
|
||||||
let abs_addr: usize = addr as usize + self.active_rom_bank() as usize * 0x4000;
|
let abs_addr: usize = addr as usize + self.active_rom_bank() as usize * 0x4000;
|
||||||
let val: u8 = self.rom[abs_addr];
|
let val: u8 = self.rom[abs_addr];
|
||||||
val
|
val
|
||||||
},
|
}
|
||||||
0xA000 ... 0xBFFF => {
|
0xA000..=0xBFFF => {
|
||||||
let addr = addr - 0xA000;
|
let addr = addr - 0xA000;
|
||||||
match self.active_ram_bank() {
|
match self.active_ram_bank() {
|
||||||
0x00 ... 0x03 => {
|
0x00..=0x03 => {
|
||||||
self.ram[self.active_ram_bank() as usize * 0x2000 + addr as usize]
|
self.ram[self.active_ram_bank() as usize * 0x2000 + addr as usize]
|
||||||
}
|
}
|
||||||
0x08 ... 0x0C => {
|
0x08..=0x0C => {
|
||||||
// TODO
|
// TODO
|
||||||
println!("MBC3: Ignoring RTC read");
|
println!("MBC3: Ignoring RTC read");
|
||||||
0
|
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) {
|
fn write_byte(&mut self, addr: u16, val: u8) {
|
||||||
match addr {
|
match addr {
|
||||||
0x0000 ... 0x1FFF => {
|
0x0000..=0x1FFF => match val {
|
||||||
match val {
|
0x0A => self.ram_rtc_enabled = true,
|
||||||
0x0A => self.ram_rtc_enabled = true,
|
0x00 => self.ram_rtc_enabled = false,
|
||||||
0x00 => self.ram_rtc_enabled = false,
|
_ => println!("MBC3: Unknown MBC value {:02X} for {:04X}", val, addr),
|
||||||
_ => println!("MBC3: Unknown MBC value {:02X} for {:04X}", val, addr)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
0x2000 ... 0x3FFF => self.rom_bank_no = val & 0x7F,
|
0x2000..=0x3FFF => self.rom_bank_no = val & 0x7F,
|
||||||
0x4000 ... 0x5FFF => {
|
0x4000..=0x5FFF => {
|
||||||
// RAM bank select
|
// RAM bank select
|
||||||
match val {
|
match val {
|
||||||
0x00 ... 0x03 => self.ram_bank_no = val,
|
0x00..=0x03 => self.ram_bank_no = val,
|
||||||
0x08 ... 0x0C => self.ram_bank_no = val,
|
0x08..=0x0C => self.ram_bank_no = val,
|
||||||
_ => panic!("MBC3: Unknown RAM bank {:02X}", val)
|
_ => panic!("MBC3: Unknown RAM bank {:02X}", val),
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
0x6000 ... 0x7FFF => {
|
0x6000..=0x7FFF => {
|
||||||
// Latch clock data
|
// Latch clock data
|
||||||
match val {
|
match val {
|
||||||
0x00 => println!("latch = 0"),
|
0x00 => println!("latch = 0"),
|
||||||
0x01 => println!("latch = 1"), // TODO: This should copy the current clock to the register
|
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;
|
let addr = addr - 0xA000;
|
||||||
match self.active_ram_bank() {
|
match self.active_ram_bank() {
|
||||||
0x00 ... 0x03 => {
|
0x00..=0x03 => {
|
||||||
let active_bank = self.active_ram_bank() as usize;
|
let active_bank = self.active_ram_bank() as usize;
|
||||||
self.ram[active_bank * 0x2000 + addr as usize] = val;
|
self.ram[active_bank * 0x2000 + addr as usize] = val;
|
||||||
}
|
}
|
||||||
0x08 ... 0x0C => {
|
0x08..=0x0C => {
|
||||||
// TODO
|
// TODO
|
||||||
println!("MBC3: Ignoring RTC write ({:02X})", val);
|
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),
|
_ => panic!("MBC3: Writing {:02X} to {:04X} not supported", val, addr),
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
extern crate pulse_simple;
|
extern crate pulse_simple;
|
||||||
mod envelope;
|
mod envelope;
|
||||||
mod square;
|
|
||||||
mod length;
|
mod length;
|
||||||
|
mod square;
|
||||||
mod wave;
|
mod wave;
|
||||||
|
|
||||||
use self::pulse_simple::Playback;
|
use self::pulse_simple::Playback;
|
||||||
@ -30,7 +30,7 @@ struct Channel1 {
|
|||||||
|
|
||||||
tick_state: u8,
|
tick_state: u8,
|
||||||
|
|
||||||
stored_regs: [u8; 5]
|
stored_regs: [u8; 5],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Channel1 {
|
impl Channel1 {
|
||||||
@ -73,7 +73,10 @@ impl Channel1 {
|
|||||||
pub fn read_byte(&self, addr: u16) -> u8 {
|
pub fn read_byte(&self, addr: u16) -> u8 {
|
||||||
match addr {
|
match addr {
|
||||||
0xFF10...0xFF14 => self.stored_regs[(addr - 0xFF10) as usize],
|
0xFF10...0xFF14 => self.stored_regs[(addr - 0xFF10) as usize],
|
||||||
_ => {println!("Channel1 does not support reading ({:04X})", addr); 0},
|
_ => {
|
||||||
|
println!("Channel1 does not support reading ({:04X})", addr);
|
||||||
|
0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +153,10 @@ impl Channel2 {
|
|||||||
|
|
||||||
pub fn read_byte(&self, addr: u16) -> u8 {
|
pub fn read_byte(&self, addr: u16) -> u8 {
|
||||||
match addr {
|
match addr {
|
||||||
_ => {println!("Channel2 does not support reading ({:04X})", addr); 0},
|
_ => {
|
||||||
|
println!("Channel2 does not support reading ({:04X})", addr);
|
||||||
|
0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,7 +198,6 @@ struct Channel3 {
|
|||||||
wave_gen: wave::WaveGenerator,
|
wave_gen: wave::WaveGenerator,
|
||||||
length_counter: length::LengthCounter<u8>,
|
length_counter: length::LengthCounter<u8>,
|
||||||
// TODO: Volume
|
// TODO: Volume
|
||||||
|
|
||||||
tick_state: u8,
|
tick_state: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,7 +273,6 @@ impl Channel3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Sound {
|
pub struct Sound {
|
||||||
channel1: Channel1,
|
channel1: Channel1,
|
||||||
@ -299,36 +303,39 @@ impl SoundManager {
|
|||||||
if false {
|
if false {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
thread::Builder::new().name("Audio".into()).spawn(move || {
|
thread::Builder::new()
|
||||||
// PulseAudio playback object
|
.name("Audio".into())
|
||||||
let playback = Playback::new("GBC", "Output", None, (OUTPUT_SAMPLE_RATE) as _ );
|
.spawn(move || {
|
||||||
|
// PulseAudio playback object
|
||||||
|
let playback = Playback::new("GBC", "Output", None, (OUTPUT_SAMPLE_RATE) as _);
|
||||||
|
|
||||||
// Counter, used for calling the 512 Hz timer
|
// Counter, used for calling the 512 Hz timer
|
||||||
let mut counter = 0;
|
let mut counter = 0;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let (s1, s2) = {
|
let (s1, s2) = {
|
||||||
let mut c_obj = obj.lock().unwrap();
|
let mut c_obj = obj.lock().unwrap();
|
||||||
|
|
||||||
// Check for 512 Hz timer
|
// Check for 512 Hz timer
|
||||||
if counter >= (OUTPUT_SAMPLE_RATE / 512) {
|
if counter >= (OUTPUT_SAMPLE_RATE / 512) {
|
||||||
c_obj.clock();
|
c_obj.clock();
|
||||||
counter = 0;
|
counter = 0;
|
||||||
} else {
|
} else {
|
||||||
counter += 1;
|
counter += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sample clock
|
// Sample clock
|
||||||
c_obj.sample_clock();
|
c_obj.sample_clock();
|
||||||
|
|
||||||
// Get sample
|
// Get sample
|
||||||
c_obj.sample()
|
c_obj.sample()
|
||||||
};
|
};
|
||||||
let samps = [[s1, s2]];
|
let samps = [[s1, s2]];
|
||||||
// No sleep needed, it seems like the playback.write is blocking
|
// No sleep needed, it seems like the playback.write is blocking
|
||||||
playback.write(&samps[..]);
|
playback.write(&samps[..]);
|
||||||
}
|
}
|
||||||
}).unwrap();
|
})
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,38 +372,38 @@ impl Sound {
|
|||||||
if s01_volume > 0 {
|
if s01_volume > 0 {
|
||||||
if self.sound_output_terminal_selector & 1 > 0 {
|
if self.sound_output_terminal_selector & 1 > 0 {
|
||||||
// Output Channel1
|
// Output Channel1
|
||||||
s[0] += c1_sample as _;
|
s[0] += c1_sample as u16;
|
||||||
}
|
}
|
||||||
if self.sound_output_terminal_selector & 2 > 0 && false {
|
if self.sound_output_terminal_selector & 2 > 0 && false {
|
||||||
// Output Channel2
|
// Output Channel2
|
||||||
s[0] += c2_sample as _;
|
s[0] += c2_sample as u16;
|
||||||
}
|
}
|
||||||
if self.sound_output_terminal_selector & 4 > 0 {
|
if self.sound_output_terminal_selector & 4 > 0 {
|
||||||
// Output Channel3
|
// Output Channel3
|
||||||
s[0] += c3_sample as _;
|
s[0] += c3_sample as u16;
|
||||||
}
|
}
|
||||||
if self.sound_output_terminal_selector & 8 > 0 {
|
if self.sound_output_terminal_selector & 8 > 0 {
|
||||||
// Output Channel4
|
// Output Channel4
|
||||||
s[0] += c4_sample as _;
|
s[0] += c4_sample as u16;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if s02_volume > 0 {
|
if s02_volume > 0 {
|
||||||
if self.sound_output_terminal_selector & 0x10 > 0 && false {
|
if self.sound_output_terminal_selector & 0x10 > 0 && false {
|
||||||
// Output Channel1
|
// Output Channel1
|
||||||
s[1] += c1_sample as _;
|
s[1] += c1_sample as u16;
|
||||||
}
|
}
|
||||||
if self.sound_output_terminal_selector & 0x20 > 0 {
|
if self.sound_output_terminal_selector & 0x20 > 0 {
|
||||||
// Output Channel2
|
// Output Channel2
|
||||||
s[1] += c2_sample as _;
|
s[1] += c2_sample as u16;
|
||||||
}
|
}
|
||||||
if self.sound_output_terminal_selector & 0x40 > 0 {
|
if self.sound_output_terminal_selector & 0x40 > 0 {
|
||||||
// Output Channel3
|
// Output Channel3
|
||||||
s[1] += c3_sample as _;
|
s[1] += c3_sample as u16;
|
||||||
}
|
}
|
||||||
if self.sound_output_terminal_selector & 0x80 > 0 {
|
if self.sound_output_terminal_selector & 0x80 > 0 {
|
||||||
// Output Channel4
|
// Output Channel4
|
||||||
s[1] += c4_sample as _;
|
s[1] += c4_sample as u16;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -419,7 +426,7 @@ impl Sound {
|
|||||||
_ => {
|
_ => {
|
||||||
// panic!("Sound: Write {:02X} to {:04X} unsupported", val, addr);
|
// panic!("Sound: Write {:02X} to {:04X} unsupported", val, addr);
|
||||||
println!("Sound: Write {:02X} to {:04X} unsupported", val, addr);
|
println!("Sound: Write {:02X} to {:04X} unsupported", val, addr);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
11
src/timer.rs
11
src/timer.rs
@ -74,15 +74,16 @@ impl Timer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_byte(&self, addr: u16) -> u8 {
|
pub fn read_byte(&self, addr: u16) -> u8 {
|
||||||
let r = match addr {
|
match addr {
|
||||||
0xFF04 => self.div,
|
0xFF04 => self.div,
|
||||||
0xFF05 => self.tima,
|
0xFF05 => self.tima,
|
||||||
0xFF06 => self.tma,
|
0xFF06 => self.tma,
|
||||||
0xFF07 => self.tac,
|
0xFF07 => self.tac,
|
||||||
_ => unreachable!(),
|
_ => {
|
||||||
};
|
println!("Timer: Read from {:04X} unsupported", addr);
|
||||||
// println!("Timer RD: {:04X} = {:02X}", addr, r);
|
0
|
||||||
r
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn timer_interrupt(&mut self) -> bool {
|
pub fn timer_interrupt(&mut self) -> bool {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user