aboutsummaryrefslogtreecommitdiff
path: root/src/mmio.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/mmio.rs')
-rw-r--r--src/mmio.rs268
1 files changed, 268 insertions, 0 deletions
diff --git a/src/mmio.rs b/src/mmio.rs
new file mode 100644
index 0000000..e669ff8
--- /dev/null
+++ b/src/mmio.rs
@@ -0,0 +1,268 @@
+use crate::state::{MemError, Memory};
+use crate::io::{Serial, Audio};
+
+impl<S: Serial, A: Audio> Memory<S, A> {
+ 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;
+
+ ret |= if self.display.ly >= 0x90 {
+ 1
+ } else if self.display.stat < 80 {
+ 2
+ } else if self.display.stat < 280 {
+ 3
+ } else {
+ 0
+ };
+
+ if self.display.ly == self.display.lyc + 1 {
+ ret |= 0b100;
+ }
+
+ 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 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;
+ }
+ }
+ 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)?)?;
+ }
+ }
+ }
+ 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
+ );
+ }
+ }
+ }
+ 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(())
+ }
+}