From 6955ca49eeb610c24fdacb6a7f567a442e242bf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20L=C3=B6ffler?= Date: Sun, 29 Jan 2023 00:33:18 +0100 Subject: [PATCH] input/output instructions --- src/emulator/main.rs | 10 ++++++++++ src/emulator/mapper/mod.rs | 13 +++++++++++++ src/emulator/structs.rs | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/src/emulator/main.rs b/src/emulator/main.rs index 449b5ea..617b446 100644 --- a/src/emulator/main.rs +++ b/src/emulator/main.rs @@ -39,6 +39,16 @@ fn tick(state: &mut EmulatorState) { 0x00 => {} // NOP /* Special */ + 0xd3 => { + // OUT + let port = state.next_byte(); + state.write_io(port, state.a); + } + 0xdb => { + // IN + let port = state.next_byte(); + state.a = state.read_io(port); + } 0xfb => state.ei = true, // EI 0xf3 => state.ei = false, // DI 0x76 => { diff --git a/src/emulator/mapper/mod.rs b/src/emulator/mapper/mod.rs index 897e917..a887a3b 100644 --- a/src/emulator/mapper/mod.rs +++ b/src/emulator/mapper/mod.rs @@ -1,6 +1,19 @@ pub trait MemoryMapper { + /// Read a byte at the specified address through the memory mapper fn read(&mut self, address: u16) -> u8; + + /// Write a byte to the specified address through the memory mapper fn write(&mut self, address: u16, value: u8); + + /// Receive a byte from a device on the I/O bus through the memory mapper + #[allow(unused_variables)] + fn read_io(&mut self, port: u8) -> u8 { + 0 + } + + /// Send a byte to a device on the I/O bus through the memory mapper + #[allow(unused_variables)] + fn write_io(&mut self, port: u8, value: u8) {} } //#[cfg(test)] diff --git a/src/emulator/structs.rs b/src/emulator/structs.rs index 37570bf..aa82681 100644 --- a/src/emulator/structs.rs +++ b/src/emulator/structs.rs @@ -47,12 +47,20 @@ impl EmulatorState { } /// Read a byte at a specific address + /// + /// May return an indeterminate value if an invalid region is accessed. + /// May cause internal mapper state (eg. interrupts, emulated shift + /// registers) to be updated. Subsequent reads from the same address may + /// yield different values. #[inline] pub fn read_byte(&mut self, address: u16) -> u8 { self.mapper.read(address) } /// Write a byte at a specific address + /// + /// May do nothing if an invalid region is accessed. Subsequent reads + /// from the same address may yield different values. #[inline] pub fn write_byte(&mut self, address: u16, value: u8) { self.mapper.write(address, value); @@ -67,6 +75,11 @@ impl EmulatorState { } /// Read a 16-bit word at a specific address + /// + /// May return an indeterminate value if an invalid region is accessed. + /// May cause internal mapper state (eg. interrupts, emulated shift + /// registers) to be updated. Subsequent reads from the same address may + /// yield different values. #[inline] pub fn read_word(&mut self, address: u16) -> u16 { u16::from_le_bytes([ @@ -76,6 +89,9 @@ impl EmulatorState { } /// Write a 16-bit word at a specific address + /// + /// May do nothing if an invalid region is accessed. Subsequent reads + /// from the same address may yield different values. #[inline] pub fn write_word(&mut self, address: u16, value: u16) { let [low, high] = u16::to_le_bytes(value); @@ -90,6 +106,24 @@ impl EmulatorState { (self.pc, ..) = self.pc.overflowing_add(2); value } + + /// Receive a byte from a device on the I/O bus + /// + /// May return an indeterminate value if an invalid device is accessed. + /// May (and usually will) cause internal mapper state (eg. interrupts) + /// to be updated. + #[inline] + pub fn read_io(&mut self, port: u8) -> u8 { + self.mapper.read_io(port) + } + + /// Send a byte to a device on the I/O bus + /// + /// May do nothing if an invalid device is accessed. + #[inline] + pub fn write_io(&mut self, port: u8, value: u8) { + self.mapper.write_io(port, value); + } } impl Default for EmulatorState {