FDXF3GQNDC2QFZDM55EG4HLP74ZF5T6XGCO2L7ELN2WIIZEB5PUAC
#![no_std]
mod gbcore;
pub use gbcore::*;
mod helpers;
use core::convert::TryInto;
#[derive(PartialEq, Eq, Clone, Debug)]
pub(crate) struct Splitu16(u16);
impl From<u16> for Splitu16 {
fn from(f: u16) -> Self {
let mut result = Splitu16::new();
result.set_u16(f);
result
}
}
impl Splitu16 {
pub(crate) const fn new() -> Splitu16 {
Splitu16(0)
}
pub(crate) const fn as_u16(&self) -> u16 {
self.0
}
pub(crate) fn set_u16(&mut self, f: u16) -> &mut Splitu16 {
self.0 = f;
self
}
pub(crate) fn high_u8(&self) -> u8 {
(self.0 >> 8).try_into().unwrap()
}
pub(crate) fn set_high_u8(&mut self, f: u8) {
self.0 |= u16::from(f) << 8
}
pub(crate) fn low_u8(&self) -> u8 {
(self.0 & 0x0F).try_into().unwrap()
}
pub(crate) fn set_low_u8(&mut self, f: u8) {
self.0 |= u16::from(f)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn splitu16() {
let expected = Splitu16(0b1111111100000000);
let mut real = Splitu16::new();
real.set_high_u8(0xFF);
assert_eq!(expected, real);
let expected = Splitu16(0b0000000011111111);
let mut real = Splitu16::new();
real.set_low_u8(0xFF);
assert_eq!(expected, real);
}
}
use core::panic;
use log::error;
use super::helpers::Splitu16;
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use super::*;
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct GbCore<'rom> {
cpu: Cpu,
memory: Memory<8, 8>,
screen: Screen,
rom: Option<&'rom [u8]>,
}
#[derive(PartialEq, Eq, Clone, Debug)]
struct Screen;
#[derive(PartialEq, Eq, Clone, Debug)]
struct Cpu {
registers: Registers,
}
#[derive(PartialEq, Eq, Clone, Debug)]
struct Memory<const W: usize, const V: usize> {
wram: [u8; W],
vram: [u8; V],
}
impl<const W: usize, const V: usize> Default for Memory<W, V> {
fn default() -> Self {
Memory::<W, V> {
wram: [0x0; W],
vram: [0x0; V],
}
}
}
#[allow(non_snake_case)]
#[derive(PartialEq, Eq, Clone, Debug)]
struct Registers {
AF: Splitu16,
BC: Splitu16,
DE: Splitu16,
HL: Splitu16,
SP: u16,
PC: u16,
}
impl<'rom> GbCore<'rom> {
pub fn new() -> GbCore<'rom> {
GbCore {
cpu: Cpu {
registers: Registers {
AF: Splitu16::from(0x01B0),
BC: Splitu16::from(0x0013),
DE: Splitu16::from(0x00D8),
HL: Splitu16::from(0x014D),
SP: 0xFFFE,
PC: 0x0100,
}
},
memory: Memory::<8, 8>::default(),
screen: Screen,
rom: None,
}
}
pub fn load(&mut self, rom: &'rom [u8]) -> &mut GbCore<'rom> {
self.rom = Some(rom);
self
}
pub fn fetch(&self) -> u8 {
debug_assert!(self.rom.is_some());
self.rom.unwrap()[usize::from(self.cpu.registers.PC)]
}
pub fn fetch_op(&mut self) -> TranslatedOpCode {
match self.fetch() {
0x0 => TranslatedOpCode::Noop,
0xC3 => {
let mut address = u16::from(self.next().fetch()) << 8;
address |= u16::from(self.next().fetch());
TranslatedOpCode::Jmp(address)
}
e => {
error!("Unknown Op Code {:#X}", e);
panic!();
}
}
}
pub fn next(&mut self) -> &mut GbCore<'rom> {
self.cpu.registers.PC += 1;
self
}
pub fn test(self, x: i32) {
log::error!("Oh dear, x is '{}'", x);
}
}
impl Default for GbCore<'_> {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug)]
pub enum TranslatedOpCode {
Noop,
Jmp(u16),
}
[package]
name = "gbcore"
version = "0.1.0"
authors = ["Daniel Stroh <strohdaniel624@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
log = "0.4"
use std::{fs::File, io::Read};
fn main() {
env_logger::init();
let rom: Vec<u8> = File::open("testroms/cpu_instrs.gb").expect("Cannot open file.").bytes().filter_map(|x| x.ok()).collect();
let mut x = gbcore::GbCore::new();
x.load(&rom);
for i in 1..100 {
println!("Op {}: {:#X?}", i, x.fetch_op());
x.next();
}
x.test(13);
println!("Hello, world!");
}
[package]
name = "emu"
version = "0.1.0"
authors = ["Daniel Stroh <strohdaniel624@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
gbcore = {path = "../gbcore" }
env_logger = "0.8"
[workspace]
members = [
"gbcore",
"emu",
]