aboutsummaryrefslogtreecommitdiff
path: root/src/io.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/io.rs')
-rw-r--r--src/io.rs409
1 files changed, 159 insertions, 250 deletions
diff --git a/src/io.rs b/src/io.rs
index 935109b..3e7330e 100644
--- a/src/io.rs
+++ b/src/io.rs
@@ -1,267 +1,176 @@
-use crate::state::{MemError, Memory};
+use std::time::SystemTime;
+use std::{thread, time};
-impl Memory {
- pub fn r_io(&self, addr: u8) -> u8 {
- if addr > 0x50 {
- println!("Reading from 0xff{:02x} not implemented yet", addr);
- }
- match addr {
- 0x00 => {
- if self.joypad_is_action {
- (self.joypad_reg >> 4) | 0b11010000
- } else {
- (self.joypad_reg & 0xf) | 0b11100000
- }
- }
- 0x01 => self.serial_data,
- 0x02 => self.serial_control,
- 0x04 => self.div,
- 0x0f => self.io[0x0f],
- 0x40 => self.display.lcdc,
- 0x42 => self.display.viewport_y,
- 0x43 => self.display.viewport_x,
- 0x41 => {
- let mut ret = 0b00001000 << self.display.lcd_interrupt_mode;
+use crate::state::GBState;
+use crate::consts;
- ret |= if self.display.ly >= 0x90 {
- 1
- } else if self.display.stat < 80 {
- 2
- } else if self.display.stat < 280 {
- 3
- } else {
- 0
- };
+pub trait Input {
+ fn update_events(&mut self, cycles: u128) -> Option<u128>;
+ fn get_action_gamepad_reg(&self) -> u8;
+ fn get_direction_gamepad_reg(&self) -> u8;
+}
- if self.display.ly == self.display.lyc + 1 {
- ret |= 0b100;
- }
+impl<T: Input + ?Sized> Input for Box<T> {
+ fn update_events(&mut self, cycles: u128) -> Option<u128> {
+ (**self).update_events(cycles)
+ }
+ fn get_action_gamepad_reg(&self) -> u8 {
+ (**self).get_action_gamepad_reg()
+ }
+ fn get_direction_gamepad_reg(&self) -> u8 {
+ (**self).get_direction_gamepad_reg()
+ }
+}
- ret
- }
- 0x44 => self.display.ly,
- 0x45 => self.display.lyc,
- 0x47 => self.display.bg_palette,
- 0x48 => self.display.obj_palettes[0],
- 0x49 => self.display.obj_palettes[1],
- 0x4a => self.display.window_y,
- 0x4b => self.display.window_x,
- 0x50 => {
- if self.boot_rom_on {
- 0xfe
- } else {
- 0xff
- }
- }
- _ => {
- println!("Reading from 0xff{:02x} not implemented yet", addr);
- self.io[addr as usize]
- }
+pub enum WindowSignal {
+ Exit,
+}
+
+pub trait Window {
+ fn update(&mut self, fb: &[u32; 160 * 144]) -> Option<WindowSignal>;
+}
+
+pub trait Serial {
+ // Should not be blocking
+ fn write(&mut self, byte: u8);
+ fn read(&mut self) -> u8;
+
+ fn new_transfer(&mut self) -> bool; // since last read
+ fn clock_master(&mut self) -> bool;
+
+ fn set_clock_master(&mut self, clock_master: bool);
+}
+
+pub trait Audio {
+ fn new<S: Iterator<Item = f32> + Send + 'static>(wave: S) -> Self;
+}
+
+pub trait LoadSave where Self::Error: std::fmt::Display, Self::Error: std::fmt::Debug {
+ type Error;
+ fn load_bootrom(&self, boot_rom: &mut [u8]) -> Result<(), Self::Error>;
+ fn load_rom(&self, rom: &mut [u8]) -> Result<(), Self::Error>;
+ fn load_external_ram(&self, external_ram: &mut [u8]) -> Result<(), Self::Error>;
+ fn save_external_ram(&self, external_ram: &[u8]) -> Result<(), Self::Error>;
+}
+
+pub struct Gameboy<
+ I: Input,
+ W: Window,
+ S: Serial,
+ A: Audio,
+ LS: LoadSave,
+> {
+ input: I,
+ window: W,
+ speed: f64,
+ state: GBState<S, A>,
+ load_save: LS,
+}
+
+impl<I: Input, W: Window, S: Serial, A: Audio, LS: LoadSave> Gameboy<I, W, S, A, LS> {
+ pub fn new(input: I, window: W, serial: S, load_save: LS, speed: f64) -> Self {
+ Self {
+ input,
+ window,
+ speed,
+ state: GBState::<S, A>::new(serial),
+ load_save,
}
}
- pub fn w_io(&mut self, addr: u8, value: u8) -> Result<(), MemError> {
- match addr {
- 0x00 => {
- self.joypad_is_action = !value & 0b00100000 != 0;
- }
- 0x01 => {
- self.serial_data = value;
- }
- 0x02 => {
- if value & 0x01 != 0 {
- self.serial.set_clock_master(true);
- println!("Set as master");
- } else if value & 0x01 != 0 {
- self.serial.set_clock_master(false);
- println!("Set as slave");
- }
- self.serial_control = value;
- }
- 0x04 => {
- self.div = 0;
- }
- 0x05 => {
- self.tima = value;
- }
- 0x06 => {
- self.tma = value;
- }
- 0x07 => {
- self.timer_enabled = value & 0b100 != 0;
- self.timer_speed = value & 0b11;
- }
- 0x0f => {
- self.io[0x0f] = value;
- }
- 0x10 => {
- self.audio.ch1.period_sweep_pace = (0b1110000 & value) >> 4;
- self.audio.ch1.period_sweep_direction = (0b1000 & value) >> 3;
- self.audio.ch1.period_sweep_slope = 0b111 & value;
- }
- 0x11 => {
- self.audio.ch1.duty = value >> 6;
- self.audio.ch1.length_timer = value & 0b111111;
- }
- 0x12 => {
- self.audio.ch1.initial_volume = value >> 4;
- self.audio.ch1.env_direction = (value & 0xf) >> 3;
- self.audio.ch1.sweep = value & 0b111;
- }
- 0x13 => {
- self.audio.ch1.period_value &= 0xff00;
- self.audio.ch1.period_value |= value as u16;
- }
- 0x14 => {
- self.audio.ch1.period_value &= 0xff;
- self.audio.ch1.period_value |= ((value & 0b111) as u16) << 8;
- self.audio.ch1.length_timer_enabled = value & 0b01000000 != 0;
- if value >> 7 == 1 {
- self.audio.ch1.update();
- }
- }
- 0x16 => {
- self.audio.ch2.duty = value >> 6;
- self.audio.ch2.length_timer = value & 0b111111;
- }
- 0x17 => {
- self.audio.ch2.initial_volume = value >> 4;
- self.audio.ch2.env_direction = (value & 0xf) >> 3;
- self.audio.ch2.sweep = value & 0b111;
- }
- 0x18 => {
- self.audio.ch2.period_value &= 0xff00;
- self.audio.ch2.period_value |= value as u16;
- }
- 0x19 => {
- self.audio.ch2.period_value &= 0xff;
- self.audio.ch2.period_value |= ((value & 0b111) as u16) << 8;
- self.audio.ch2.length_timer_enabled = value & 0b01000000 != 0;
- if value >> 7 == 1 {
- self.audio.ch2.update();
- }
- }
- 0x1a => {
- if value & 0b10000000 != 0 {
- self.audio.ch3.on = true;
- } else {
- self.audio.ch3.on = false;
- }
- self.audio.ch3.update();
- }
- 0x1b => {
- self.audio.ch3.length_timer = value & 0b111111;
- }
- 0x1c => {
- let s = (value >> 5) & 0b11;
- if s == 0 {
- self.audio.ch3.initial_volume = 0;
- } else {
- self.audio.ch3.initial_volume = 0xf >> (s - 1);
- }
- }
- 0x1d => {
- self.audio.ch3.period_value &= 0xff00;
- self.audio.ch3.period_value |= value as u16;
- }
- 0x1e => {
- self.audio.ch3.period_value &= 0xff;
- self.audio.ch3.period_value |= ((value & 0b111) as u16) << 8;
- self.audio.ch3.period_value /= 2;
- self.audio.ch3.length_timer_enabled = value & 0b01000000 != 0;
- if value >> 7 == 1 {
- self.audio.ch3.update();
- }
- }
- 0x20 => {
- self.audio.ch4.length_timer = value & 0b111111;
- }
- 0x21 => {
- self.audio.ch4.initial_volume = value >> 4;
- self.audio.ch4.env_direction = (value & 0xf) >> 3;
- self.audio.ch4.sweep = value & 0b111;
- }
- 0x22 => {
- self.audio.ch4.clock_shift = value >> 4;
- self.audio.ch4.lsfr_width = (value & 0xf) >> 3;
- self.audio.ch4.clock_divider = value & 0b111;
- }
- 0x23 => {
- self.audio.ch4.length_timer_enabled = value & 0b01000000 != 0;
- if value >> 7 == 1 {
- self.audio.ch4.update();
- }
- }
- 0x40 => self.display.lcdc = value,
- 0x41 => {
- if value & 0b01000000 != 0 {
- self.display.lcd_interrupt_mode = 3;
- } else if value & 0b00100000 != 0 {
- self.display.lcd_interrupt_mode = 2;
- } else if value & 0b00010000 != 0 {
- self.display.lcd_interrupt_mode = 1;
- } else if value & 0b00001000 != 0 {
- self.display.lcd_interrupt_mode = 0;
+
+ pub fn start(self) {
+ let Self {
+ mut window,
+ mut input,
+ speed,
+ mut state,
+ load_save,
+ } = self;
+
+ load_save.load_bootrom(&mut state.mem.boot_rom).unwrap();
+ load_save.load_rom(&mut state.mem.rom).unwrap();
+
+ if let Err(err) = load_save.load_external_ram(&mut state.mem.external_ram) {
+ println!(
+ "Loading save failed ({}). Initializing new external ram.",
+ err
+ );
+ }
+ let mut total_cycle_counter: u128 = 0;
+ let mut nanos_sleep: f64 = 0.0;
+ let mut halt_time = 0;
+ let mut was_previously_halted = false;
+
+ let mut last_ram_bank_enabled = false;
+ let mut now = SystemTime::now();
+ let mut next_precise_gamepad_update: Option<u128> = None;
+
+ loop {
+ if was_previously_halted && !state.mem.halt {
+ println!("Halt cycles {}", halt_time);
+ halt_time = 0;
+ }
+ was_previously_halted = state.mem.halt;
+ let c = if !state.mem.halt {
+ state.exec_opcode().unwrap()
+ } else {
+ halt_time += 4;
+ 4
+ };
+
+ state.cpu.dbg_cycle_counter += c;
+ total_cycle_counter += c as u128;
+
+ state.div_timer(c);
+ state.tima_timer(c);
+ state.update_display_interrupts(c);
+ state.check_interrupts().unwrap();
+ state.mem.update_serial();
+
+ nanos_sleep += c as f64 * (consts::CPU_CYCLE_LENGTH_NANOS as f64 / speed) as f64;
+
+ if nanos_sleep >= 0.0 || next_precise_gamepad_update.map_or(false, |c| (c >= total_cycle_counter)) {
+ next_precise_gamepad_update = input.update_events(total_cycle_counter);
+
+ let (action_button_reg, direction_button_reg) = (
+ input.get_action_gamepad_reg(),
+ input.get_direction_gamepad_reg(),
+ );
+
+ if state.mem.joypad_is_action
+ && (action_button_reg & (state.mem.joypad_reg >> 4)) != (state.mem.joypad_reg >> 4)
+ || (!state.mem.joypad_is_action
+ && (direction_button_reg & state.mem.joypad_reg & 0b1111)
+ != (state.mem.joypad_reg & 0b1111))
+ {
+ state.mem.io[0x0f] |= 0b10000;
}
+
+ state.mem.joypad_reg = direction_button_reg | (action_button_reg << 4);
}
- 0x45 => self.display.lyc = value,
- 0x42 => self.display.viewport_y = value,
- 0x43 => self.display.viewport_x = value,
- 0x46 => {
- if value < 0xe0 {
- let addr = (value as u16) << 8;
- for i in 0..0xa0 {
- self.w(0xfe00 | i, self.r(addr | i)?)?;
+
+ if nanos_sleep > 0.0 {
+ if let Some(fb) = state.mem.display.redraw_request {
+ if let Some(WindowSignal::Exit) = window.update(&fb) {
+ break;
}
}
- }
- 0x47 => self.display.bg_palette = value,
- 0x48 => self.display.obj_palettes[0] = value,
- 0x49 => self.display.obj_palettes[1] = value,
- 0x4a => self.display.window_y = value,
- 0x4b => self.display.window_x = value,
- 0x4f => self.display.vram_bank = value & 1,
- 0x50 => self.boot_rom_on = value & 1 == 0 && self.boot_rom_on,
- 0x68 => {
- self.bgcram_pointer = 0b111111 & value;
- self.bgcram_pointer_autoincrement = value & 0b10000000 != 0;
- }
- 0x69 => {
- self.display.cram[self.bgcram_pointer as usize] = value;
- if self.bgcram_pointer_autoincrement {
- self.bgcram_pointer += 1;
- self.bgcram_pointer &= 0b111111;
- }
- }
- 0x6a => {
- self.obcram_pointer = 0b111111 & value;
- self.obcram_pointer_autoincrement = value & 0b10000000 != 0;
- }
- 0x6b => {
- self.display.cram[self.obcram_pointer as usize + 0x40] = value;
- if self.obcram_pointer_autoincrement {
- self.obcram_pointer += 1;
- self.obcram_pointer &= 0b111111;
- }
- }
- _ => {
- if addr != 0x25 && addr != 0x24 && addr != 0x26 && addr < 0x30 && addr > 0x3f {
- println!(
- "Writing to 0xff{:02x} not implemented yet ({:02x})",
- addr, value
- );
+
+ thread::sleep(time::Duration::from_nanos(nanos_sleep as u64 / 10));
+
+ nanos_sleep =
+ nanos_sleep - SystemTime::now().duration_since(now).unwrap().as_nanos() as f64;
+ now = SystemTime::now();
+
+ if last_ram_bank_enabled && !state.mem.ram_bank_enabled {
+ if let Err(err) = load_save.save_external_ram(&state.mem.external_ram) {
+ println!("Failed to save external RAM ({})", err);
+ }
}
+ last_ram_bank_enabled = state.mem.ram_bank_enabled;
}
}
- self.io[addr as usize] = value;
-
- if addr >= 0x30 && addr <= 0x3f {
- let i = (addr - 0x30) as usize;
- self.audio.ch3.wave_pattern[i * 2] = value >> 4;
- self.audio.ch3.wave_pattern[i * 2 + 1] = value & 0xf;
- }
-
- Ok(())
}
}