Compare commits

..

1 Commits

Author SHA1 Message Date
7e5ab99329 remove Cycle struct and begin scheduler design
All checks were successful
continuous-integration/drone/push Build is passing
2021-09-12 04:55:14 -03:00
7 changed files with 211 additions and 249 deletions

View File

@@ -7,13 +7,14 @@ use crate::ppu::{Ppu, PpuMode};
use crate::serial::Serial; use crate::serial::Serial;
use crate::timer::Timer; use crate::timer::Timer;
use crate::work_ram::{VariableWorkRam, WorkRam}; use crate::work_ram::{VariableWorkRam, WorkRam};
use std::{fs::File, io::Read};
pub(crate) const BOOT_SIZE: usize = 0x100; const BOOT_ROM_SIZE: usize = 0x100;
#[derive(Debug)] #[derive(Debug)]
pub struct Bus { pub struct Bus {
boot: Option<[u8; BOOT_SIZE]>, // Boot ROM is 256b long boot: Option<[u8; BOOT_ROM_SIZE]>, // Boot ROM is 256b long
cart: Option<Cartridge>, cartridge: Option<Cartridge>,
pub(crate) ppu: Ppu, pub(crate) ppu: Ppu,
work_ram: WorkRam, work_ram: WorkRam,
var_ram: VariableWorkRam, var_ram: VariableWorkRam,
@@ -29,7 +30,7 @@ impl Default for Bus {
fn default() -> Self { fn default() -> Self {
Self { Self {
boot: None, boot: None,
cart: None, cartridge: None,
ppu: Default::default(), ppu: Default::default(),
work_ram: Default::default(), work_ram: Default::default(),
var_ram: Default::default(), var_ram: Default::default(),
@@ -44,19 +45,25 @@ impl Default for Bus {
} }
impl Bus { impl Bus {
pub(crate) fn with_boot(rom: [u8; 256]) -> Self { pub(crate) fn with_boot(path: &str) -> anyhow::Result<Self> {
Self { let mut file = File::open(path)?;
boot: Some(rom), let mut boot_rom = [0u8; 256];
file.read_exact(&mut boot_rom)?;
Ok(Self {
boot: Some(boot_rom),
..Default::default() ..Default::default()
} })
} }
pub(crate) fn load_cart(&mut self, rom: Vec<u8>) { pub(crate) fn load_cartridge(&mut self, path: &str) -> std::io::Result<()> {
self.cart = Some(Cartridge::new(rom)); self.cartridge = Some(Cartridge::new(path)?);
Ok(())
} }
pub(crate) fn cart_title(&self) -> Option<&str> { pub(crate) fn rom_title(&self) -> Option<&str> {
self.cart.as_ref()?.title() self.cartridge.as_ref()?.title()
} }
#[allow(dead_code)] #[allow(dead_code)]
@@ -97,13 +104,13 @@ impl Bus {
} }
} }
match self.cart.as_ref() { match self.cartridge.as_ref() {
Some(cart) => cart.read_byte(addr), Some(cart) => cart.read_byte(addr),
None => panic!("Tried to read from a non-existent cartridge"), None => panic!("Tried to read from a non-existent cartridge"),
} }
} }
0x8000..=0x9FFF => self.ppu.read_byte(addr), // 8KB Video RAM 0x8000..=0x9FFF => self.ppu.read_byte(addr), // 8KB Video RAM
0xA000..=0xBFFF => match self.cart.as_ref() { 0xA000..=0xBFFF => match self.cartridge.as_ref() {
// 8KB External RAM // 8KB External RAM
Some(cart) => cart.read_byte(addr), Some(cart) => cart.read_byte(addr),
None => panic!("Tried to read from a non-existent cartridge"), None => panic!("Tried to read from a non-existent cartridge"),
@@ -150,7 +157,7 @@ impl BusIo for Bus {
} }
} }
match self.cart.as_ref() { match self.cartridge.as_ref() {
Some(cart) => cart.read_byte(addr), Some(cart) => cart.read_byte(addr),
None => panic!("Tried to read from a non-existent cartridge"), None => panic!("Tried to read from a non-existent cartridge"),
} }
@@ -162,7 +169,7 @@ impl BusIo for Bus {
_ => self.ppu.read_byte(addr), _ => self.ppu.read_byte(addr),
} }
} }
0xA000..=0xBFFF => match self.cart.as_ref() { 0xA000..=0xBFFF => match self.cartridge.as_ref() {
// 8KB External RAM // 8KB External RAM
Some(cart) => cart.read_byte(addr), Some(cart) => cart.read_byte(addr),
None => panic!("Tried to read from a non-existent cartridge"), None => panic!("Tried to read from a non-existent cartridge"),
@@ -255,7 +262,7 @@ impl BusIo for Bus {
0x0000..=0x7FFF => { 0x0000..=0x7FFF => {
// 16KB ROM bank 00 (ends at 0x3FFF) // 16KB ROM bank 00 (ends at 0x3FFF)
// and 16KB ROM Bank 01 -> NN (switchable via MB) // and 16KB ROM Bank 01 -> NN (switchable via MB)
match self.cart.as_mut() { match self.cartridge.as_mut() {
Some(cart) => cart.write_byte(addr, byte), Some(cart) => cart.write_byte(addr, byte),
None => panic!("Tried to write into non-existent cartridge"), None => panic!("Tried to write into non-existent cartridge"),
} }
@@ -269,7 +276,7 @@ impl BusIo for Bus {
} }
0xA000..=0xBFFF => { 0xA000..=0xBFFF => {
// 8KB External RAM // 8KB External RAM
match self.cart.as_mut() { match self.cartridge.as_mut() {
Some(cart) => cart.write_byte(addr, byte), Some(cart) => cart.write_byte(addr, byte),
None => panic!("Tried to write into non-existent cartridge"), None => panic!("Tried to write into non-existent cartridge"),
} }

View File

@@ -1,3 +1,7 @@
use std::fs::File;
use std::io::{self, Read};
use std::path::Path;
use crate::bus::BusIo; use crate::bus::BusIo;
const RAM_SIZE_ADDRESS: usize = 0x0149; const RAM_SIZE_ADDRESS: usize = 0x0149;
@@ -13,15 +17,19 @@ pub(crate) struct Cartridge {
} }
impl Cartridge { impl Cartridge {
pub(crate) fn new(memory: Vec<u8>) -> Self { pub(crate) fn new<P: AsRef<Path> + ?Sized>(path: &P) -> io::Result<Self> {
let mut memory = vec![];
let mut rom = File::open(path)?;
rom.read_to_end(&mut memory)?;
let title = Self::find_title(&memory); let title = Self::find_title(&memory);
eprintln!("Cartridge Title: {:?}", title); eprintln!("Cartridge Title: {:?}", title);
Self { Ok(Self {
mbc: Self::detect_mbc(&memory), mbc: Self::detect_mbc(&memory),
title, title,
memory, memory,
} })
} }
fn detect_mbc(memory: &[u8]) -> Box<dyn MBCIo> { fn detect_mbc(memory: &[u8]) -> Box<dyn MBCIo> {

View File

@@ -1,6 +1,9 @@
use crate::bus::{Bus, BusIo, BOOT_SIZE}; use crate::apu::Apu;
use crate::bus::{Bus, BusIo};
use crate::instruction::Instruction; use crate::instruction::Instruction;
use crate::interrupt::{InterruptEnable, InterruptFlag}; use crate::interrupt::{InterruptEnable, InterruptFlag};
use crate::joypad::Joypad;
use crate::ppu::Ppu;
use crate::Cycle; use crate::Cycle;
use bitfield::bitfield; use bitfield::bitfield;
use std::fmt::{Display, Formatter, Result as FmtResult}; use std::fmt::{Display, Formatter, Result as FmtResult};
@@ -15,7 +18,7 @@ pub struct Cpu {
} }
impl Cpu { impl Cpu {
pub(crate) fn without_boot() -> Self { pub fn new() -> Self {
Self { Self {
reg: Registers { reg: Registers {
a: 0x01, a: 0x01,
@@ -33,11 +36,11 @@ impl Cpu {
} }
} }
pub(crate) fn with_boot(rom: [u8; BOOT_SIZE]) -> Self { pub fn boot_new(path: &str) -> anyhow::Result<Self> {
Self { Ok(Self {
bus: Bus::with_boot(rom), bus: Bus::with_boot(path)?,
..Default::default() ..Default::default()
} })
} }
pub(crate) fn ime(&self) -> ImeState { pub(crate) fn ime(&self) -> ImeState {
@@ -69,6 +72,14 @@ impl Cpu {
_ => None, _ => None,
} }
} }
pub fn load_cartridge(&mut self, path: &str) -> std::io::Result<()> {
self.bus.load_cartridge(path)
}
pub fn rom_title(&self) -> Option<&str> {
self.bus.rom_title()
}
} }
impl Cpu { impl Cpu {
@@ -156,12 +167,16 @@ impl BusIo for Cpu {
} }
impl Cpu { impl Cpu {
pub(crate) fn bus(&self) -> &Bus { pub fn ppu(&mut self) -> &Ppu {
&self.bus &self.bus.ppu
} }
pub(crate) fn bus_mut(&mut self) -> &mut Bus { pub fn apu_mut(&mut self) -> &mut Apu {
&mut self.bus &mut self.bus.apu
}
pub(crate) fn joypad_mut(&mut self) -> &mut Joypad {
&mut self.bus.joypad
} }
fn handle_ei(&mut self) { fn handle_ei(&mut self) {

View File

@@ -1,7 +1,8 @@
use crate::apu::gen::SampleProducer; use crate::cpu::Cpu as SM83;
use crate::cpu::Cpu; use crate::joypad;
use crate::joypad::{self, Joypad}; use crate::ppu::Ppu;
use crate::{Cycle, GB_HEIGHT, GB_WIDTH}; use crate::Cycle;
use anyhow::Result;
use gilrs::Gilrs; use gilrs::Gilrs;
use std::time::Duration; use std::time::Duration;
use winit_input_helper::WinitInputHelper; use winit_input_helper::WinitInputHelper;
@@ -11,111 +12,56 @@ pub const CYCLES_IN_FRAME: Cycle = 456 * 154; // 456 Cycles times 154 scanlines
pub(crate) const SM83_CLOCK_SPEED: u64 = 0x40_0000; // Hz which is 4.194304Mhz pub(crate) const SM83_CLOCK_SPEED: u64 = 0x40_0000; // Hz which is 4.194304Mhz
const DEFAULT_TITLE: &str = "DMG-01 Emulator"; const DEFAULT_TITLE: &str = "DMG-01 Emulator";
pub fn run_frame(emu: &mut Emulator, gamepad: &mut Gilrs, key: &WinitInputHelper) -> Cycle { pub fn init(boot_path: Option<&str>, rom_path: &str) -> Result<SM83> {
let mut cpu = match boot_path {
Some(path) => SM83::boot_new(path)?,
None => SM83::new(),
};
eprintln!("Initialized GB Emulator");
cpu.load_cartridge(rom_path)?;
Ok(cpu)
}
pub fn rom_title(game_boy: &SM83) -> &str {
game_boy.rom_title().unwrap_or(DEFAULT_TITLE)
}
pub fn run(
game_boy: &mut SM83,
gamepad: &mut Gilrs,
input: &WinitInputHelper,
target: Cycle,
) -> Cycle {
let mut elapsed = 0; let mut elapsed = 0;
if let Some(event) = gamepad.next_event() { if let Some(event) = gamepad.next_event() {
joypad::handle_gamepad_input(emu.joyp_mut(), event); joypad::handle_gamepad_input(game_boy.joypad_mut(), event);
} }
joypad::handle_keyboard_input(game_boy.joypad_mut(), input);
joypad::handle_keyboard_input(emu.joyp_mut(), key); while elapsed < target {
while elapsed < CYCLES_IN_FRAME { elapsed += game_boy.step();
elapsed += emu.step();
} }
elapsed elapsed
} }
pub fn draw_frame(emu: &Emulator, buf: &mut [u8; GB_HEIGHT * GB_WIDTH * 4]) { pub fn run_frame(game_boy: &mut SM83, gamepad: &mut Gilrs, input: &WinitInputHelper) -> Cycle {
buf.copy_from_slice(emu.cpu.bus().ppu.frame_buf()); let mut elapsed = 0;
if let Some(event) = gamepad.next_event() {
joypad::handle_gamepad_input(game_boy.joypad_mut(), event);
}
joypad::handle_keyboard_input(game_boy.joypad_mut(), input);
while elapsed < CYCLES_IN_FRAME {
elapsed += game_boy.step();
}
elapsed
} }
pub struct Emulator { pub fn draw(ppu: &Ppu, frame: &mut [u8]) {
cpu: Cpu, ppu.copy_to_gui(frame);
timestamp: Cycle,
}
impl Emulator {
fn new(cpu: Cpu) -> Self {
Self {
cpu,
timestamp: Default::default(),
}
}
fn step(&mut self) -> Cycle {
self.cpu.step()
}
fn load_cart(&mut self, rom: Vec<u8>) {
self.cpu.bus_mut().load_cart(rom)
}
fn joyp_mut(&mut self) -> &mut Joypad {
&mut self.cpu.bus_mut().joypad
}
pub fn set_prod(&mut self, prod: SampleProducer<f32>) {
self.cpu.bus_mut().apu.attach_producer(prod)
}
pub fn title(&self) -> &str {
self.cpu.bus().cart_title().unwrap_or(DEFAULT_TITLE)
}
}
pub mod build {
use std::fs::File;
use std::io::{Read, Result};
use std::path::Path;
use crate::bus::BOOT_SIZE;
use crate::cpu::Cpu;
use super::Emulator;
#[derive(Debug, Default)]
pub struct EmulatorBuilder {
boot: Option<[u8; BOOT_SIZE]>,
cart: Option<Vec<u8>>,
}
impl EmulatorBuilder {
pub fn new() -> Self {
Default::default()
}
pub fn with_boot<P: AsRef<Path>>(mut self, path: P) -> Result<Self> {
let mut file = File::open(path.as_ref())?;
let mut buf = [0x00; BOOT_SIZE];
file.read_exact(&mut buf)?;
self.boot = Some(buf);
Ok(self)
}
pub fn with_cart<P: AsRef<Path>>(mut self, path: P) -> Result<Self> {
let mut file = File::open(path.as_ref())?;
let mut buf = Vec::new();
file.read_to_end(&mut buf)?;
self.cart = Some(buf);
Ok(self)
}
pub fn finish(mut self) -> Emulator {
let mut emu = Emulator::new(match self.boot {
Some(rom) => Cpu::with_boot(rom),
None => Cpu::without_boot(),
});
if let Some(rom) = self.cart.take() {
emu.load_cart(rom)
}
emu
}
}
} }

View File

@@ -123,7 +123,7 @@ impl Instruction {
let addr = Self::imm_word(cpu); let addr = Self::imm_word(cpu);
let sp = cpu.register_pair(RegisterPair::SP); let sp = cpu.register_pair(RegisterPair::SP);
Self::write_word(&mut cpu.bus, addr, sp); Self::write_word(&mut cpu.bus, addr, sp);
20 (20)
} }
(LDTarget::Group1(pair), LDSource::ImmediateWord) => { (LDTarget::Group1(pair), LDSource::ImmediateWord) => {
// LD r16, u16 | Store u16 in 16-bit register // LD r16, u16 | Store u16 in 16-bit register
@@ -133,7 +133,7 @@ impl Instruction {
match pair { match pair {
BC | DE | HL | SP => cpu.set_register_pair(pair.as_register_pair(), word), BC | DE | HL | SP => cpu.set_register_pair(pair.as_register_pair(), word),
} }
12 (12)
} }
(LDTarget::IndirectGroup2(pair), LDSource::A) => { (LDTarget::IndirectGroup2(pair), LDSource::A) => {
// LD (r16), A | Store accumulator in byte at 16-bit register // LD (r16), A | Store accumulator in byte at 16-bit register
@@ -155,7 +155,7 @@ impl Instruction {
cpu.set_register_pair(RegisterPair::HL, addr - 1); cpu.set_register_pair(RegisterPair::HL, addr - 1);
} }
} }
8 (8)
} }
(LDTarget::A, LDSource::IndirectGroup2(pair)) => { (LDTarget::A, LDSource::IndirectGroup2(pair)) => {
// LD A, (r16) | Store byte at 16-bit register in accumulator // LD A, (r16) | Store byte at 16-bit register in accumulator
@@ -178,7 +178,7 @@ impl Instruction {
cpu.set_register_pair(RegisterPair::HL, addr - 1); cpu.set_register_pair(RegisterPair::HL, addr - 1);
} }
} }
8 (8)
} }
(LDTarget::Register(reg), LDSource::ImmediateByte) => { (LDTarget::Register(reg), LDSource::ImmediateByte) => {
// LD r8, u8 | Store u8 in 8-bit register // LD r8, u8 | Store u8 in 8-bit register
@@ -188,12 +188,12 @@ impl Instruction {
match reg { match reg {
A | B | C | D | E | H | L => { A | B | C | D | E | H | L => {
cpu.set_register(reg.cpu_register(), right); cpu.set_register(reg.cpu_register(), right);
8 (8)
} }
IndirectHL => { IndirectHL => {
let addr = cpu.register_pair(RegisterPair::HL); let addr = cpu.register_pair(RegisterPair::HL);
Self::write_byte(&mut cpu.bus, addr, right); Self::write_byte(&mut cpu.bus, addr, right);
12 (12)
} }
} }
} }
@@ -202,14 +202,14 @@ impl Instruction {
let addr = 0xFF00 + cpu.register(CpuRegister::C) as u16; let addr = 0xFF00 + cpu.register(CpuRegister::C) as u16;
let acc = cpu.register(CpuRegister::A); let acc = cpu.register(CpuRegister::A);
Self::write_byte(&mut cpu.bus, addr, acc); Self::write_byte(&mut cpu.bus, addr, acc);
8 (8)
} }
(LDTarget::A, LDSource::IoWithC) => { (LDTarget::A, LDSource::IoWithC) => {
// LD A, (0xFF00 + C) | Store byte at 0xFF00 + C in register A // LD A, (0xFF00 + C) | Store byte at 0xFF00 + C in register A
let addr = 0xFF00 + cpu.register(CpuRegister::C) as u16; let addr = 0xFF00 + cpu.register(CpuRegister::C) as u16;
let byte = Self::read_byte(&mut cpu.bus, addr); let byte = Self::read_byte(&mut cpu.bus, addr);
cpu.set_register(CpuRegister::A, byte); cpu.set_register(CpuRegister::A, byte);
8 (8)
} }
(LDTarget::Register(target), LDSource::Register(source)) => { (LDTarget::Register(target), LDSource::Register(source)) => {
// LD r8, r8 | Store 8-bit register in 8-bit register // LD r8, r8 | Store 8-bit register in 8-bit register
@@ -222,12 +222,12 @@ impl Instruction {
match target { match target {
B | C | D | E | H | L | A => { B | C | D | E | H | L | A => {
cpu.set_register(target.cpu_register(), right); cpu.set_register(target.cpu_register(), right);
4 (4)
} }
IndirectHL => { IndirectHL => {
let addr = cpu.register_pair(RegisterPair::HL); let addr = cpu.register_pair(RegisterPair::HL);
Self::write_byte(&mut cpu.bus, addr, right); Self::write_byte(&mut cpu.bus, addr, right);
8 (8)
} }
} }
} }
@@ -238,7 +238,7 @@ impl Instruction {
match target { match target {
B | C | D | E | H | L | A => { B | C | D | E | H | L | A => {
cpu.set_register(target.cpu_register(), right); cpu.set_register(target.cpu_register(), right);
8 (8)
} }
IndirectHL => { IndirectHL => {
unreachable!("LD (HL), (HL) is an illegal instruction") unreachable!("LD (HL), (HL) is an illegal instruction")
@@ -252,33 +252,33 @@ impl Instruction {
let addr = 0xFF00 + Self::imm_byte(cpu) as u16; let addr = 0xFF00 + Self::imm_byte(cpu) as u16;
let acc = cpu.register(CpuRegister::A); let acc = cpu.register(CpuRegister::A);
Self::write_byte(&mut cpu.bus, addr, acc); Self::write_byte(&mut cpu.bus, addr, acc);
12 (12)
} }
(LDTarget::A, LDSource::IoWithImmediateOffset) => { (LDTarget::A, LDSource::IoWithImmediateOffset) => {
// LD A, (0xFF00 + u8) | Store byte at address 0xFF00 + u8 in accumulator // LD A, (0xFF00 + u8) | Store byte at address 0xFF00 + u8 in accumulator
let addr = 0xFF00 + Self::imm_byte(cpu) as u16; let addr = 0xFF00 + Self::imm_byte(cpu) as u16;
let byte = Self::read_byte(&mut cpu.bus, addr); let byte = Self::read_byte(&mut cpu.bus, addr);
cpu.set_register(CpuRegister::A, byte); cpu.set_register(CpuRegister::A, byte);
12 (12)
} }
(LDTarget::SP, LDSource::HL) => { (LDTarget::SP, LDSource::HL) => {
// LD SP, HL | Store HL in stack pointer // LD SP, HL | Store HL in stack pointer
cpu.set_register_pair(RegisterPair::SP, cpu.register_pair(RegisterPair::HL)); cpu.set_register_pair(RegisterPair::SP, cpu.register_pair(RegisterPair::HL));
8 // performs an internal operation that takes 4 cycles (8) // performs an internal operation that takes 4 cycles
} }
(LDTarget::IndirectImmediateWord, LDSource::A) => { (LDTarget::IndirectImmediateWord, LDSource::A) => {
// LD (u16), A | Store accumulator in byte at 16-bit register // LD (u16), A | Store accumulator in byte at 16-bit register
let addr = Self::imm_word(cpu); let addr = Self::imm_word(cpu);
let acc = cpu.register(CpuRegister::A); let acc = cpu.register(CpuRegister::A);
Self::write_byte(&mut cpu.bus, addr, acc); Self::write_byte(&mut cpu.bus, addr, acc);
16 (16)
} }
(LDTarget::A, LDSource::IndirectImmediateWord) => { (LDTarget::A, LDSource::IndirectImmediateWord) => {
// LD A, (u16) | Store byte at 16-bit register in accumulator // LD A, (u16) | Store byte at 16-bit register in accumulator
let addr = Self::imm_word(cpu); let addr = Self::imm_word(cpu);
let byte = Self::read_byte(&mut cpu.bus, addr); let byte = Self::read_byte(&mut cpu.bus, addr);
cpu.set_register(CpuRegister::A, byte); cpu.set_register(CpuRegister::A, byte);
16 (16)
} }
_ => unreachable!("LD {:?}, {:?} is an illegal instruction", target, src), _ => unreachable!("LD {:?}, {:?} is an illegal instruction", target, src),
}, },
@@ -296,38 +296,38 @@ impl Instruction {
JumpCondition::NotZero => { JumpCondition::NotZero => {
if !flags.z() { if !flags.z() {
Self::jump(cpu, addr); Self::jump(cpu, addr);
12 (12)
} else { } else {
8 (8)
} }
} }
JumpCondition::Zero => { JumpCondition::Zero => {
if flags.z() { if flags.z() {
Self::jump(cpu, addr); Self::jump(cpu, addr);
12 (12)
} else { } else {
8 (8)
} }
} }
JumpCondition::NotCarry => { JumpCondition::NotCarry => {
if !flags.c() { if !flags.c() {
Self::jump(cpu, addr); Self::jump(cpu, addr);
12 (12)
} else { } else {
8 (8)
} }
} }
JumpCondition::Carry => { JumpCondition::Carry => {
if flags.c() { if flags.c() {
Self::jump(cpu, addr); Self::jump(cpu, addr);
12 (12)
} else { } else {
8 (8)
} }
} }
JumpCondition::Always => { JumpCondition::Always => {
Self::jump(cpu, addr); Self::jump(cpu, addr);
12 (12)
} }
} }
} }
@@ -349,7 +349,7 @@ impl Instruction {
} }
} }
cpu.set_flags(flags); cpu.set_flags(flags);
8 (8)
} }
(AddTarget::A, AddSource::Register(reg)) => { (AddTarget::A, AddSource::Register(reg)) => {
// ADD A, r8 | Add 8-bit register to accumulator // ADD A, r8 | Add 8-bit register to accumulator
@@ -383,7 +383,7 @@ impl Instruction {
let sum = Self::add_u16_i8(left, Self::imm_byte(cpu) as i8, &mut flags); let sum = Self::add_u16_i8(left, Self::imm_byte(cpu) as i8, &mut flags);
cpu.set_register_pair(RegisterPair::SP, sum); cpu.set_register_pair(RegisterPair::SP, sum);
cpu.set_flags(flags); cpu.set_flags(flags);
16 (16)
} }
(AddTarget::A, AddSource::ImmediateByte) => { (AddTarget::A, AddSource::ImmediateByte) => {
// ADD A, u8 | Add u8 to accumulator // ADD A, u8 | Add u8 to accumulator
@@ -393,7 +393,7 @@ impl Instruction {
let sum = Self::add(left, Self::imm_byte(cpu), &mut flags); let sum = Self::add(left, Self::imm_byte(cpu), &mut flags);
cpu.set_register(CpuRegister::A, sum); cpu.set_register(CpuRegister::A, sum);
cpu.set_flags(flags); cpu.set_flags(flags);
8 (8)
} }
_ => unreachable!("ADD {:?}, {:?} is an illegal instruction", target, src), _ => unreachable!("ADD {:?}, {:?} is an illegal instruction", target, src),
}, },
@@ -408,13 +408,13 @@ impl Instruction {
B | C | D | E | H | L | A => { B | C | D | E | H | L | A => {
let reg = reg.cpu_register(); let reg = reg.cpu_register();
cpu.set_register(reg, Self::inc(cpu.register(reg), &mut flags)); cpu.set_register(reg, Self::inc(cpu.register(reg), &mut flags));
4 (4)
} }
IndirectHL => { IndirectHL => {
let addr = cpu.register_pair(RegisterPair::HL); let addr = cpu.register_pair(RegisterPair::HL);
let left = Self::read_byte(&mut cpu.bus, addr); let left = Self::read_byte(&mut cpu.bus, addr);
Self::write_byte(&mut cpu.bus, addr, Self::inc(left, &mut flags)); Self::write_byte(&mut cpu.bus, addr, Self::inc(left, &mut flags));
12 (12)
} }
}; };
cpu.set_flags(flags); cpu.set_flags(flags);
@@ -433,7 +433,7 @@ impl Instruction {
cpu.set_register_pair(pair, left.wrapping_add(1)); cpu.set_register_pair(pair, left.wrapping_add(1));
} }
} }
8 (8)
} }
} }
} }
@@ -448,13 +448,13 @@ impl Instruction {
B | C | D | E | H | L | A => { B | C | D | E | H | L | A => {
let reg = reg.cpu_register(); let reg = reg.cpu_register();
cpu.set_register(reg, Self::dec(cpu.register(reg), &mut flags)); cpu.set_register(reg, Self::dec(cpu.register(reg), &mut flags));
4 (4)
} }
IndirectHL => { IndirectHL => {
let addr = cpu.register_pair(RegisterPair::HL); let addr = cpu.register_pair(RegisterPair::HL);
let left = Self::read_byte(&mut cpu.bus, addr); let left = Self::read_byte(&mut cpu.bus, addr);
Self::write_byte(&mut cpu.bus, addr, Self::dec(left, &mut flags)); Self::write_byte(&mut cpu.bus, addr, Self::dec(left, &mut flags));
12 (12)
} }
}; };
cpu.set_flags(flags); cpu.set_flags(flags);
@@ -472,7 +472,7 @@ impl Instruction {
cpu.set_register_pair(pair, left.wrapping_sub(1)); cpu.set_register_pair(pair, left.wrapping_sub(1));
} }
}; };
8 (8)
} }
} }
} }
@@ -483,7 +483,7 @@ impl Instruction {
let acc_rotated = acc.rotate_left(1); let acc_rotated = acc.rotate_left(1);
cpu.set_register(CpuRegister::A, acc_rotated); cpu.set_register(CpuRegister::A, acc_rotated);
cpu.update_flags(false, false, false, most_sgfnt == 0x01); cpu.update_flags(false, false, false, most_sgfnt == 0x01);
4 (4)
} }
Instruction::RRCA => { Instruction::RRCA => {
// RRCA | Rotate accumulator right // RRCA | Rotate accumulator right
@@ -492,7 +492,7 @@ impl Instruction {
let acc_rotated = acc.rotate_right(1); let acc_rotated = acc.rotate_right(1);
cpu.set_register(CpuRegister::A, acc_rotated); cpu.set_register(CpuRegister::A, acc_rotated);
cpu.update_flags(false, false, false, least_sgfnt == 0x01); cpu.update_flags(false, false, false, least_sgfnt == 0x01);
4 (4)
} }
Instruction::RLA => { Instruction::RLA => {
// RLA | Rotate accumulator left through carry // RLA | Rotate accumulator left through carry
@@ -502,7 +502,7 @@ impl Instruction {
let (acc_rotated, carry) = Self::rl_thru_carry(acc, flags.c()); let (acc_rotated, carry) = Self::rl_thru_carry(acc, flags.c());
cpu.set_register(CpuRegister::A, acc_rotated); cpu.set_register(CpuRegister::A, acc_rotated);
cpu.update_flags(false, false, false, carry); cpu.update_flags(false, false, false, carry);
4 (4)
} }
Instruction::RRA => { Instruction::RRA => {
// RRA | Rotate accumulator right through carry // RRA | Rotate accumulator right through carry
@@ -512,7 +512,7 @@ impl Instruction {
let (acc_rotated, carry) = Self::rr_thru_carry(acc, flags.c()); let (acc_rotated, carry) = Self::rr_thru_carry(acc, flags.c());
cpu.set_register(CpuRegister::A, acc_rotated); cpu.set_register(CpuRegister::A, acc_rotated);
cpu.update_flags(false, false, false, carry); cpu.update_flags(false, false, false, carry);
4 (4)
} }
Instruction::DAA => { Instruction::DAA => {
// DAA | Change accumulator into its BCD representation // DAA | Change accumulator into its BCD representation
@@ -553,7 +553,7 @@ impl Instruction {
flags.set_z(tmp as u8 == 0); flags.set_z(tmp as u8 == 0);
flags.set_h(false); flags.set_h(false);
cpu.set_flags(flags); cpu.set_flags(flags);
4 (4)
} }
Instruction::CPL => { Instruction::CPL => {
// CPL | Compliment accumulator // CPL | Compliment accumulator
@@ -564,7 +564,7 @@ impl Instruction {
flags.set_n(true); flags.set_n(true);
flags.set_h(true); flags.set_h(true);
cpu.set_flags(flags); cpu.set_flags(flags);
4 (4)
} }
Instruction::SCF => { Instruction::SCF => {
// SCF | Set Carry Flag // SCF | Set Carry Flag
@@ -574,7 +574,7 @@ impl Instruction {
flags.set_h(false); flags.set_h(false);
flags.set_c(true); flags.set_c(true);
cpu.set_flags(flags); cpu.set_flags(flags);
4 (4)
} }
Instruction::CCF => { Instruction::CCF => {
// CCF | Compliment Carry Flag // CCF | Compliment Carry Flag
@@ -584,7 +584,7 @@ impl Instruction {
flags.set_h(false); flags.set_h(false);
flags.set_c(!flags.c()); flags.set_c(!flags.c());
cpu.set_flags(flags); cpu.set_flags(flags);
4 (4)
} }
Instruction::HALT => { Instruction::HALT => {
// HALT | Enter CPU low power consumption mode until interrupt occurs // HALT | Enter CPU low power consumption mode until interrupt occurs
@@ -596,7 +596,7 @@ impl Instruction {
_ => NonePending, _ => NonePending,
}; };
cpu.halt_cpu(kind); cpu.halt_cpu(kind);
4 (4)
} }
Instruction::ADC(source) => match source { Instruction::ADC(source) => match source {
AluSource::Register(reg) => { AluSource::Register(reg) => {
@@ -632,7 +632,7 @@ impl Instruction {
let sum = Self::add_with_carry_bit(left, right, flags.c(), &mut flags); let sum = Self::add_with_carry_bit(left, right, flags.c(), &mut flags);
cpu.set_register(CpuRegister::A, sum); cpu.set_register(CpuRegister::A, sum);
cpu.set_flags(flags); cpu.set_flags(flags);
8 (8)
} }
}, },
Instruction::SUB(source) => match source { Instruction::SUB(source) => match source {
@@ -666,7 +666,7 @@ impl Instruction {
let right = Self::imm_byte(cpu); let right = Self::imm_byte(cpu);
cpu.set_register(CpuRegister::A, Self::sub(left, right, &mut flags)); cpu.set_register(CpuRegister::A, Self::sub(left, right, &mut flags));
cpu.set_flags(flags); cpu.set_flags(flags);
8 (8)
} }
}, },
Instruction::SBC(target) => match target { Instruction::SBC(target) => match target {
@@ -703,7 +703,7 @@ impl Instruction {
let diff = Self::sub_with_carry(left, right, flags.c(), &mut flags); let diff = Self::sub_with_carry(left, right, flags.c(), &mut flags);
cpu.set_register(CpuRegister::A, diff); cpu.set_register(CpuRegister::A, diff);
cpu.set_flags(flags); cpu.set_flags(flags);
8 (8)
} }
}, },
Instruction::AND(target) => match target { Instruction::AND(target) => match target {
@@ -729,7 +729,7 @@ impl Instruction {
let acc = cpu.register(CpuRegister::A) & Self::imm_byte(cpu); let acc = cpu.register(CpuRegister::A) & Self::imm_byte(cpu);
cpu.set_register(CpuRegister::A, acc); cpu.set_register(CpuRegister::A, acc);
cpu.update_flags(acc == 0, false, true, false); cpu.update_flags(acc == 0, false, true, false);
8 (8)
} }
}, },
Instruction::XOR(source) => match source { Instruction::XOR(source) => match source {
@@ -755,7 +755,7 @@ impl Instruction {
let acc = cpu.register(CpuRegister::A) ^ Self::imm_byte(cpu); let acc = cpu.register(CpuRegister::A) ^ Self::imm_byte(cpu);
cpu.set_register(CpuRegister::A, acc); cpu.set_register(CpuRegister::A, acc);
cpu.update_flags(acc == 0, false, false, false); cpu.update_flags(acc == 0, false, false, false);
8 (8)
} }
}, },
Instruction::OR(target) => match target { Instruction::OR(target) => match target {
@@ -781,7 +781,7 @@ impl Instruction {
let acc = cpu.register(CpuRegister::A) | Self::imm_byte(cpu); let acc = cpu.register(CpuRegister::A) | Self::imm_byte(cpu);
cpu.set_register(CpuRegister::A, acc); cpu.set_register(CpuRegister::A, acc);
cpu.update_flags(acc == 0, false, false, false); cpu.update_flags(acc == 0, false, false, false);
8 (8)
} }
}, },
Instruction::CP(target) => match target { Instruction::CP(target) => match target {
@@ -795,13 +795,13 @@ impl Instruction {
let cycles = match reg { let cycles = match reg {
B | C | D | E | H | L | A => { B | C | D | E | H | L | A => {
let _ = Self::sub(left, cpu.register(reg.cpu_register()), &mut flags); let _ = Self::sub(left, cpu.register(reg.cpu_register()), &mut flags);
4 (4)
} }
IndirectHL => { IndirectHL => {
let addr = cpu.register_pair(RegisterPair::HL); let addr = cpu.register_pair(RegisterPair::HL);
let right = Self::read_byte(&mut cpu.bus, addr); let right = Self::read_byte(&mut cpu.bus, addr);
let _ = Self::sub(left, right, &mut flags); let _ = Self::sub(left, right, &mut flags);
8 (8)
} }
}; };
cpu.set_flags(flags); cpu.set_flags(flags);
@@ -814,7 +814,7 @@ impl Instruction {
let left = cpu.register(CpuRegister::A); let left = cpu.register(CpuRegister::A);
let _ = Self::sub(left, Self::imm_byte(cpu), &mut flags); let _ = Self::sub(left, Self::imm_byte(cpu), &mut flags);
cpu.set_flags(flags); cpu.set_flags(flags);
8 (8)
} }
}, },
Instruction::LDHL => { Instruction::LDHL => {
@@ -826,7 +826,7 @@ impl Instruction {
cpu.set_register_pair(RegisterPair::HL, sum); cpu.set_register_pair(RegisterPair::HL, sum);
cpu.set_flags(flags); cpu.set_flags(flags);
cpu.bus.clock(); // FIXME: Is this in the right place? cpu.bus.clock(); // FIXME: Is this in the right place?
12 (12)
} }
Instruction::RET(cond) => { Instruction::RET(cond) => {
// RET cond | Return from subroutine if condition is true // RET cond | Return from subroutine if condition is true
@@ -840,9 +840,9 @@ impl Instruction {
if !flags.z() { if !flags.z() {
let addr = Self::pop(cpu); let addr = Self::pop(cpu);
Self::jump(cpu, addr); Self::jump(cpu, addr);
20 (20)
} else { } else {
8 (8)
} }
} }
JumpCondition::Zero => { JumpCondition::Zero => {
@@ -851,9 +851,9 @@ impl Instruction {
if flags.z() { if flags.z() {
let addr = Self::pop(cpu); let addr = Self::pop(cpu);
Self::jump(cpu, addr); Self::jump(cpu, addr);
20 (20)
} else { } else {
8 (8)
} }
} }
JumpCondition::NotCarry => { JumpCondition::NotCarry => {
@@ -862,9 +862,9 @@ impl Instruction {
if !flags.c() { if !flags.c() {
let addr = Self::pop(cpu); let addr = Self::pop(cpu);
Self::jump(cpu, addr); Self::jump(cpu, addr);
20 (20)
} else { } else {
8 (8)
} }
} }
JumpCondition::Carry => { JumpCondition::Carry => {
@@ -873,15 +873,15 @@ impl Instruction {
if flags.c() { if flags.c() {
let addr = Self::pop(cpu); let addr = Self::pop(cpu);
Self::jump(cpu, addr); Self::jump(cpu, addr);
20 (20)
} else { } else {
8 (8)
} }
} }
JumpCondition::Always => { JumpCondition::Always => {
let addr = Self::pop(cpu); let addr = Self::pop(cpu);
Self::jump(cpu, addr); Self::jump(cpu, addr);
16 (16)
} }
} }
} }
@@ -895,21 +895,21 @@ impl Instruction {
cpu.set_register_pair(pair.as_register_pair(), right); cpu.set_register_pair(pair.as_register_pair(), right);
} }
} }
12 (12)
} }
Instruction::RETI => { Instruction::RETI => {
// RETI | Return from subroutine, then enable interrupts // RETI | Return from subroutine, then enable interrupts
let addr = Self::pop(cpu); let addr = Self::pop(cpu);
Self::jump(cpu, addr); Self::jump(cpu, addr);
cpu.set_ime(ImeState::Enabled); cpu.set_ime(ImeState::Enabled);
16 (16)
} }
Instruction::JP(cond, location) => match location { Instruction::JP(cond, location) => match location {
JumpLocation::HL => { JumpLocation::HL => {
// JP HL | Store HL in program counter // JP HL | Store HL in program counter
let right = cpu.register_pair(RegisterPair::HL); let right = cpu.register_pair(RegisterPair::HL);
cpu.set_register_pair(RegisterPair::PC, right); cpu.set_register_pair(RegisterPair::PC, right);
4 (4)
} }
JumpLocation::ImmediateWord => { JumpLocation::ImmediateWord => {
// JP cond u16 | Store u16 in program counter if condition is true // JP cond u16 | Store u16 in program counter if condition is true
@@ -922,38 +922,38 @@ impl Instruction {
JumpCondition::NotZero => { JumpCondition::NotZero => {
if !flags.z() { if !flags.z() {
Self::jump(cpu, addr); Self::jump(cpu, addr);
16 (16)
} else { } else {
12 (12)
} }
} }
JumpCondition::Zero => { JumpCondition::Zero => {
if flags.z() { if flags.z() {
Self::jump(cpu, addr); Self::jump(cpu, addr);
16 (16)
} else { } else {
12 (12)
} }
} }
JumpCondition::NotCarry => { JumpCondition::NotCarry => {
if !flags.c() { if !flags.c() {
Self::jump(cpu, addr); Self::jump(cpu, addr);
16 (16)
} else { } else {
12 (12)
} }
} }
JumpCondition::Carry => { JumpCondition::Carry => {
if flags.c() { if flags.c() {
Self::jump(cpu, addr); Self::jump(cpu, addr);
16 (16)
} else { } else {
12 (12)
} }
} }
JumpCondition::Always => { JumpCondition::Always => {
Self::jump(cpu, addr); Self::jump(cpu, addr);
16 (16)
} }
} }
} }
@@ -961,12 +961,12 @@ impl Instruction {
Instruction::DI => { Instruction::DI => {
// DI | Disable IME // DI | Disable IME
cpu.set_ime(ImeState::Disabled); cpu.set_ime(ImeState::Disabled);
4 (4)
} }
Instruction::EI => { Instruction::EI => {
// EI | Enable IME after the next instruction // EI | Enable IME after the next instruction
cpu.set_ime(ImeState::EiExecuted); cpu.set_ime(ImeState::EiExecuted);
4 (4)
} }
Instruction::CALL(cond) => { Instruction::CALL(cond) => {
// CALL cond u16 | Push PC on the stack and store u16 in program counter if condition is true // CALL cond u16 | Push PC on the stack and store u16 in program counter if condition is true
@@ -982,9 +982,9 @@ impl Instruction {
cpu.bus.clock(); // internal branch decision cpu.bus.clock(); // internal branch decision
Self::push(cpu, return_addr); Self::push(cpu, return_addr);
cpu.set_register_pair(RegisterPair::PC, addr); cpu.set_register_pair(RegisterPair::PC, addr);
24 (24)
} else { } else {
12 (12)
} }
} }
JumpCondition::Zero => { JumpCondition::Zero => {
@@ -992,9 +992,9 @@ impl Instruction {
cpu.bus.clock(); // internal branch decision cpu.bus.clock(); // internal branch decision
Self::push(cpu, return_addr); Self::push(cpu, return_addr);
cpu.set_register_pair(RegisterPair::PC, addr); cpu.set_register_pair(RegisterPair::PC, addr);
24 (24)
} else { } else {
12 (12)
} }
} }
JumpCondition::NotCarry => { JumpCondition::NotCarry => {
@@ -1002,9 +1002,9 @@ impl Instruction {
cpu.bus.clock(); // internal branch decision cpu.bus.clock(); // internal branch decision
Self::push(cpu, return_addr); Self::push(cpu, return_addr);
cpu.set_register_pair(RegisterPair::PC, addr); cpu.set_register_pair(RegisterPair::PC, addr);
24 (24)
} else { } else {
12 (12)
} }
} }
JumpCondition::Carry => { JumpCondition::Carry => {
@@ -1012,16 +1012,16 @@ impl Instruction {
cpu.bus.clock(); // internal branch decision cpu.bus.clock(); // internal branch decision
Self::push(cpu, return_addr); Self::push(cpu, return_addr);
cpu.set_register_pair(RegisterPair::PC, addr); cpu.set_register_pair(RegisterPair::PC, addr);
24 (24)
} else { } else {
12 (12)
} }
} }
JumpCondition::Always => { JumpCondition::Always => {
cpu.bus.clock(); // internal branch decision cpu.bus.clock(); // internal branch decision
Self::push(cpu, return_addr); Self::push(cpu, return_addr);
cpu.set_register_pair(RegisterPair::PC, addr); cpu.set_register_pair(RegisterPair::PC, addr);
24 (24)
} }
} }
} }
@@ -1037,7 +1037,7 @@ impl Instruction {
Self::push(cpu, word); Self::push(cpu, word);
} }
} }
16 (16)
} }
Instruction::RST(vector) => { Instruction::RST(vector) => {
// RST vector | Push current address onto the stack, jump to 0x0000 + n // RST vector | Push current address onto the stack, jump to 0x0000 + n
@@ -1262,13 +1262,13 @@ impl Instruction {
let register = reg.cpu_register(); let register = reg.cpu_register();
let byte = cpu.register(register); let byte = cpu.register(register);
cpu.set_register(register, byte & !(1 << bit)); cpu.set_register(register, byte & !(1 << bit));
8 (8)
} }
IndirectHL => { IndirectHL => {
let addr = cpu.register_pair(RegisterPair::HL); let addr = cpu.register_pair(RegisterPair::HL);
let byte = Self::read_byte(&mut cpu.bus, addr); let byte = Self::read_byte(&mut cpu.bus, addr);
Self::write_byte(&mut cpu.bus, addr, byte & !(1 << bit)); Self::write_byte(&mut cpu.bus, addr, byte & !(1 << bit));
16 (16)
} }
} }
} }
@@ -1281,13 +1281,13 @@ impl Instruction {
let reg = reg.cpu_register(); let reg = reg.cpu_register();
let byte = cpu.register(reg); let byte = cpu.register(reg);
cpu.set_register(reg, byte | (1u8 << bit)); cpu.set_register(reg, byte | (1u8 << bit));
8 (8)
} }
IndirectHL => { IndirectHL => {
let addr = cpu.register_pair(RegisterPair::HL); let addr = cpu.register_pair(RegisterPair::HL);
let byte = Self::read_byte(&mut cpu.bus, addr); let byte = Self::read_byte(&mut cpu.bus, addr);
Self::write_byte(&mut cpu.bus, addr, byte | (1u8 << bit)); Self::write_byte(&mut cpu.bus, addr, byte | (1u8 << bit));
16 (16)
} }
} }
} }
@@ -1473,7 +1473,7 @@ impl Instruction {
let addr = cpu.register_pair(RegisterPair::PC); let addr = cpu.register_pair(RegisterPair::PC);
Self::push(cpu, addr); Self::push(cpu, addr);
cpu.set_register_pair(RegisterPair::PC, vector as u16); cpu.set_register_pair(RegisterPair::PC, vector as u16);
16 (16)
} }
/// Read u8 from memory (4 cycles) /// Read u8 from memory (4 cycles)

