Compare commits
No commits in common. "3b282682794a3520d63dbfc6a1e28305f7275881" and "6efb1507ff9ed3f1628c0d788db69d1131a02749" have entirely different histories.
3b28268279
...
6efb1507ff
39
shell.nix
39
shell.nix
@ -1,39 +0,0 @@
|
|||||||
let
|
|
||||||
moz_overlay = import (builtins.fetchTarball https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz);
|
|
||||||
nixpkgs = import <nixpkgs> { overlays = [ moz_overlay ]; };
|
|
||||||
rustNightlyChannelClippy = (nixpkgs.rustChannelOf { date = "2020-01-29"; channel = "nightly"; }).rust.override {
|
|
||||||
extensions = [
|
|
||||||
"rust-src"
|
|
||||||
"rls-preview"
|
|
||||||
"rustfmt-preview"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
rustNightlyChannel = nixpkgs.latest.rustChannels.nightly.rust.override {
|
|
||||||
extensions = [
|
|
||||||
"rust-src"
|
|
||||||
"rls-preview"
|
|
||||||
"rustfmt-preview"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
rustStableChannel = nixpkgs.latest.rustChannels.stable.rust.override {
|
|
||||||
extensions = [
|
|
||||||
"rust-src"
|
|
||||||
"rls-preview"
|
|
||||||
"clippy-preview"
|
|
||||||
"rustfmt-preview"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
in
|
|
||||||
with nixpkgs;
|
|
||||||
stdenv.mkDerivation {
|
|
||||||
name = "moz_overlay_shell";
|
|
||||||
buildInputs = [
|
|
||||||
rustNightlyChannelClippy
|
|
||||||
rls
|
|
||||||
rustup
|
|
||||||
pkg-config
|
|
||||||
SDL2
|
|
||||||
pulseaudio
|
|
||||||
];
|
|
||||||
TELOXIDE_TOKEN = "916718418:AAFFtn9WmqipuKCvwiXZ2cEIA_IuNqlO94I";
|
|
||||||
}
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
use crate::mbc;
|
use crate::mbc::{mbc, mbc1, mbc2, mbc3, mbc5};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
enum MemoryBankControllerType {
|
enum MemoryBankControllerType {
|
||||||
@ -24,7 +24,7 @@ pub struct Cartridge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Cartridge {
|
impl Cartridge {
|
||||||
pub fn new(rom: Box<[u8]>, savefile: Option<String>) -> Cartridge {
|
pub fn new(rom: Box<[u8]>, save_file: Option<String>) -> Cartridge {
|
||||||
let mbc_type: MemoryBankControllerType = match rom[0x0147] {
|
let mbc_type: MemoryBankControllerType = match rom[0x0147] {
|
||||||
0x00 | 0x08..=0x09 => MemoryBankControllerType::None,
|
0x00 | 0x08..=0x09 => MemoryBankControllerType::None,
|
||||||
0x01..=0x03 => MemoryBankControllerType::MBC1,
|
0x01..=0x03 => MemoryBankControllerType::MBC1,
|
||||||
@ -57,20 +57,23 @@ impl Cartridge {
|
|||||||
println!("Rom size: {} banks", rom_banks);
|
println!("Rom size: {} banks", rom_banks);
|
||||||
println!("Ram size: {:?}", ram_size);
|
println!("Ram size: {:?}", ram_size);
|
||||||
|
|
||||||
let ram = Cartridge::load_savefile(&savefile, ram_size);
|
let ram = Cartridge::load_savefile(&save_file, ram_size);
|
||||||
|
|
||||||
let mbc: Box<dyn mbc::MBC> = match mbc_type {
|
let mbc: Box<dyn mbc::MBC> = match mbc_type {
|
||||||
MemoryBankControllerType::None => Box::new(mbc::NoMBC::new(rom, ram)),
|
MemoryBankControllerType::None => Box::new(mbc::NoMBC::new(rom, ram)),
|
||||||
MemoryBankControllerType::MBC1 => Box::new(mbc::MBC1::new(rom, ram)),
|
MemoryBankControllerType::MBC1 => Box::new(mbc1::MBC1::new(rom, ram)),
|
||||||
MemoryBankControllerType::MBC2 => Box::new(mbc::MBC2::new(rom, ram)),
|
MemoryBankControllerType::MBC2 => Box::new(mbc2::MBC2::new(rom, ram)),
|
||||||
MemoryBankControllerType::MBC3 => Box::new(mbc::MBC3::new(rom, ram)),
|
MemoryBankControllerType::MBC3 => Box::new(mbc3::MBC3::new(rom, ram)),
|
||||||
MemoryBankControllerType::MBC5 => Box::new(mbc::MBC5::new(rom, ram)),
|
MemoryBankControllerType::MBC5 => Box::new(mbc5::MBC5::new(rom, ram)),
|
||||||
};
|
};
|
||||||
|
|
||||||
Cartridge { mbc, savefile }
|
Cartridge {
|
||||||
|
mbc: mbc,
|
||||||
|
savefile: save_file,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_savefile(savefile: &Option<String>, ram_size: RamSize) -> Box<[u8]> {
|
fn load_savefile(save_file: &Option<String>, ram_size: RamSize) -> Box<[u8]> {
|
||||||
let size = match ram_size {
|
let size = match ram_size {
|
||||||
RamSize::None => 0,
|
RamSize::None => 0,
|
||||||
RamSize::Ram2KB => 2048,
|
RamSize::Ram2KB => 2048,
|
||||||
@ -78,7 +81,7 @@ impl Cartridge {
|
|||||||
RamSize::Ram32KB => 16 * 2048,
|
RamSize::Ram32KB => 16 * 2048,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(ref filename) = *savefile {
|
if let &Some(ref filename) = save_file {
|
||||||
let data = super::read_file(&filename);
|
let data = super::read_file(&filename);
|
||||||
if let Ok(data) = data {
|
if let Ok(data) = data {
|
||||||
if data.len() != size {
|
if data.len() != size {
|
||||||
|
|||||||
125
src/cpu.rs
125
src/cpu.rs
@ -1,6 +1,5 @@
|
|||||||
use crate::interconnect;
|
use super::interconnect;
|
||||||
|
|
||||||
use std::convert::TryFrom;
|
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
@ -15,7 +14,7 @@ const REG_N_L: usize = 5;
|
|||||||
const REG_N_HL: usize = 6;
|
const REG_N_HL: usize = 6;
|
||||||
const REG_N_A: usize = 7;
|
const REG_N_A: usize = 7;
|
||||||
const REG_N_F: usize = 8;
|
const REG_N_F: usize = 8;
|
||||||
const REG_NAMES: [&str; 9] = ["B", "C", "D", "E", "H", "L", "(HL)", "A", "F"];
|
const REG_NAMES: [&'static str; 9] = ["B", "C", "D", "E", "H", "L", "(HL)", "A", "F"];
|
||||||
|
|
||||||
const FLAG_Z: u8 = 1 << 7;
|
const FLAG_Z: u8 = 1 << 7;
|
||||||
const FLAG_N: u8 = 1 << 6;
|
const FLAG_N: u8 = 1 << 6;
|
||||||
@ -45,29 +44,23 @@ pub struct CPU {
|
|||||||
trigger_once: bool,
|
trigger_once: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_u16(args: Args) -> u16 {
|
||||||
|
match args {
|
||||||
|
Args::Double(a, b) => (a as u16) | ((b as u16) << 8),
|
||||||
|
_ => panic!("to_u16 only works with Args::Double"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum Args {
|
enum Args {
|
||||||
Single(u8),
|
Single(u8),
|
||||||
Double(u8, u8),
|
Double(u8, u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<Args> for u8 {
|
impl Args {
|
||||||
type Error = ();
|
fn single_val(&self) -> u8 {
|
||||||
|
match self {
|
||||||
fn try_from(val: Args) -> Result<u8, Self::Error> {
|
&Args::Single(x) => x,
|
||||||
match val {
|
_ => panic!("single_val only works with Args::Single"),
|
||||||
Args::Single(x) => Ok(x),
|
|
||||||
_ => Err(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<Args> for u16 {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn try_from(val: Args) -> Result<u16, Self::Error> {
|
|
||||||
match val {
|
|
||||||
Args::Double(a, b) => Ok((a as u16) | ((b as u16) << 8)),
|
|
||||||
_ => Err(()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,7 +72,7 @@ impl CPU {
|
|||||||
regs: [0, 0, 0, 0, 0, 0, 0],
|
regs: [0, 0, 0, 0, 0, 0, 0],
|
||||||
ip: 0,
|
ip: 0,
|
||||||
sp: 0xFFFE,
|
sp: 0xFFFE,
|
||||||
interconnect,
|
interconnect: interconnect,
|
||||||
ime: false,
|
ime: false,
|
||||||
debug: false,
|
debug: false,
|
||||||
halted: false,
|
halted: false,
|
||||||
@ -88,10 +81,12 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn read_byte(&self, addr: u16) -> u8 {
|
fn read_byte(&self, addr: u16) -> u8 {
|
||||||
self.interconnect.read_byte(addr)
|
self.interconnect.read_byte(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn load_args(&mut self, num_args: u8) -> Args {
|
fn load_args(&mut self, num_args: u8) -> Args {
|
||||||
match num_args {
|
match num_args {
|
||||||
1 => {
|
1 => {
|
||||||
@ -110,6 +105,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn set_8bit_reg(&mut self, reg_id: usize, value: u8) {
|
fn set_8bit_reg(&mut self, reg_id: usize, value: u8) {
|
||||||
// Make sure that we skip the (HL) part.
|
// Make sure that we skip the (HL) part.
|
||||||
if reg_id == REG_N_A {
|
if reg_id == REG_N_A {
|
||||||
@ -124,6 +120,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn get_8bit_reg(&self, reg_id: usize) -> u8 {
|
fn get_8bit_reg(&self, reg_id: usize) -> u8 {
|
||||||
// Make sure that we skip the (HL) part.
|
// Make sure that we skip the (HL) part.
|
||||||
if reg_id == REG_N_A {
|
if reg_id == REG_N_A {
|
||||||
@ -138,6 +135,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn adc_r(&mut self, val: u8) {
|
fn adc_r(&mut self, val: u8) {
|
||||||
let old: u8 = self.regs[REG_A];
|
let old: u8 = self.regs[REG_A];
|
||||||
let mut new: u8 = old;
|
let mut new: u8 = old;
|
||||||
@ -157,6 +155,7 @@ impl CPU {
|
|||||||
self.set_clear_flag(FLAG_H, ((old & 0x0F) + (val & 0x0F) + c) > 0x0F);
|
self.set_clear_flag(FLAG_H, ((old & 0x0F) + (val & 0x0F) + c) > 0x0F);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn add_r(&mut self, val: u8) {
|
fn add_r(&mut self, val: u8) {
|
||||||
let old: u8 = self.regs[REG_A];
|
let old: u8 = self.regs[REG_A];
|
||||||
let new: u8 = old.wrapping_add(val);
|
let new: u8 = old.wrapping_add(val);
|
||||||
@ -169,6 +168,7 @@ impl CPU {
|
|||||||
self.set_clear_flag(FLAG_H, carry & 0x10 == 0x10);
|
self.set_clear_flag(FLAG_H, carry & 0x10 == 0x10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn sbc_r(&mut self, val: u8) {
|
fn sbc_r(&mut self, val: u8) {
|
||||||
let old: u8 = self.regs[REG_A];
|
let old: u8 = self.regs[REG_A];
|
||||||
let mut new: u8 = old as u8;
|
let mut new: u8 = old as u8;
|
||||||
@ -192,6 +192,7 @@ impl CPU {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn sub_r(&mut self, val: u8) {
|
fn sub_r(&mut self, val: u8) {
|
||||||
let old: u8 = self.regs[REG_A];
|
let old: u8 = self.regs[REG_A];
|
||||||
let new: u8 = old.wrapping_sub(val);
|
let new: u8 = old.wrapping_sub(val);
|
||||||
@ -204,6 +205,7 @@ impl CPU {
|
|||||||
self.set_clear_flag(FLAG_H, carry & 0x10 == 0x10);
|
self.set_clear_flag(FLAG_H, carry & 0x10 == 0x10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn cp_r(&mut self, val: u8) {
|
fn cp_r(&mut self, val: u8) {
|
||||||
let old: u8 = self.regs[REG_A];
|
let old: u8 = self.regs[REG_A];
|
||||||
let new: u8 = old.wrapping_sub(val);
|
let new: u8 = old.wrapping_sub(val);
|
||||||
@ -215,6 +217,7 @@ impl CPU {
|
|||||||
self.set_clear_flag(FLAG_H, carry & 0x10 == 0x10);
|
self.set_clear_flag(FLAG_H, carry & 0x10 == 0x10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn run_prefix_instruction(&mut self) {
|
fn run_prefix_instruction(&mut self) {
|
||||||
let instruction = self.read_byte(self.ip);
|
let instruction = self.read_byte(self.ip);
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
@ -585,15 +588,18 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn get_pair_value(&self, a: usize, b: usize) -> u16 {
|
fn get_pair_value(&self, a: usize, b: usize) -> u16 {
|
||||||
(self.get_8bit_reg(a) as u16) << 8 | self.get_8bit_reg(b) as u16
|
(self.get_8bit_reg(a) as u16) << 8 | self.get_8bit_reg(b) as u16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn set_pair_value(&mut self, a: usize, b: usize, value: u16) {
|
fn set_pair_value(&mut self, a: usize, b: usize, value: u16) {
|
||||||
self.set_8bit_reg(a, (value >> 8) as u8);
|
self.set_8bit_reg(a, (value >> 8) as u8);
|
||||||
self.set_8bit_reg(b, value as u8);
|
self.set_8bit_reg(b, value as u8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn call(&mut self, dst: u16) {
|
fn call(&mut self, dst: u16) {
|
||||||
let ip = self.ip;
|
let ip = self.ip;
|
||||||
self.push(ip);
|
self.push(ip);
|
||||||
@ -601,8 +607,9 @@ impl CPU {
|
|||||||
self.ip = dst;
|
self.ip = dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn call_condition(&mut self, cond_str: &'static str, cond: bool) -> u8 {
|
fn call_condition(&mut self, cond_str: &'static str, cond: bool) -> u8 {
|
||||||
let dst = u16::try_from(self.load_args(2)).unwrap();
|
let dst = to_u16(self.load_args(2));
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("CALL {} {:04X}", cond_str, dst);
|
println!("CALL {} {:04X}", cond_str, dst);
|
||||||
}
|
}
|
||||||
@ -614,6 +621,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn ret_condition(&mut self, cond_str: &'static str, cond: bool) -> u8 {
|
fn ret_condition(&mut self, cond_str: &'static str, cond: bool) -> u8 {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("RET {}", cond_str);
|
println!("RET {}", cond_str);
|
||||||
@ -626,6 +634,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn jmp_r(&mut self, addr: u8) {
|
fn jmp_r(&mut self, addr: u8) {
|
||||||
let off: i8 = addr as i8;
|
let off: i8 = addr as i8;
|
||||||
if off < 0 {
|
if off < 0 {
|
||||||
@ -635,8 +644,9 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn jmp_r_condition(&mut self, cond_str: &'static str, cond: bool) -> u8 {
|
fn jmp_r_condition(&mut self, cond_str: &'static str, cond: bool) -> u8 {
|
||||||
let t = u8::try_from(self.load_args(1)).unwrap();
|
let t = self.load_args(1).single_val();
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("JR {} {:02X}", cond_str, t);
|
println!("JR {} {:02X}", cond_str, t);
|
||||||
}
|
}
|
||||||
@ -648,12 +658,14 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn jmp_p(&mut self, addr: u16) {
|
fn jmp_p(&mut self, addr: u16) {
|
||||||
self.ip = addr;
|
self.ip = addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn jmp_p_condition(&mut self, cond_str: &'static str, cond: bool) -> u8 {
|
fn jmp_p_condition(&mut self, cond_str: &'static str, cond: bool) -> u8 {
|
||||||
let t = u16::try_from(self.load_args(2)).unwrap();
|
let t = to_u16(self.load_args(2));
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("JP {} {:04X}", cond_str, t);
|
println!("JP {} {:04X}", cond_str, t);
|
||||||
}
|
}
|
||||||
@ -665,6 +677,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn rst(&mut self, val: u8) -> u8 {
|
fn rst(&mut self, val: u8) -> u8 {
|
||||||
// Make sure this is correct.
|
// Make sure this is correct.
|
||||||
if self.debug {
|
if self.debug {
|
||||||
@ -674,6 +687,7 @@ impl CPU {
|
|||||||
16
|
16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn pop_rr(&mut self, r1: usize, r2: usize) -> u8 {
|
fn pop_rr(&mut self, r1: usize, r2: usize) -> u8 {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("POP {}{}", REG_NAMES[r1], REG_NAMES[r2]);
|
println!("POP {}{}", REG_NAMES[r1], REG_NAMES[r2]);
|
||||||
@ -683,6 +697,7 @@ impl CPU {
|
|||||||
12
|
12
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn push_rr(&mut self, r1: usize, r2: usize) -> u8 {
|
fn push_rr(&mut self, r1: usize, r2: usize) -> u8 {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("PUSH {}{}", REG_NAMES[r1], REG_NAMES[r2]);
|
println!("PUSH {}{}", REG_NAMES[r1], REG_NAMES[r2]);
|
||||||
@ -692,6 +707,7 @@ impl CPU {
|
|||||||
16
|
16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn dec_rr(&mut self, r1: usize, r2: usize) -> u8 {
|
fn dec_rr(&mut self, r1: usize, r2: usize) -> u8 {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("DEC {}{}", REG_NAMES[r1], REG_NAMES[r2]);
|
println!("DEC {}{}", REG_NAMES[r1], REG_NAMES[r2]);
|
||||||
@ -702,6 +718,7 @@ impl CPU {
|
|||||||
8
|
8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn inc_rr(&mut self, r1: usize, r2: usize) -> u8 {
|
fn inc_rr(&mut self, r1: usize, r2: usize) -> u8 {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("INC {}{}", REG_NAMES[r1], REG_NAMES[r2]);
|
println!("INC {}{}", REG_NAMES[r1], REG_NAMES[r2]);
|
||||||
@ -712,17 +729,20 @@ impl CPU {
|
|||||||
8
|
8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn push(&mut self, val: u16) {
|
fn push(&mut self, val: u16) {
|
||||||
self.interconnect.write_word(self.sp - 2, val);
|
self.interconnect.write_word(self.sp - 2, val);
|
||||||
self.sp -= 2;
|
self.sp -= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn pop(&mut self) -> u16 {
|
fn pop(&mut self) -> u16 {
|
||||||
let v: u16 = self.interconnect.read_word(self.sp);
|
let v: u16 = self.interconnect.read_word(self.sp);
|
||||||
self.sp += 2;
|
self.sp += 2;
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn ld_r_r(&mut self, reg_dst: usize, reg_src: usize) -> u8 {
|
fn ld_r_r(&mut self, reg_dst: usize, reg_src: usize) -> u8 {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("LD {}, {}", REG_NAMES[reg_dst], REG_NAMES[reg_src])
|
println!("LD {}, {}", REG_NAMES[reg_dst], REG_NAMES[reg_src])
|
||||||
@ -736,8 +756,9 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn ld_r_v(&mut self, r: usize) -> u8 {
|
fn ld_r_v(&mut self, r: usize) -> u8 {
|
||||||
let val = u8::try_from(self.load_args(1)).unwrap();
|
let val: u8 = self.load_args(1).single_val();
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("LD {}, {:02X}", REG_NAMES[r], val);
|
println!("LD {}, {:02X}", REG_NAMES[r], val);
|
||||||
}
|
}
|
||||||
@ -749,8 +770,9 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn ld_rr_vv(&mut self, r1: usize, r2: usize) -> u8 {
|
fn ld_rr_vv(&mut self, r1: usize, r2: usize) -> u8 {
|
||||||
let val = u16::try_from(self.load_args(2)).unwrap();
|
let val: u16 = to_u16(self.load_args(2));
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("LD {}{}, {:04X}", REG_NAMES[r1], REG_NAMES[r2], val);
|
println!("LD {}{}, {:04X}", REG_NAMES[r1], REG_NAMES[r2], val);
|
||||||
}
|
}
|
||||||
@ -758,6 +780,7 @@ impl CPU {
|
|||||||
12
|
12
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn reg_inc(&mut self, reg_id: usize) -> u8 {
|
fn reg_inc(&mut self, reg_id: usize) -> u8 {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("INC {}", REG_NAMES[reg_id]);
|
println!("INC {}", REG_NAMES[reg_id]);
|
||||||
@ -771,6 +794,7 @@ impl CPU {
|
|||||||
4
|
4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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!(
|
println!(
|
||||||
@ -793,6 +817,7 @@ impl CPU {
|
|||||||
8
|
8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn reg_dec(&mut self, reg_id: usize) -> u8 {
|
fn reg_dec(&mut self, reg_id: usize) -> u8 {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("DEC {}", REG_NAMES[reg_id]);
|
println!("DEC {}", REG_NAMES[reg_id]);
|
||||||
@ -808,6 +833,7 @@ impl CPU {
|
|||||||
4
|
4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn ld_dref_rr_a(&mut self, r1: usize, r2: usize) -> u8 {
|
fn ld_dref_rr_a(&mut self, r1: usize, r2: usize) -> u8 {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("LD ({}{}), A", REG_NAMES[r1], REG_NAMES[r2]);
|
println!("LD ({}{}), A", REG_NAMES[r1], REG_NAMES[r2]);
|
||||||
@ -818,14 +844,17 @@ impl CPU {
|
|||||||
8
|
8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn set_flag(&mut self, flag: u8) {
|
fn set_flag(&mut self, flag: u8) {
|
||||||
self.flags |= flag;
|
self.flags |= flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn clear_flag(&mut self, flag: u8) {
|
fn clear_flag(&mut self, flag: u8) {
|
||||||
self.flags &= !flag;
|
self.flags &= !flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn set_clear_flag(&mut self, flag: u8, dep: bool) {
|
fn set_clear_flag(&mut self, flag: u8, dep: bool) {
|
||||||
if dep {
|
if dep {
|
||||||
self.set_flag(flag);
|
self.set_flag(flag);
|
||||||
@ -834,6 +863,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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);
|
||||||
@ -843,18 +873,21 @@ impl CPU {
|
|||||||
self.call(val as u16);
|
self.call(val as u16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn ret(&mut self) {
|
fn ret(&mut self) {
|
||||||
let new_ip: u16 = self.pop();
|
let new_ip: u16 = self.pop();
|
||||||
//self.dump_stack();
|
//self.dump_stack();
|
||||||
self.ip = new_ip;
|
self.ip = new_ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn reti(&mut self) -> u8 {
|
fn reti(&mut self) -> u8 {
|
||||||
self.ret();
|
self.ret();
|
||||||
self.ime = true;
|
self.ime = true;
|
||||||
16
|
16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn handle_interrupt(&mut self, offset: u8, flag: u8) {
|
fn handle_interrupt(&mut self, offset: u8, flag: u8) {
|
||||||
// Remove interrupt requested flag
|
// Remove interrupt requested flag
|
||||||
let new_flag = self.interconnect.read_byte(0xFF0F) & !flag;
|
let new_flag = self.interconnect.read_byte(0xFF0F) & !flag;
|
||||||
@ -1010,7 +1043,7 @@ impl CPU {
|
|||||||
4
|
4
|
||||||
}
|
}
|
||||||
0x08 => {
|
0x08 => {
|
||||||
let a = u16::try_from(self.load_args(2)).unwrap();
|
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);
|
||||||
}
|
}
|
||||||
@ -1051,7 +1084,7 @@ impl CPU {
|
|||||||
0x10 => {
|
0x10 => {
|
||||||
println!(
|
println!(
|
||||||
"STOP 0 {:02X} not implemented.",
|
"STOP 0 {:02X} not implemented.",
|
||||||
u8::try_from(self.load_args(1)).unwrap()
|
self.load_args(1).single_val()
|
||||||
);
|
);
|
||||||
4
|
4
|
||||||
}
|
}
|
||||||
@ -1079,7 +1112,7 @@ impl CPU {
|
|||||||
4
|
4
|
||||||
}
|
}
|
||||||
0x18 => {
|
0x18 => {
|
||||||
let dst = u8::try_from(self.load_args(1)).unwrap();
|
let dst = self.load_args(1).single_val();
|
||||||
self.jmp_r(dst);
|
self.jmp_r(dst);
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("JMPR {:02X}", dst);
|
println!("JMPR {:02X}", dst);
|
||||||
@ -1208,7 +1241,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
0x31 => {
|
0x31 => {
|
||||||
let args = self.load_args(2);
|
let args = self.load_args(2);
|
||||||
self.sp = u16::try_from(args).unwrap();
|
self.sp = to_u16(args);
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("LD SP, {:04x}", self.sp);
|
println!("LD SP, {:04x}", self.sp);
|
||||||
}
|
}
|
||||||
@ -1425,7 +1458,7 @@ impl CPU {
|
|||||||
self.jmp_p_condition("NZ", c)
|
self.jmp_p_condition("NZ", c)
|
||||||
}
|
}
|
||||||
0xC3 => {
|
0xC3 => {
|
||||||
let dst = u16::try_from(self.load_args(2)).unwrap();
|
let dst = to_u16(self.load_args(2));
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("JMP {:04X}", dst);
|
println!("JMP {:04X}", dst);
|
||||||
}
|
}
|
||||||
@ -1438,7 +1471,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
0xC5 => self.push_rr(REG_N_B, REG_N_C),
|
0xC5 => self.push_rr(REG_N_B, REG_N_C),
|
||||||
0xC6 => {
|
0xC6 => {
|
||||||
let val = u8::try_from(self.load_args(1)).unwrap();
|
let val = self.load_args(1).single_val();
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("ADD A, {:02X}", val);
|
println!("ADD A, {:02X}", val);
|
||||||
}
|
}
|
||||||
@ -1473,7 +1506,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
0xCD => self.call_condition("", true),
|
0xCD => self.call_condition("", true),
|
||||||
0xCE => {
|
0xCE => {
|
||||||
let arg = u8::try_from(self.load_args(1)).unwrap();
|
let arg = self.load_args(1).single_val();
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("ADC A, {:02X}", arg);
|
println!("ADC A, {:02X}", arg);
|
||||||
}
|
}
|
||||||
@ -1498,7 +1531,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
0xD5 => self.push_rr(REG_N_D, REG_N_E),
|
0xD5 => self.push_rr(REG_N_D, REG_N_E),
|
||||||
0xD6 => {
|
0xD6 => {
|
||||||
let val = u8::try_from(self.load_args(1)).unwrap();
|
let val = self.load_args(1).single_val();
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("SUB {:02X}", val);
|
println!("SUB {:02X}", val);
|
||||||
}
|
}
|
||||||
@ -1528,7 +1561,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
0xDD => panic!("NON-EXISTING OPCODE"),
|
0xDD => panic!("NON-EXISTING OPCODE"),
|
||||||
0xDE => {
|
0xDE => {
|
||||||
let arg = u8::try_from(self.load_args(1)).unwrap();
|
let arg = self.load_args(1).single_val();
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("SBC {:02X}", arg);
|
println!("SBC {:02X}", arg);
|
||||||
}
|
}
|
||||||
@ -1538,7 +1571,7 @@ impl CPU {
|
|||||||
0xDF => self.rst(0x18),
|
0xDF => self.rst(0x18),
|
||||||
|
|
||||||
0xE0 => {
|
0xE0 => {
|
||||||
let arg = u8::try_from(self.load_args(1)).unwrap();
|
let arg = self.load_args(1).single_val();
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("LDH {:02X}, A", arg);
|
println!("LDH {:02X}, A", arg);
|
||||||
}
|
}
|
||||||
@ -1558,7 +1591,7 @@ impl CPU {
|
|||||||
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 => {
|
||||||
let val = u8::try_from(self.load_args(1)).unwrap();
|
let val = self.load_args(1).single_val();
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("AND {:02X}", val);
|
println!("AND {:02X}", val);
|
||||||
}
|
}
|
||||||
@ -1573,7 +1606,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
0xE7 => self.rst(0x20),
|
0xE7 => self.rst(0x20),
|
||||||
0xE8 => {
|
0xE8 => {
|
||||||
let arg = u8::try_from(self.load_args(1)).unwrap() as i8;
|
let arg = self.load_args(1).single_val() as i8;
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("ADD SP, {:02X}", arg);
|
println!("ADD SP, {:02X}", arg);
|
||||||
}
|
}
|
||||||
@ -1599,7 +1632,7 @@ impl CPU {
|
|||||||
4
|
4
|
||||||
}
|
}
|
||||||
0xEA => {
|
0xEA => {
|
||||||
let addr = u16::try_from(self.load_args(2)).unwrap();
|
let addr = to_u16(self.load_args(2));
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("LD ({:04X}), A", addr);
|
println!("LD ({:04X}), A", addr);
|
||||||
}
|
}
|
||||||
@ -1608,7 +1641,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
0xEB..=0xED => panic!("NON-EXISTING OPCODE"),
|
0xEB..=0xED => panic!("NON-EXISTING OPCODE"),
|
||||||
0xEE => {
|
0xEE => {
|
||||||
let arg = u8::try_from(self.load_args(1)).unwrap();
|
let arg = self.load_args(1).single_val();
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("XOR {:02X}", arg);
|
println!("XOR {:02X}", arg);
|
||||||
}
|
}
|
||||||
@ -1623,7 +1656,7 @@ impl CPU {
|
|||||||
0xEF => self.rst(0x28),
|
0xEF => self.rst(0x28),
|
||||||
|
|
||||||
0xF0 => {
|
0xF0 => {
|
||||||
let arg = u8::try_from(self.load_args(1)).unwrap();
|
let arg = self.load_args(1).single_val();
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("LDH A, {:02X}", arg);
|
println!("LDH A, {:02X}", arg);
|
||||||
}
|
}
|
||||||
@ -1649,7 +1682,7 @@ impl CPU {
|
|||||||
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 => {
|
||||||
let val = u8::try_from(self.load_args(1)).unwrap();
|
let val = self.load_args(1).single_val();
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("OR {:02X}", val);
|
println!("OR {:02X}", val);
|
||||||
}
|
}
|
||||||
@ -1663,7 +1696,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
0xF7 => self.rst(0x30),
|
0xF7 => self.rst(0x30),
|
||||||
0xF8 => {
|
0xF8 => {
|
||||||
let arg = u8::try_from(self.load_args(1)).unwrap() as i8;
|
let arg = self.load_args(1).single_val() as i8;
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("LD HL, SP+{:02X}", arg);
|
println!("LD HL, SP+{:02X}", arg);
|
||||||
}
|
}
|
||||||
@ -1693,7 +1726,7 @@ impl CPU {
|
|||||||
8
|
8
|
||||||
}
|
}
|
||||||
0xFA => {
|
0xFA => {
|
||||||
let addr = u16::try_from(self.load_args(2)).unwrap();
|
let addr = to_u16(self.load_args(2));
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("LD A, ({:04X})", addr);
|
println!("LD A, ({:04X})", addr);
|
||||||
}
|
}
|
||||||
@ -1710,7 +1743,7 @@ impl CPU {
|
|||||||
0xFC | 0xFD => panic!("NON-EXISTING OPCODE"),
|
0xFC | 0xFD => panic!("NON-EXISTING OPCODE"),
|
||||||
|
|
||||||
0xFE => {
|
0xFE => {
|
||||||
let arg = u8::try_from(self.load_args(1)).unwrap();
|
let arg = self.load_args(1).single_val();
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("CP {:02X}", arg);
|
println!("CP {:02X}", arg);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -193,7 +193,7 @@ pub struct Display {
|
|||||||
status: u8,
|
status: u8,
|
||||||
background_palette: u8,
|
background_palette: u8,
|
||||||
// Only 0 and 1 for GB
|
// Only 0 and 1 for GB
|
||||||
object_palette: [u8; 2],
|
object_palette: [u8; 256], //4 * 2 * 8],
|
||||||
scrollx: u8,
|
scrollx: u8,
|
||||||
scrolly: u8,
|
scrolly: u8,
|
||||||
windowx: u8,
|
windowx: u8,
|
||||||
@ -248,7 +248,7 @@ impl Display {
|
|||||||
control: 0,
|
control: 0,
|
||||||
status: 0,
|
status: 0,
|
||||||
background_palette: 0,
|
background_palette: 0,
|
||||||
object_palette: [0u8; 2],
|
object_palette: [0u8; 256], // 64],
|
||||||
scrollx: 0,
|
scrollx: 0,
|
||||||
scrolly: 0,
|
scrolly: 0,
|
||||||
windowx: 0,
|
windowx: 0,
|
||||||
@ -262,13 +262,13 @@ impl Display {
|
|||||||
vram1: vec![0; VRAM_SIZE].into_boxed_slice(),
|
vram1: vec![0; VRAM_SIZE].into_boxed_slice(),
|
||||||
vram_bank: 0,
|
vram_bank: 0,
|
||||||
oam: vec![0; OAM_SIZE].into_boxed_slice(),
|
oam: vec![0; OAM_SIZE].into_boxed_slice(),
|
||||||
renderer,
|
renderer: renderer,
|
||||||
|
|
||||||
event_pump,
|
event_pump: event_pump,
|
||||||
|
|
||||||
vblank_interrupt: false,
|
vblank_interrupt: false,
|
||||||
stat_interrupt: false,
|
stat_interrupt: false,
|
||||||
pixels,
|
pixels: pixels,
|
||||||
frameskip: 0,
|
frameskip: 0,
|
||||||
frame_no: 0,
|
frame_no: 0,
|
||||||
|
|
||||||
@ -281,12 +281,14 @@ impl Display {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn set_pixel(&mut self, x: u8, y: u8, color: sdl2::pixels::Color, origin: PixelOrigin) {
|
fn set_pixel(&mut self, x: u8, y: u8, color: sdl2::pixels::Color, origin: PixelOrigin) {
|
||||||
let p = &mut self.pixels[(x as usize) + (y as usize) * GB_PIXELS_X];
|
let p = &mut self.pixels[(x as usize) + (y as usize) * GB_PIXELS_X];
|
||||||
p.color = color;
|
p.color = color;
|
||||||
p.origin = origin;
|
p.origin = origin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn get_pixel_origin(&self, x: u8, y: u8) -> PixelOrigin {
|
fn get_pixel_origin(&self, x: u8, y: u8) -> PixelOrigin {
|
||||||
self.pixels[(x as usize) + (y as usize) * GB_PIXELS_X].origin
|
self.pixels[(x as usize) + (y as usize) * GB_PIXELS_X].origin
|
||||||
}
|
}
|
||||||
@ -322,6 +324,7 @@ impl Display {
|
|||||||
.expect("Rendering failed");
|
.expect("Rendering failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn vblank_interrupt(&mut self) -> bool {
|
pub fn vblank_interrupt(&mut self) -> bool {
|
||||||
// Returns whether or not a vblank interrupt should be done
|
// Returns whether or not a vblank interrupt should be done
|
||||||
// Yes, this is polling, and yes, this sucks.
|
// Yes, this is polling, and yes, this sucks.
|
||||||
@ -333,6 +336,7 @@ impl Display {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn stat_interrupt(&mut self) -> bool {
|
pub fn stat_interrupt(&mut self) -> bool {
|
||||||
if self.stat_interrupt {
|
if self.stat_interrupt {
|
||||||
self.stat_interrupt = false;
|
self.stat_interrupt = false;
|
||||||
@ -342,6 +346,7 @@ impl Display {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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 => {
|
0x8000..=0x9FFF => {
|
||||||
@ -410,6 +415,7 @@ impl Display {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn read_byte(&self, addr: u16) -> u8 {
|
pub fn read_byte(&self, addr: u16) -> u8 {
|
||||||
match addr {
|
match addr {
|
||||||
0x8000..=0x9FFF => {
|
0x8000..=0x9FFF => {
|
||||||
@ -461,6 +467,7 @@ impl Display {
|
|||||||
BgMapAttributes(self.vram1[vram_offset])
|
BgMapAttributes(self.vram1[vram_offset])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn tick(&mut self, ticks: u16) {
|
pub fn tick(&mut self, ticks: u16) {
|
||||||
self.status &= 0xFC;
|
self.status &= 0xFC;
|
||||||
if self.control & CTRL_LCD_DISPLAY_ENABLE == 0 {
|
if self.control & CTRL_LCD_DISPLAY_ENABLE == 0 {
|
||||||
@ -678,6 +685,7 @@ impl Display {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn get_bg_window_tile_addr(&self, tile_id: u8) -> usize {
|
fn get_bg_window_tile_addr(&self, tile_id: u8) -> usize {
|
||||||
if (self.control & CTRL_BG_WINDOW_TILE_DATA_SELECT) == CTRL_BG_WINDOW_TILE_DATA_SELECT {
|
if (self.control & CTRL_BG_WINDOW_TILE_DATA_SELECT) == CTRL_BG_WINDOW_TILE_DATA_SELECT {
|
||||||
let base_addr = 0x0000;
|
let base_addr = 0x0000;
|
||||||
@ -700,6 +708,7 @@ impl Display {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn renderscan(&mut self) {
|
fn renderscan(&mut self) {
|
||||||
// Points to the background map offset to use.
|
// Points to the background map offset to use.
|
||||||
let background_map: usize;
|
let background_map: usize;
|
||||||
|
|||||||
@ -165,83 +165,87 @@ impl Interconnect {
|
|||||||
|
|
||||||
// Make sure the window is responsive:
|
// Make sure the window is responsive:
|
||||||
if self.cycles > 500 {
|
if self.cycles > 500 {
|
||||||
while let Some(event) = self.display.event_pump.poll_event() {
|
loop {
|
||||||
match event {
|
if let Some(event) = self.display.event_pump.poll_event() {
|
||||||
Event::Quit { .. }
|
match event {
|
||||||
| Event::KeyDown {
|
Event::Quit { .. }
|
||||||
keycode: Some(Keycode::Escape),
|
| Event::KeyDown {
|
||||||
..
|
keycode: Some(Keycode::Escape),
|
||||||
} => {
|
..
|
||||||
self.cartridge.save();
|
} => {
|
||||||
self.display.dump_vram();
|
self.cartridge.save();
|
||||||
return TickResult::Shutdown;
|
self.display.dump_vram();
|
||||||
}
|
return TickResult::Shutdown;
|
||||||
Event::KeyDown {
|
}
|
||||||
keycode: Some(Keycode::Left),
|
Event::KeyDown {
|
||||||
..
|
keycode: Some(Keycode::Left),
|
||||||
} => self.press_key(Key::LEFT),
|
..
|
||||||
Event::KeyDown {
|
} => self.press_key(Key::LEFT),
|
||||||
keycode: Some(Keycode::Down),
|
Event::KeyDown {
|
||||||
..
|
keycode: Some(Keycode::Down),
|
||||||
} => self.press_key(Key::DOWN),
|
..
|
||||||
Event::KeyDown {
|
} => self.press_key(Key::DOWN),
|
||||||
keycode: Some(Keycode::Up),
|
Event::KeyDown {
|
||||||
..
|
keycode: Some(Keycode::Up),
|
||||||
} => self.press_key(Key::UP),
|
..
|
||||||
Event::KeyDown {
|
} => self.press_key(Key::UP),
|
||||||
keycode: Some(Keycode::Right),
|
Event::KeyDown {
|
||||||
..
|
keycode: Some(Keycode::Right),
|
||||||
} => self.press_key(Key::RIGHT),
|
..
|
||||||
Event::KeyDown {
|
} => self.press_key(Key::RIGHT),
|
||||||
keycode: Some(Keycode::A),
|
Event::KeyDown {
|
||||||
..
|
keycode: Some(Keycode::A),
|
||||||
} => self.press_key(Key::START),
|
..
|
||||||
Event::KeyDown {
|
} => self.press_key(Key::START),
|
||||||
keycode: Some(Keycode::S),
|
Event::KeyDown {
|
||||||
..
|
keycode: Some(Keycode::S),
|
||||||
} => self.press_key(Key::SELECT),
|
..
|
||||||
Event::KeyDown {
|
} => self.press_key(Key::SELECT),
|
||||||
keycode: Some(Keycode::Z),
|
Event::KeyDown {
|
||||||
..
|
keycode: Some(Keycode::Z),
|
||||||
} => self.press_key(Key::A),
|
..
|
||||||
Event::KeyDown {
|
} => self.press_key(Key::A),
|
||||||
keycode: Some(Keycode::X),
|
Event::KeyDown {
|
||||||
..
|
keycode: Some(Keycode::X),
|
||||||
} => self.press_key(Key::B),
|
..
|
||||||
|
} => self.press_key(Key::B),
|
||||||
|
|
||||||
Event::KeyUp {
|
Event::KeyUp {
|
||||||
keycode: Some(Keycode::Left),
|
keycode: Some(Keycode::Left),
|
||||||
..
|
..
|
||||||
} => self.release_key(Key::LEFT),
|
} => self.release_key(Key::LEFT),
|
||||||
Event::KeyUp {
|
Event::KeyUp {
|
||||||
keycode: Some(Keycode::Down),
|
keycode: Some(Keycode::Down),
|
||||||
..
|
..
|
||||||
} => self.release_key(Key::DOWN),
|
} => self.release_key(Key::DOWN),
|
||||||
Event::KeyUp {
|
Event::KeyUp {
|
||||||
keycode: Some(Keycode::Up),
|
keycode: Some(Keycode::Up),
|
||||||
..
|
..
|
||||||
} => self.release_key(Key::UP),
|
} => self.release_key(Key::UP),
|
||||||
Event::KeyUp {
|
Event::KeyUp {
|
||||||
keycode: Some(Keycode::Right),
|
keycode: Some(Keycode::Right),
|
||||||
..
|
..
|
||||||
} => self.release_key(Key::RIGHT),
|
} => self.release_key(Key::RIGHT),
|
||||||
Event::KeyUp {
|
Event::KeyUp {
|
||||||
keycode: Some(Keycode::A),
|
keycode: Some(Keycode::A),
|
||||||
..
|
..
|
||||||
} => self.release_key(Key::START),
|
} => self.release_key(Key::START),
|
||||||
Event::KeyUp {
|
Event::KeyUp {
|
||||||
keycode: Some(Keycode::S),
|
keycode: Some(Keycode::S),
|
||||||
..
|
..
|
||||||
} => self.release_key(Key::SELECT),
|
} => self.release_key(Key::SELECT),
|
||||||
Event::KeyUp {
|
Event::KeyUp {
|
||||||
keycode: Some(Keycode::Z),
|
keycode: Some(Keycode::Z),
|
||||||
..
|
..
|
||||||
} => self.release_key(Key::A),
|
} => self.release_key(Key::A),
|
||||||
Event::KeyUp {
|
Event::KeyUp {
|
||||||
keycode: Some(Keycode::X),
|
keycode: Some(Keycode::X),
|
||||||
..
|
..
|
||||||
} => self.release_key(Key::B),
|
} => self.release_key(Key::B),
|
||||||
_ => {}
|
_ => {}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.cycles = 0;
|
self.cycles = 0;
|
||||||
@ -251,10 +255,12 @@ impl Interconnect {
|
|||||||
TickResult::Continue
|
TickResult::Continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn is_boot_rom(&self) -> bool {
|
pub fn is_boot_rom(&self) -> bool {
|
||||||
self.disable_bootrom == 0
|
self.disable_bootrom == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn read_byte(&self, addr: u16) -> u8 {
|
pub fn read_byte(&self, addr: u16) -> u8 {
|
||||||
// TODO: Make this more beautiful.
|
// TODO: Make this more beautiful.
|
||||||
// TODO: if some flag set, use bios, otherwise only use rom
|
// TODO: if some flag set, use bios, otherwise only use rom
|
||||||
@ -313,6 +319,7 @@ impl Interconnect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn write_byte(&mut self, addr: u16, val: u8) {
|
pub fn write_byte(&mut self, addr: u16, val: u8) {
|
||||||
// TODO: Make this more beautful
|
// TODO: Make this more beautful
|
||||||
/*
|
/*
|
||||||
@ -437,11 +444,13 @@ impl Interconnect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn write_word(&mut self, addr: u16, val: u16) {
|
pub fn write_word(&mut self, addr: u16, val: u16) {
|
||||||
self.write_byte(addr, val as u8);
|
self.write_byte(addr, val as u8);
|
||||||
self.write_byte(addr + 1, (val >> 8) as u8);
|
self.write_byte(addr + 1, (val >> 8) as u8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn read_word(&self, addr: u16) -> u16 {
|
pub fn read_word(&self, addr: u16) -> u16 {
|
||||||
self.read_byte(addr) as u16 | (self.read_byte(addr + 1) as u16) << 8
|
self.read_byte(addr) as u16 | (self.read_byte(addr + 1) as u16) << 8
|
||||||
// (self.read_byte(addr) as u16) << 8 | (self.read_byte(addr + 1) as u16)
|
// (self.read_byte(addr) as u16) << 8 | (self.read_byte(addr + 1) as u16)
|
||||||
|
|||||||
@ -47,8 +47,8 @@ pub fn read_file<P: AsRef<Path>>(rom_path: P) -> Result<Box<[u8]>, io::Error> {
|
|||||||
Ok(buf.into_boxed_slice())
|
Ok(buf.into_boxed_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_file<P: AsRef<Path>>(path: P, data: &[u8]) -> Result<(), io::Error> {
|
pub fn write_file<P: AsRef<Path>>(path: P, data: &Box<[u8]>) -> Result<(), io::Error> {
|
||||||
let mut file = fs::File::create(path)?;
|
let mut file = fs::File::create(path)?;
|
||||||
file.write_all(&data)?;
|
file.write(&data)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
53
src/mbc/mbc.rs
Normal file
53
src/mbc/mbc.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
pub trait MBC {
|
||||||
|
fn read_byte(&self, addr: u16) -> u8;
|
||||||
|
fn write_byte(&mut self, addr: u16, val: u8);
|
||||||
|
|
||||||
|
fn dump_ram(&self, file: &String);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NoMBC {
|
||||||
|
rom: Box<[u8]>,
|
||||||
|
ram: Box<[u8]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NoMBC {
|
||||||
|
pub fn new(rom: Box<[u8]>, ram: Box<[u8]>) -> NoMBC {
|
||||||
|
NoMBC { rom: rom, ram: ram }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MBC for NoMBC {
|
||||||
|
fn dump_ram(&self, file: &String) {
|
||||||
|
super::super::write_file(&file, &self.ram).expect("Saving failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_byte(&mut self, addr: u16, val: u8) {
|
||||||
|
println!(
|
||||||
|
"Writing not supported for cartridges without MBC. (Tried to set {:04X} to {:02X})",
|
||||||
|
addr, val
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_byte(&self, addr: u16) -> u8 {
|
||||||
|
match addr {
|
||||||
|
0x0000..=0x7FFF => self.rom[addr as usize],
|
||||||
|
0xA000..=0xBFFF => {
|
||||||
|
// TODO: Check for ram
|
||||||
|
let addr = (addr as usize) - 0xA000;
|
||||||
|
|
||||||
|
if addr >= self.ram.len() {
|
||||||
|
println!(
|
||||||
|
"Tried to access {:04X}, however the memory is not present.",
|
||||||
|
addr + 0xA000
|
||||||
|
);
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
self.ram[addr]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
panic!("Cartride: Unable to read from {:04X}", addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
use super::MBC;
|
use super::mbc::MBC;
|
||||||
|
|
||||||
enum BankMode {
|
enum BankMode {
|
||||||
RomBankMode,
|
RomBankMode,
|
||||||
@ -17,8 +17,8 @@ pub struct MBC1 {
|
|||||||
impl MBC1 {
|
impl MBC1 {
|
||||||
pub fn new(rom: Box<[u8]>, ram: Box<[u8]>) -> MBC1 {
|
pub fn new(rom: Box<[u8]>, ram: Box<[u8]>) -> MBC1 {
|
||||||
MBC1 {
|
MBC1 {
|
||||||
rom,
|
rom: rom,
|
||||||
ram,
|
ram: ram,
|
||||||
rom_bank_no: 1,
|
rom_bank_no: 1,
|
||||||
bank_mode: BankMode::RomBankMode,
|
bank_mode: BankMode::RomBankMode,
|
||||||
bank_no_high: 0,
|
bank_no_high: 0,
|
||||||
@ -43,7 +43,7 @@ impl MBC1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MBC for MBC1 {
|
impl MBC for MBC1 {
|
||||||
fn dump_ram(&self, file: &str) {
|
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use super::MBC;
|
use super::mbc::MBC;
|
||||||
|
|
||||||
pub struct MBC2 {
|
pub struct MBC2 {
|
||||||
rom: Box<[u8]>,
|
rom: Box<[u8]>,
|
||||||
@ -10,8 +10,8 @@ pub struct MBC2 {
|
|||||||
impl MBC2 {
|
impl MBC2 {
|
||||||
pub fn new(rom: Box<[u8]>, ram: Box<[u8]>) -> MBC2 {
|
pub fn new(rom: Box<[u8]>, ram: Box<[u8]>) -> MBC2 {
|
||||||
MBC2 {
|
MBC2 {
|
||||||
rom,
|
rom: rom,
|
||||||
ram,
|
ram: ram,
|
||||||
rom_bank_no: 1,
|
rom_bank_no: 1,
|
||||||
ram_enable: false,
|
ram_enable: false,
|
||||||
}
|
}
|
||||||
@ -23,7 +23,7 @@ impl MBC2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MBC for MBC2 {
|
impl MBC for MBC2 {
|
||||||
fn dump_ram(&self, file: &str) {
|
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ impl MBC for MBC2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
0x2000..=0x3FFF => {
|
0x2000..=0x3FFF => {
|
||||||
if addr & 0x0100 == 0 {
|
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);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use super::MBC;
|
use super::mbc::MBC;
|
||||||
|
|
||||||
pub struct MBC3 {
|
pub struct MBC3 {
|
||||||
rom: Box<[u8]>,
|
rom: Box<[u8]>,
|
||||||
@ -11,8 +11,8 @@ pub struct MBC3 {
|
|||||||
impl MBC3 {
|
impl MBC3 {
|
||||||
pub fn new(rom: Box<[u8]>, ram: Box<[u8]>) -> MBC3 {
|
pub fn new(rom: Box<[u8]>, ram: Box<[u8]>) -> MBC3 {
|
||||||
MBC3 {
|
MBC3 {
|
||||||
rom,
|
rom: rom,
|
||||||
ram,
|
ram: ram,
|
||||||
rom_bank_no: 1,
|
rom_bank_no: 1,
|
||||||
ram_bank_no: 0,
|
ram_bank_no: 0,
|
||||||
ram_rtc_enabled: false,
|
ram_rtc_enabled: false,
|
||||||
@ -28,7 +28,7 @@ impl MBC3 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MBC for MBC3 {
|
impl MBC for MBC3 {
|
||||||
fn dump_ram(&self, file: &str) {
|
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use super::MBC;
|
use super::mbc::MBC;
|
||||||
|
|
||||||
pub struct MBC5 {
|
pub struct MBC5 {
|
||||||
rom: Box<[u8]>,
|
rom: Box<[u8]>,
|
||||||
@ -11,8 +11,8 @@ pub struct MBC5 {
|
|||||||
impl MBC5 {
|
impl MBC5 {
|
||||||
pub fn new(rom: Box<[u8]>, ram: Box<[u8]>) -> Self {
|
pub fn new(rom: Box<[u8]>, ram: Box<[u8]>) -> Self {
|
||||||
MBC5 {
|
MBC5 {
|
||||||
rom,
|
rom: rom,
|
||||||
ram,
|
ram: ram,
|
||||||
rom_bank_no: 1,
|
rom_bank_no: 1,
|
||||||
ram_bank_no: 0,
|
ram_bank_no: 0,
|
||||||
ram_rtc_enabled: false,
|
ram_rtc_enabled: false,
|
||||||
@ -28,7 +28,7 @@ impl MBC5 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MBC for MBC5 {
|
impl MBC for MBC5 {
|
||||||
fn dump_ram(&self, file: &str) {
|
fn dump_ram(&self, file: &String) {
|
||||||
use crate::write_file;
|
use crate::write_file;
|
||||||
write_file(&file, &self.ram).expect("Saving failed");
|
write_file(&file, &self.ram).expect("Saving failed");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,63 +1,5 @@
|
|||||||
mod mbc1;
|
pub mod mbc;
|
||||||
mod mbc2;
|
pub mod mbc1;
|
||||||
mod mbc3;
|
pub mod mbc2;
|
||||||
mod mbc5;
|
pub mod mbc3;
|
||||||
|
pub mod mbc5;
|
||||||
pub use mbc1::MBC1;
|
|
||||||
pub use mbc2::MBC2;
|
|
||||||
pub use mbc3::MBC3;
|
|
||||||
pub use mbc5::MBC5;
|
|
||||||
|
|
||||||
pub trait MBC {
|
|
||||||
fn read_byte(&self, addr: u16) -> u8;
|
|
||||||
fn write_byte(&mut self, addr: u16, val: u8);
|
|
||||||
|
|
||||||
fn dump_ram(&self, file: &str);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct NoMBC {
|
|
||||||
rom: Box<[u8]>,
|
|
||||||
ram: Box<[u8]>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NoMBC {
|
|
||||||
pub fn new(rom: Box<[u8]>, ram: Box<[u8]>) -> NoMBC {
|
|
||||||
NoMBC { rom: rom, ram: ram }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MBC for NoMBC {
|
|
||||||
fn dump_ram(&self, file: &str) {
|
|
||||||
super::write_file(&file, &self.ram).expect("Saving failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_byte(&mut self, addr: u16, val: u8) {
|
|
||||||
println!(
|
|
||||||
"Writing not supported for cartridges without MBC. (Tried to set {:04X} to {:02X})",
|
|
||||||
addr, val
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_byte(&self, addr: u16) -> u8 {
|
|
||||||
match addr {
|
|
||||||
0x0000..=0x7FFF => self.rom[addr as usize],
|
|
||||||
0xA000..=0xBFFF => {
|
|
||||||
// TODO: Check for ram
|
|
||||||
let addr = (addr as usize) - 0xA000;
|
|
||||||
|
|
||||||
if addr >= self.ram.len() {
|
|
||||||
println!(
|
|
||||||
"Tried to access {:04X}, however the memory is not present.",
|
|
||||||
addr + 0xA000
|
|
||||||
);
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
self.ram[addr]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
panic!("Cartride: Unable to read from {:04X}", addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -5,10 +5,7 @@ mod square;
|
|||||||
mod wave;
|
mod wave;
|
||||||
|
|
||||||
use self::pulse_simple::Playback;
|
use self::pulse_simple::Playback;
|
||||||
use std::sync::{
|
use std::sync::{Arc, Mutex};
|
||||||
atomic::{AtomicBool, Ordering},
|
|
||||||
Arc, Mutex,
|
|
||||||
};
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
const OUTPUT_SAMPLE_RATE: usize = 48100;
|
const OUTPUT_SAMPLE_RATE: usize = 48100;
|
||||||
@ -335,12 +332,12 @@ pub struct Sound {
|
|||||||
pub struct SoundManager {
|
pub struct SoundManager {
|
||||||
pub sound_object: Arc<Mutex<Sound>>,
|
pub sound_object: Arc<Mutex<Sound>>,
|
||||||
handle: Option<std::thread::JoinHandle<()>>,
|
handle: Option<std::thread::JoinHandle<()>>,
|
||||||
do_exit: Arc<AtomicBool>,
|
do_exit: Arc<Mutex<bool>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for SoundManager {
|
impl Drop for SoundManager {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.do_exit.store(true, Ordering::Relaxed);
|
*self.do_exit.lock().unwrap() = true;
|
||||||
self.handle.take().and_then(|h| h.join().ok());
|
self.handle.take().and_then(|h| h.join().ok());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -350,7 +347,7 @@ impl SoundManager {
|
|||||||
let mut res = SoundManager {
|
let mut res = SoundManager {
|
||||||
sound_object: Arc::new(Mutex::new(Sound::new())),
|
sound_object: Arc::new(Mutex::new(Sound::new())),
|
||||||
handle: None,
|
handle: None,
|
||||||
do_exit: Arc::new(AtomicBool::new(false)),
|
do_exit: Arc::new(Mutex::new(false)),
|
||||||
};
|
};
|
||||||
|
|
||||||
res.launch_thread();
|
res.launch_thread();
|
||||||
@ -374,7 +371,7 @@ impl SoundManager {
|
|||||||
// Counter, used for calling the 512 Hz timer
|
// Counter, used for calling the 512 Hz timer
|
||||||
let mut counter = 0;
|
let mut counter = 0;
|
||||||
|
|
||||||
while !do_exit.load(Ordering::Relaxed) {
|
while !*do_exit.lock().unwrap() {
|
||||||
for _ in 0..100 {
|
for _ in 0..100 {
|
||||||
let (s1, s2) = {
|
let (s1, s2) = {
|
||||||
let mut c_obj = obj.lock().unwrap();
|
let mut c_obj = obj.lock().unwrap();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user