View File

@@ -1,8 +1,5 @@
use std::convert::TryInto;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg}; use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg};
use gb::emu::build::EmulatorBuilder;
use gb::{AudioSPSC, Cycle, GB_HEIGHT, GB_WIDTH}; use gb::{AudioSPSC, Cycle, GB_HEIGHT, GB_WIDTH};
use gilrs::Gilrs; use gilrs::Gilrs;
use pixels::{PixelsBuilder, SurfaceTexture}; use pixels::{PixelsBuilder, SurfaceTexture};
@@ -41,15 +38,13 @@ fn main() -> Result<()> {
) )
.get_matches(); .get_matches();
let mut emu_build = let rom_path = m
EmulatorBuilder::new().with_cart(m.value_of("rom").expect("ROM path provided"))?; .value_of("rom")
.expect("Required value 'rom' was provided");
if let Some(path) = m.value_of("boot") { let mut game_boy =
emu_build = emu_build.with_boot(path)?; gb::emu::init(m.value_of("boot"), rom_path).expect("Initialize DMG-01 Emulator");
} let rom_title = gb::emu::rom_title(&game_boy);
let mut emu = emu_build.finish();
let rom_title = emu.title();
let mut gamepad = Gilrs::new().expect("Initialize Controller Support"); let mut gamepad = Gilrs::new().expect("Initialize Controller Support");
@@ -73,14 +68,10 @@ fn main() -> Result<()> {
if AUDIO_ENABLED { if AUDIO_ENABLED {
let spsc: AudioSPSC<f32> = Default::default(); let spsc: AudioSPSC<f32> = Default::default();
let (prod, cons) = spsc.init(); let (prod, cons) = spsc.init();
let sink = { let sink = Sink::try_new(&stream_handle)?;
let s = Sink::try_new(&stream_handle)?; sink.append(cons);
s.append(cons); sink.set_volume(0.1); // TODO: Is this the right way to go about this?
s.set_volume(0.1); game_boy.apu_mut().attach_producer(prod);
s
};
emu.set_prod(prod);
std::thread::spawn(move || { std::thread::spawn(move || {
sink.sleep_until_end(); sink.sleep_until_end();
@@ -111,17 +102,12 @@ fn main() -> Result<()> {
pixels.resize_surface(size.width, size.height); pixels.resize_surface(size.width, size.height);
} }
cycle_count += gb::emu::run_frame(&mut emu, &mut gamepad, &input); cycle_count += gb::emu::run_frame(&mut game_boy, &mut gamepad, &input);
if cycle_count >= gb::emu::CYCLES_IN_FRAME { if cycle_count >= gb::emu::CYCLES_IN_FRAME {
cycle_count %= gb::emu::CYCLES_IN_FRAME; cycle_count %= gb::emu::CYCLES_IN_FRAME;
let buf: &mut [u8; GB_WIDTH * GB_HEIGHT * 4] = pixels gb::emu::draw(game_boy.ppu(), pixels.get_frame());
.get_frame()
.try_into()
.expect("Size of Pixel Buffer is GB_WIDTH * GB_HEIGHT * 4");
gb::emu::draw_frame(&emu, buf);
window.request_redraw(); window.request_redraw();
} }
} }

View File

@@ -418,8 +418,8 @@ impl Ppu {
self.frame_buf.swap_with_slice(&mut blank); self.frame_buf.swap_with_slice(&mut blank);
} }
pub(crate) fn frame_buf(&self) -> &[u8; GB_HEIGHT * GB_WIDTH * 4] { pub fn copy_to_gui(&self, frame: &mut [u8]) {
&self.frame_buf frame.copy_from_slice(self.frame_buf.as_ref());
} }
fn clock_fifo(&mut self) -> Option<GrayShade> { fn clock_fifo(&mut self) -> Option<GrayShade> {