aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAstatin <[email protected]>2024-09-14 23:49:32 +0900
committerAstatin <astatin@redacted>2024-09-14 23:49:32 +0900
commit7101d14f3bcc0af29b8d51b1f5d58689fee1a571 (patch)
treef0d24574e6ec3153a3f4a5969233946be43719a6
parent249921a2094d172e94543446e2af11dcba5075e8 (diff)
Add keyboard support & start supporting serial communication (but tetris doesn't work in 2p)
-rw-r--r--src/audio.rs35
-rw-r--r--src/display.rs5
-rw-r--r--src/gamepad.rs77
-rw-r--r--src/interrupts_timers.rs4
-rw-r--r--src/io.rs20
-rw-r--r--src/main.rs83
-rw-r--r--src/opcodes.rs1276
-rw-r--r--src/serial.rs136
-rw-r--r--src/state.rs44
9 files changed, 993 insertions, 687 deletions
diff --git a/src/audio.rs b/src/audio.rs
index 07f7c85..0a152a6 100644
--- a/src/audio.rs
+++ b/src/audio.rs
@@ -40,7 +40,38 @@ const SQUARE_WAVE_PATTERN_DUTY_3: [u8; 32] = [
*/
const NOISE_WAVE: [u16; 1023] = [
- 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1
+ 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1,
+ 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0,
+ 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0,
+ 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+ 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1,
+ 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0,
+ 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1,
+ 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0,
+ 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0,
+ 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1,
+ 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1,
+ 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0,
+ 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1,
+ 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1,
+ 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1,
+ 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1,
+ 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1,
+ 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
+ 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0,
+ 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0,
+ 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1,
+ 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1,
+ 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1,
+ 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1,
+ 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0,
+ 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0,
+ 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1,
+ 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1,
];
const SQUARE_WAVE_PATTERNS: [[u8; 32]; 4] = [
@@ -256,7 +287,7 @@ impl Iterator for NoiseWave {
for n in 0..SAMPLE_AVERAGING {
if self.num_sample as i32 + n as i32 - SAMPLE_AVERAGING as i32 >= 0 {
let ns = ((262144. / ((clock_divider) * (2 << self.clock_shift) as f32)) / 32768.)
- * (self.num_sample + n - (SAMPLE_AVERAGING / 2)) as f32;
+ * (self.num_sample + n - (SAMPLE_AVERAGING / 2)) as f32;
let i = (ns as f32 * (32768 as f32 / SAMPLE_RATE as f32)) as usize;
diff --git a/src/display.rs b/src/display.rs
index 8db432f..7abf5b3 100644
--- a/src/display.rs
+++ b/src/display.rs
@@ -29,7 +29,7 @@ pub enum DisplayInterrupt {
#[derive(Debug)]
pub struct Display {
- window: Window,
+ pub window: Window,
framebuffer: [u32; 160 * 144],
bg_buffer: [u8; 160 * 144],
@@ -64,7 +64,8 @@ impl Display {
Self {
window: Window::new(
"Gameboy Emulator",
- 512, 461,
+ 512,
+ 461,
/* 1200, 1080, */
WindowOptions::default(),
)
diff --git a/src/gamepad.rs b/src/gamepad.rs
index 8f05333..4ea82f7 100644
--- a/src/gamepad.rs
+++ b/src/gamepad.rs
@@ -1,11 +1,18 @@
-use crate::state::GBState;
-use gilrs::{Button, GamepadId, Gilrs, Event};
+use crate::display::Display;
+use gilrs::{Button, GamepadId, Gilrs};
+use minifb::Key;
pub struct Gamepad {
gilrs: Gilrs,
gamepad_id: Option<GamepadId>,
}
+pub trait Input {
+ fn update_events(&mut self);
+ fn get_action_gamepad_reg(&self) -> u8;
+ fn get_direction_gamepad_reg(&self) -> u8;
+}
+
impl Gamepad {
pub fn new() -> Self {
let mut gilrs = Gilrs::new().unwrap();
@@ -18,24 +25,24 @@ impl Gamepad {
None
};
-
Self { gilrs, gamepad_id }
}
- pub fn update_events(&mut self) {
- while let Some(_) = self.gilrs.next_event() {}
- }
-
- pub fn check_special_actions(&self, state: &mut GBState) {
+ pub fn check_special_actions(&self, is_debug: &mut bool) {
if let Some(gamepad_id) = self.gamepad_id {
if let Some(gamepad) = self.gilrs.connected_gamepad(gamepad_id) {
- state.is_debug = gamepad.is_pressed(Button::West);
+ *is_debug = gamepad.is_pressed(Button::West);
}
}
}
+}
- pub fn get_action_gamepad_reg(&self) -> u8 {
+impl Input for Gamepad {
+ fn update_events(&mut self) {
+ while let Some(_) = self.gilrs.next_event() {}
+ }
+ fn get_action_gamepad_reg(&self) -> u8 {
let mut res = 0xf;
if let Some(gamepad_id) = self.gamepad_id {
@@ -61,7 +68,7 @@ impl Gamepad {
res
}
- pub fn get_direction_gamepad_reg(&self) -> u8 {
+ fn get_direction_gamepad_reg(&self) -> u8 {
let mut res = 0xf;
if let Some(gamepad_id) = self.gamepad_id {
@@ -87,3 +94,51 @@ impl Gamepad {
res
}
}
+
+impl Input for Display {
+ fn update_events(&mut self) {}
+
+ fn get_action_gamepad_reg(&self) -> u8 {
+ let mut res = 0xf;
+
+ if self.window.is_key_down(Key::A) {
+ res &= 0b1110;
+ }
+
+ if self.window.is_key_down(Key::B) {
+ res &= 0b1101;
+ }
+
+ if self.window.is_key_down(Key::Backspace) {
+ res &= 0b1011;
+ }
+
+ if self.window.is_key_down(Key::Enter) {
+ res &= 0b0111;
+ }
+
+ res
+ }
+
+ fn get_direction_gamepad_reg(&self) -> u8 {
+ let mut res = 0xf;
+
+ if self.window.is_key_down(Key::Right) {
+ res &= 0b1110;
+ }
+
+ if self.window.is_key_down(Key::Left) {
+ res &= 0b1101;
+ }
+
+ if self.window.is_key_down(Key::Up) {
+ res &= 0b1011;
+ }
+
+ if self.window.is_key_down(Key::Down) {
+ res &= 0b0111;
+ }
+
+ res
+ }
+}
diff --git a/src/interrupts_timers.rs b/src/interrupts_timers.rs
index c339ce8..4ba07be 100644
--- a/src/interrupts_timers.rs
+++ b/src/interrupts_timers.rs
@@ -1,5 +1,5 @@
use crate::display::DisplayInterrupt;
-use crate::opcodes;
+use crate::serial::Serial;
use crate::state::{GBState, MemError};
const TIMA_TIMER_SPEEDS: [u64; 4] = [1024, 16, 64, 256];
@@ -10,7 +10,7 @@ impl GBState {
let interrupts = self.mem.io[0x0f] & self.mem.interrupts_register & 0b11111;
for i in 0..5 {
if interrupts & (1 << i) != 0 {
- opcodes::push(self, self.cpu.pc)?;
+ self.push(self.cpu.pc)?;
self.mem.ime = false;
self.cpu.pc = 0x40 + (i << 3);
diff --git a/src/io.rs b/src/io.rs
index df50c51..2aa8e87 100644
--- a/src/io.rs
+++ b/src/io.rs
@@ -1,3 +1,4 @@
+use crate::serial::Serial;
use crate::state::{MemError, Memory};
impl Memory {
@@ -13,6 +14,8 @@ impl Memory {
(self.joypad_reg & 0xf) | 0b11100000
}
}
+ 0x01 => self.serial_data,
+ 0x02 => self.serial_control,
0x04 => self.div,
0x40 => self.display.lcdc,
0x42 => self.display.viewport_y,
@@ -57,7 +60,7 @@ impl Memory {
}
}
_ => {
- // println!("Reading from 0xff{:02x} not implemented yet", addr);
+ println!("Reading from 0xff{:02x} not implemented yet", addr);
self.io[addr as usize]
}
}
@@ -68,6 +71,19 @@ impl Memory {
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;
}
@@ -236,7 +252,7 @@ impl Memory {
}
}
_ => {
- if addr >= 0x4d {
+ if addr != 0x25 && addr != 0x24 && addr != 0x26 && addr < 0x30 && addr > 0x3f {
println!(
"Writing to 0xff{:02x} not implemented yet ({:02x})",
addr, value
diff --git a/src/main.rs b/src/main.rs
index f0a7361..b057486 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -5,38 +5,15 @@ pub mod gamepad;
pub mod interrupts_timers;
pub mod io;
pub mod opcodes;
+pub mod serial;
pub mod state;
-use crate::gamepad::Gamepad;
-use crate::state::{GBState, MemError};
+use crate::gamepad::{Gamepad, Input};
+use crate::state::GBState;
use clap::Parser;
use std::time::SystemTime;
use std::{thread, time};
-pub fn exec_opcode(state: &mut GBState) -> Result<u64, MemError> {
- let opcode = state.mem.r(state.cpu.pc)?;
-
- if state.is_debug {
- println!(
- "{:02x}:{:04x} = {:02x} (IME: {})",
- state.mem.rom_bank, state.cpu.pc, opcode, state.mem.ime
- );
- }
-
- state.cpu.pc += 1;
-
- let n1 = (opcode >> 3) & 0b111;
- let n2 = opcode & 0b111;
-
- match opcode >> 6 {
- 0b00 => opcodes::op00(state, n1, n2),
- 0b01 => opcodes::op01(state, n1, n2),
- 0b10 => opcodes::op10(state, n1, n2),
- 0b11 => opcodes::op11(state, n1, n2),
- _ => panic!(),
- }
-}
-
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
@@ -45,7 +22,16 @@ struct Cli {
/// Setting this saves battery by using thread::sleep instead of spin_sleeping. It can result in lag and inconsistent timing.
#[arg(long)]
- thread_sleep: bool,
+ loop_lock_timing: bool,
+
+ #[arg(long)]
+ fifo_input: Option<String>,
+
+ #[arg(long)]
+ fifo_output: Option<String>,
+
+ #[arg(short, long, default_value_t = false)]
+ keyboard: bool,
#[arg(short, long, default_value_t = 1.0)]
speed: f32,
@@ -56,11 +42,17 @@ fn main() {
println!("Initializing Gamepad...");
- let mut gamepad = Gamepad::new();
-
println!("Starting {:?}...", &cli.rom);
- let mut state = GBState::new();
+ let mut state = match (cli.fifo_input, cli.fifo_output) {
+ (Some(fifo_input), Some(fifo_output)) => {
+ GBState::new(Box::new(serial::FIFOSerial::new(fifo_input, fifo_output)))
+ }
+ (None, None) => GBState::new(Box::new(serial::UnconnectedSerial {})),
+ _ => panic!("If using fifo serial, both input and output should be set"),
+ };
+
+ let mut gamepad = Gamepad::new();
let save_file = format!("{}.sav", &cli.rom);
@@ -87,7 +79,7 @@ fn main() {
}
was_previously_halted = state.mem.halt;
let c = if !state.mem.halt {
- exec_opcode(&mut state).unwrap()
+ state.exec_opcode().unwrap()
} else {
halt_time += 4;
4
@@ -97,18 +89,27 @@ fn main() {
state.tima_timer(c);
state.update_display_interrupts(c);
state.check_interrupts().unwrap();
+ state.mem.update_serial();
nanos_sleep += c as i128 * (consts::CPU_CYCLE_LENGTH_NANOS as f32 / cli.speed) as i128;
if nanos_sleep > 0 {
- gamepad.update_events();
+ let (action_button_reg, direction_button_reg) = if cli.keyboard {
+ (
+ state.mem.display.get_action_gamepad_reg(),
+ state.mem.display.get_direction_gamepad_reg(),
+ )
+ } else {
+ gamepad.update_events();
- let action_button_reg = gamepad.get_action_gamepad_reg();
- let direction_button_reg = gamepad.get_direction_gamepad_reg();
- gamepad.check_special_actions(&mut state);
+ (
+ gamepad.get_action_gamepad_reg(),
+ gamepad.get_direction_gamepad_reg(),
+ )
+ };
+ // gamepad.check_special_actions(&mut state.is_debug);
if state.mem.joypad_is_action
- && (action_button_reg & (state.mem.joypad_reg >> 4))
- != (state.mem.joypad_reg >> 4)
+ && (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))
@@ -118,16 +119,18 @@ fn main() {
state.mem.joypad_reg = direction_button_reg | (action_button_reg << 4);
- if cli.thread_sleep {
+ if !cli.loop_lock_timing {
thread::sleep(time::Duration::from_nanos(nanos_sleep as u64 / 10));
} else {
while SystemTime::now().duration_since(now).unwrap().as_nanos()
< nanos_sleep as u128
- {}
+ {
+ for _ in 0..100_000_000 {}
+ }
}
nanos_sleep =
- nanos_sleep - SystemTime::now().duration_since(now).unwrap().as_nanos() as i128;
+ nanos_sleep - SystemTime::now().duration_since(now).unwrap().as_nanos() as i128;
now = SystemTime::now();
if last_ram_bank_enabled && !state.mem.ram_bank_enabled {
diff --git a/src/opcodes.rs b/src/opcodes.rs
index f238239..2502782 100644
--- a/src/opcodes.rs
+++ b/src/opcodes.rs
@@ -1,825 +1,851 @@
+use crate::serial::Serial;
use crate::state::{flag, reg, GBState, MemError};
// The opcodes functions are returning the number of cycles used.
-pub fn r_16b_from_pc(state: &mut GBState) -> Result<u16, MemError> {
- let p: u16 = state.mem.r(state.cpu.pc)? as u16 | ((state.mem.r(state.cpu.pc + 1)? as u16) << 8);
- state.cpu.pc += 2;
+impl GBState {
+ fn r_16b_from_pc(&mut self) -> Result<u16, MemError> {
+ let p: u16 = self.mem.r(self.cpu.pc)? as u16 | ((self.mem.r(self.cpu.pc + 1)? as u16) << 8);
+ self.cpu.pc += 2;
- Ok(p)
-}
+ Ok(p)
+ }
-pub fn r_8b_from_pc(state: &mut GBState) -> Result<u8, MemError> {
- let p = state.mem.r(state.cpu.pc)?;
- state.cpu.pc += 1;
+ fn r_8b_from_pc(&mut self) -> Result<u8, MemError> {
+ let p = self.mem.r(self.cpu.pc)?;
+ self.cpu.pc += 1;
- Ok(p)
-}
+ Ok(p)
+ }
-pub fn ldrr(state: &mut GBState, n1: u8, n2: u8) -> Result<(), MemError> {
- // Load a register into another register
- // LD r, r
- state.w_reg(n1, state.r_reg(n2)?)
-}
+ fn ldrr(&mut self, n1: u8, n2: u8) -> Result<(), MemError> {
+ // Load a register into another register
+ // LD r, r
+ self.w_reg(n1, self.r_reg(n2)?)
+ }
-pub fn ldr8(state: &mut GBState, n1: u8) -> Result<u64, MemError> {
- // Load an raw 8b value into a register
- let p = r_8b_from_pc(state)?;
+ fn ldr8(&mut self, n1: u8) -> Result<u64, MemError> {
+ // Load an raw 8b value into a register
+ let p = self.r_8b_from_pc()?;
- state.w_reg(n1, p)?;
- Ok(8)
-}
+ self.w_reg(n1, p)?;
+ Ok(8)
+ }
-pub fn ldrr16(state: &mut GBState, rr: u8, x: u16) {
- // Load a raw 16b value into a register
- state.cpu.w16(rr, x);
-}
+ fn ldrr16(&mut self, rr: u8, x: u16) {
+ // Load a raw 16b value into a register
+ self.cpu.w16(rr, x);
+ }
-pub fn ldnnsp(state: &mut GBState) -> Result<u64, MemError> {
- // Load SP into an arbitrary position in memory
- let p = r_16b_from_pc(state)?;
+ fn ldnnsp(&mut self) -> Result<u64, MemError> {
+ // Load SP into an arbitrary position in memory
+ let p = self.r_16b_from_pc()?;
- state.mem.w(p, (state.cpu.sp & 0xff) as u8)?;
- state.mem.w(p + 1, (state.cpu.sp >> 8) as u8)?;
- Ok(20)
-}
+ self.mem.w(p, (self.cpu.sp & 0xff) as u8)?;
+ self.mem.w(p + 1, (self.cpu.sp >> 8) as u8)?;
+ Ok(20)
+ }
-pub fn ldsphl(state: &mut GBState) -> u64 {
- state.cpu.sp = state.cpu.r16(reg::HL);
- 8
-}
+ fn ldsphl(&mut self) -> u64 {
+ self.cpu.sp = self.cpu.r16(reg::HL);
+ 8
+ }
-pub fn ldnna(state: &mut GBState, nn: u16) -> Result<(), MemError> {
- // Load A into an arbitrary position in memory
- state.mem.w(nn, state.cpu.r[reg::A as usize])?;
- Ok(())
-}
+ fn ldnna(&mut self, nn: u16) -> Result<(), MemError> {
+ // Load A into an arbitrary position in memory
+ self.mem.w(nn, self.cpu.r[reg::A as usize])?;
+ Ok(())
+ }
-pub fn ldann(state: &mut GBState, nn: u16) -> Result<(), MemError> {
- // Load A from an arbitrary position in memory
- state.cpu.r[reg::A as usize] = state.mem.r(nn)?;
- Ok(())
-}
+ fn ldann(&mut self, nn: u16) -> Result<(), MemError> {
+ // Load A from an arbitrary position in memory
+ self.cpu.r[reg::A as usize] = self.mem.r(nn)?;
+ Ok(())
+ }
-pub fn push(state: &mut GBState, x: u16) -> Result<(), MemError> {
- state.cpu.sp -= 2;
+ pub fn push(&mut self, x: u16) -> Result<(), MemError> {
+ self.cpu.sp -= 2;
- state.mem.w(state.cpu.sp, (x & 0xff) as u8)?;
+ self.mem.w(self.cpu.sp, (x & 0xff) as u8)?;
- state.mem.w(state.cpu.sp + 1, (x >> 8) as u8)?;
+ self.mem.w(self.cpu.sp + 1, (x >> 8) as u8)?;
- Ok(())
-}
+ Ok(())
+ }
-pub fn pop(state: &mut GBState) -> Result<u16, MemError> {
- let res = state.mem.r(state.cpu.sp)? as u16 | ((state.mem.r(state.cpu.sp + 1)? as u16) << 8);
+ fn pop(&mut self) -> Result<u16, MemError> {
+ let res = self.mem.r(self.cpu.sp)? as u16 | ((self.mem.r(self.cpu.sp + 1)? as u16) << 8);
- state.cpu.sp += 2;
+ self.cpu.sp += 2;
- Ok(res)
-}
+ Ok(res)
+ }
-pub fn jr8(state: &mut GBState) -> Result<u64, MemError> {
- // Unconditional relative jump
- let p = r_8b_from_pc(state)?;
+ fn jr8(&mut self) -> Result<u64, MemError> {
+ // Unconditional relative jump
+ let p = self.r_8b_from_pc()?;
- state.cpu.pc = (state.cpu.pc as i16 + p as i8 as i16) as u16;
+ self.cpu.pc = (self.cpu.pc as i16 + p as i8 as i16) as u16;
- Ok(12)
-}
+ Ok(12)
+ }
+
+ fn jrcc8(&mut self, n1: u8) -> Result<u64, MemError> {
+ // Conditional relative jump
+ let p = self.r_8b_from_pc()?;
+ let mut cycles = 8;
-pub fn jrcc8(state: &mut GBState, n1: u8) -> Result<u64, MemError> {
- // Conditional relative jump
- let p = r_8b_from_pc(state)?;
- let mut cycles = 8;
+ if self.cpu.check_flag(n1 & 0b11) {
+ cycles += 4;
+ self.cpu.pc = (self.cpu.pc as i16 + p as i8 as i16) as u16;
+ }
- if state.cpu.check_flag(n1 & 0b11) {
- cycles += 4;
- state.cpu.pc = (state.cpu.pc as i16 + p as i8 as i16) as u16;
+ Ok(cycles)
}
- Ok(cycles)
-}
+ fn jp16(&mut self) -> Result<u64, MemError> {
+ // Unconditional absolute jump
+ let p = self.r_16b_from_pc()?;
-pub fn jp16(state: &mut GBState) -> Result<u64, MemError> {
- // Unconditional absolute jump
- let p = r_16b_from_pc(state)?;
+ self.cpu.pc = p;
- state.cpu.pc = p;
+ Ok(16)
+ }
- Ok(16)
-}
+ fn jphl(&mut self) -> u64 {
+ // Unconditional absolute jump to HL
+ self.cpu.pc = self.cpu.r16(reg::HL);
-pub fn jphl(state: &mut GBState) -> u64 {
- // Unconditional absolute jump to HL
- state.cpu.pc = state.cpu.r16(reg::HL);
+ 4
+ }
- 4
-}
+ fn jpcc16(&mut self, n1: u8) -> Result<u64, MemError> {
+ // Conditional absolute jump
+ let p = self.r_16b_from_pc()?;
+ let mut cycles = 8;
-pub fn jpcc16(state: &mut GBState, n1: u8) -> Result<u64, MemError> {
- // Conditional absolute jump
- let p = r_16b_from_pc(state)?;
- let mut cycles = 8;
+ if self.cpu.check_flag(n1 & 0b11) {
+ cycles += 4;
+ self.cpu.pc = p;
+ }
- if state.cpu.check_flag(n1 & 0b11) {
- cycles += 4;
- state.cpu.pc = p;
+ Ok(cycles)
}
- Ok(cycles)
-}
+ fn call(&mut self) -> Result<u64, MemError> {
+ // Unconditional function call
+ let p = self.r_16b_from_pc()?;
-pub fn call(state: &mut GBState) -> Result<u64, MemError> {
- // Unconditional function call
- let p = r_16b_from_pc(state)?;
+ self.push(self.cpu.pc)?;
+ self.cpu.pc = p;
- push(state, state.cpu.pc)?;
- state.cpu.pc = p;
+ Ok(24)
+ }
- Ok(24)
-}
+ fn callcc(&mut self, n1: u8) -> Result<u64, MemError> {
+ // Conditional function call
+ let p = self.r_16b_from_pc()?;
+ let mut cycles = 12;
-pub fn callcc(state: &mut GBState, n1: u8) -> Result<u64, MemError> {
- // Conditional function call
- let p = r_16b_from_pc(state)?;
- let mut cycles = 12;
+ if self.cpu.check_flag(n1 & 0b11) {
+ cycles += 12;
+ self.push(self.cpu.pc)?;
+ self.cpu.pc = p;
+ }
- if state.cpu.check_flag(n1 & 0b11) {
- cycles += 12;
- push(state, state.cpu.pc)?;
- state.cpu.pc = p;
+ Ok(cycles)
}
- Ok(cycles)
-}
+ fn ret(&mut self) -> Result<u64, MemError> {
+ let res = self.pop()?;
+
+ if res == 0 {
+ println!("DEBUG: {:?}", self.cpu);
+ panic!("RET to start");
+ }
-pub fn ret(state: &mut GBState) -> Result<u64, MemError> {
- let res = pop(state)?;
+ self.cpu.pc = res;
- if res == 0 {
- println!("DEBUG: {:?}", state.cpu);
- panic!("RET to start");
+ Ok(16)
}
- state.cpu.pc = res;
-
- Ok(16)
-}
+ fn retcc(&mut self, n1: u8) -> Result<u64, MemError> {
+ let mut cycles = 8;
+ if self.cpu.check_flag(n1 & 0b11) {
+ cycles += 12;
+ self.cpu.pc = self.pop()?;
+ }
-pub fn retcc(state: &mut GBState, n1: u8) -> Result<u64, MemError> {
- let mut cycles = 8;
- if state.cpu.check_flag(n1 & 0b11) {
- cycles += 12;
- state.cpu.pc = pop(state)?;
+ Ok(cycles)
}
- Ok(cycles)
-}
+ fn ld00a(&mut self, n1: u8) -> Result<u64, MemError> {
+ // Load register A into or from memory pointed by rr (BC, DE or HL(+/-))
+ // LD (rr), A
+ // LD A, (rr)
+ let ptr_reg = match n1 & 0b110 {
+ 0b000 => reg::B,
+ 0b010 => reg::C,
+ _ => reg::HL,
+ };
-pub fn ld00a(state: &mut GBState, n1: u8) -> Result<u64, MemError> {
- // Load register A into or from memory pointed by rr (BC, DE or HL(+/-))
- // LD (rr), A
- // LD A, (rr)
- let ptr_reg = match n1 & 0b110 {
- 0b000 => reg::B,
- 0b010 => reg::C,
- _ => reg::HL,
- };
+ if n1 & 0b001 == 1 {
+ self.cpu.r[reg::A as usize] = self.mem.r(self.cpu.r16(ptr_reg))?;
+ } else {
+ self.mem
+ .w(self.cpu.r16(ptr_reg), self.cpu.r[reg::A as usize])?;
+ }
- if n1 & 0b001 == 1 {
- state.cpu.r[reg::A as usize] = state.mem.r(state.cpu.r16(ptr_reg))?;
- } else {
- state
- .mem
- .w(state.cpu.r16(ptr_reg), state.cpu.r[reg::A as usize])?;
- }
+ if n1 & 0b110 == 0b100 {
+ self.cpu.w16(reg::HL, self.cpu.r16(reg::HL) + 1); // (HL+)
+ }
- if n1 & 0b110 == 0b100 {
- state.cpu.w16(reg::HL, state.cpu.r16(reg::HL) + 1); // (HL+)
- }
+ if n1 & 0b110 == 0b110 {
+ self.cpu.w16(reg::HL, self.cpu.r16(reg::HL) - 1); // (HL-)
+ }
- if n1 & 0b110 == 0b110 {
- state.cpu.w16(reg::HL, state.cpu.r16(reg::HL) - 1); // (HL-)
+ Ok(8)
}
- Ok(8)
-}
+ fn inc8(&mut self, n1: u8) -> Result<u64, MemError> {
+ // Increment 8 bit register
+ self.w_reg(n1, self.r_reg(n1)? + 1)?;
+ self.cpu.r[reg::F as usize] &= !(flag::N | flag::ZF | flag::H);
+ if self.r_reg(n1)? == 0 {
+ self.cpu.r[reg::F as usize] |= flag::ZF;
+ }
-pub fn inc8(state: &mut GBState, n1: u8) -> Result<u64, MemError> {
- // Increment 8 bit register
- state.w_reg(n1, state.r_reg(n1)? + 1)?;
- state.cpu.r[reg::F as usize] &= !(flag::N | flag::ZF | flag::H);
- if state.r_reg(n1)? == 0 {
- state.cpu.r[reg::F as usize] |= flag::ZF;
- }
+ if self.r_reg(n1)? & 0xf == 0x0 {
+ self.cpu.r[reg::F as usize] |= flag::H;
+ }
- if state.r_reg(n1)? & 0xf == 0x0 {
- state.cpu.r[reg::F as usize] |= flag::H;
+ Ok(4)
}
- Ok(4)
-}
+ fn dec8(&mut self, n1: u8) -> Result<u64, MemError> {
+ // Decrement 8 bit register
+ self.w_reg(n1, self.r_reg(n1)? - 1)?;
+ self.cpu.r[reg::F as usize] |= flag::N;
+
+ self.cpu.r[reg::F as usize] &= !(flag::ZF | flag::H);
+ if self.r_reg(n1)? == 0 {
+ self.cpu.r[reg::F as usize] |= flag::ZF;
+ }
-pub fn dec8(state: &mut GBState, n1: u8) -> Result<u64, MemError> {
- // Decrement 8 bit register
- state.w_reg(n1, state.r_reg(n1)? - 1)?;
- state.cpu.r[reg::F as usize] |= flag::N;
+ if self.r_reg(n1)? & 0xf == 0xf {
+ self.cpu.r[reg::F as usize] |= flag::H;
+ }
- state.cpu.r[reg::F as usize] &= !(flag::ZF | flag::H);
- if state.r_reg(n1)? == 0 {
- state.cpu.r[reg::F as usize] |= flag::ZF;
+ Ok(4)
}
- if state.r_reg(n1)? & 0xf == 0xf {
- state.cpu.r[reg::F as usize] |= flag::H;
+ fn inc16(&mut self, rr: u8) -> u64 {
+ // Increment 16 bit register
+ self.cpu.w16(rr, self.cpu.r16(rr) + 1);
+ 8
}
- Ok(4)
-}
+ fn dec16(&mut self, rr: u8) -> u64 {
+ // Decrement 16 bit register
+ self.cpu.w16(rr, self.cpu.r16(rr) - 1);
+ 8
+ }
-pub fn inc16(state: &mut GBState, rr: u8) -> u64 {
- // Increment 16 bit register
- state.cpu.w16(rr, state.cpu.r16(rr) + 1);
- 8
-}
+ fn ccf(&mut self) {
+ // Flip carry flag
+ self.cpu.r[reg::F as usize] = (self.cpu.r[reg::F as usize] & 0b10011111) ^ 0b00010000
+ }
-pub fn dec16(state: &mut GBState, rr: u8) -> u64 {
- // Decrement 16 bit register
- state.cpu.w16(rr, state.cpu.r16(rr) - 1);
- 8
-}
+ fn scf(&mut self) {
+ // Set carry flag
+ self.cpu.r[reg::F as usize] = (self.cpu.r[reg::F as usize] & 0b10011111) | 0b00010000
+ }
-pub fn ccf(state: &mut GBState) {
- // Flip carry flag
- state.cpu.r[reg::F as usize] = (state.cpu.r[reg::F as usize] & 0b10011111) ^ 0b00010000
-}
+ fn daa(&mut self) {
+ // Decimal Adjust Accumulator
+ // Adjust the A register after a addition or substraction to keep valid BCD representation
+ let nibble_low = self.cpu.r[reg::A as usize] & 0b1111;
+ let sub_flag = (self.cpu.r[reg::F as usize] & flag::N) != 0;
+ let half_carry_flag = (self.cpu.r[reg::F as usize] & flag::H) != 0;
-pub fn scf(state: &mut GBState) {
- // Set carry flag
- state.cpu.r[reg::F as usize] = (state.cpu.r[reg::F as usize] & 0b10011111) | 0b00010000
-}
+ if (half_carry_flag || nibble_low > 9) && !sub_flag {
+ self.cpu.r[reg::A as usize] += 0x06;
+ }
+ if (half_carry_flag || nibble_low > 9) && sub_flag {
+ self.cpu.r[reg::A as usize] -= 0x06;
+ }
-pub fn daa(state: &mut GBState) {
- // Decimal Adjust Accumulator
- // Adjust the A register after a addition or substraction to keep valid BCD representation
- let nibble_low = state.cpu.r[reg::A as usize] & 0b1111;
- let sub_flag = (state.cpu.r[reg::F as usize] & flag::N) != 0;
- let half_carry_flag = (state.cpu.r[reg::F as usize] & flag::H) != 0;
+ let nibble_high = self.cpu.r[reg::A as usize] >> 4;
- if (half_carry_flag || nibble_low > 9) && !sub_flag {
- state.cpu.r[reg::A as usize] += 0x06;
- }
- if (half_carry_flag || nibble_low > 9) && sub_flag {
- state.cpu.r[reg::A as usize] -= 0x06;
- }
+ self.cpu.r[reg::F as usize] &= !(flag::CY | flag::ZF);
- let nibble_high = state.cpu.r[reg::A as usize] >> 4;
+ if nibble_high > 9 && !sub_flag {
+ self.cpu.r[reg::A as usize] += 0x60;
+ self.cpu.r[reg::F as usize] |= flag::CY;
+ }
+ if nibble_high > 9 && sub_flag {
+ self.cpu.r[reg::A as usize] -= 0x60;
+ self.cpu.r[reg::F as usize] |= flag::CY;
+ }
- state.cpu.r[reg::F as usize] &= !(flag::CY | flag::ZF);
+ if self.cpu.r[reg::A as usize] == 0 {
+ self.cpu.r[reg::F as usize] |= flag::ZF;
+ }
- if nibble_high > 9 && !sub_flag {
- state.cpu.r[reg::A as usize] += 0x60;
- state.cpu.r[reg::F as usize] |= flag::CY;
- }
- if nibble_high > 9 && sub_flag {
- state.cpu.r[reg::A as usize] -= 0x60;
- state.cpu.r[reg::F as usize] |= flag::CY;
+ self.cpu.r[reg::F as usize] &= !flag::H;
}
- if state.cpu.r[reg::A as usize] == 0 {
- state.cpu.r[reg::F as usize] |= flag::ZF;
+ fn cpl(&mut self) {
+ // Flip all bits in register A
+ self.cpu.r[reg::F as usize] = self.cpu.r[reg::F as usize] | flag::N | flag::H;
+ self.cpu.r[reg::A as usize] ^= 0xff;
}
- state.cpu.r[reg::F as usize] &= !flag::H;
-}
+ fn addsp8(&mut self) -> Result<u64, MemError> {
+ let n = self.r_8b_from_pc()? as i8;
-pub fn cpl(state: &mut GBState) {
- // Flip all bits in register A
- state.cpu.r[reg::F as usize] = state.cpu.r[reg::F as usize] | flag::N | flag::H;
- state.cpu.r[reg::A as usize] ^= 0xff;
-}
-
-pub fn addsp8(state: &mut GBState) -> Result<u64, MemError> {
- let n = r_8b_from_pc(state)? as i8;
+ self.cpu.sp = (self.cpu.sp as i32 + n as i32) as u16;
- state.cpu.sp = (state.cpu.sp as i32 + n as i32) as u16;
+ self.cpu.r[reg::F as usize] &= !(flag::N | flag::H | flag::CY);
- state.cpu.r[reg::F as usize] &= !(flag::N | flag::H | flag::CY);
+ if (self.cpu.sp & 0xff) as i32 + n as i32 & !0xff != 0 {
+ self.cpu.r[reg::F as usize] |= flag::H;
+ }
- if (state.cpu.sp & 0xff) as i32 + n as i32 & !0xff != 0 {
- state.cpu.r[reg::F as usize] |= flag::H;
+ if (self.cpu.sp as i32 + n as i32) & !0xffff != 0 {
+ self.cpu.r[reg::F as usize] |= flag::CY;
+ }
+ Ok(16)
}
- if (state.cpu.sp as i32 + n as i32) & !0xffff != 0 {
- state.cpu.r[reg::F as usize] |= flag::CY;
- }
- Ok(16)
-}
+ fn add(&mut self, x: u8) {
+ // ADD a number to A and store the result in A
+ let res = x as u16 + self.cpu.r[reg::A as usize] as u16;
-pub fn add(state: &mut GBState, x: u8) {
- // ADD a number to A and store the result in A
- let res = x as u16 + state.cpu.r[reg::A as usize] as u16;
+ self.cpu.r[reg::F as usize] = 0;
- state.cpu.r[reg::F as usize] = 0;
+ if (x & 0xf) + (self.cpu.r[reg::A as usize] & 0xf) > 0xf {
+ self.cpu.r[reg::F as usize] |= flag::H;
+ }
- if (x & 0xf) + (state.cpu.r[reg::A as usize] & 0xf) > 0xf {
- state.cpu.r[reg::F as usize] |= flag::H;
- }
+ if res > 0xff {
+ self.cpu.r[reg::F as usize] |= flag::CY;
+ }
- if res > 0xff {
- state.cpu.r[reg::F as usize] |= flag::CY;
+ self.cpu.r[reg::A as usize] = res as u8;
+
+ if self.cpu.r[reg::A as usize] == 0 {
+ self.cpu.r[reg::F as usize] |= flag::ZF;
+ }
}
- state.cpu.r[reg::A as usize] = res as u8;
+ fn addhlrr(&mut self, rr: u8) -> u64 {
+ let n = self.cpu.r16(rr);
+ let hl = self.cpu.r16(reg::HL);
- if state.cpu.r[reg::A as usize] == 0 {
- state.cpu.r[reg::F as usize] |= flag::ZF;
- }
-}
+ self.cpu.w16(reg::HL, (hl as i32 + n as i32) as u16);
-pub fn addhlrr(state: &mut GBState, rr: u8) -> u64 {
- let n = state.cpu.r16(rr);
- let hl = state.cpu.r16(reg::HL);
+ self.cpu.r[reg::F as usize] &= !(flag::N | flag::H | flag::CY);
- state.cpu.w16(reg::HL, (hl as i32 + n as i32) as u16);
+ if (hl & 0xff) as i32 + n as i32 & !0xff != 0 {
+ self.cpu.r[reg::F as usize] |= flag::H;
+ }
- state.cpu.r[reg::F as usize] &= !(flag::N | flag::H | flag::CY);
+ if (hl as i32 + n as i32) & !0xffff != 0 {
+ self.cpu.r[reg::F as usize] |= flag::CY;
+ }
- if (hl & 0xff) as i32 + n as i32 & !0xff != 0 {
- state.cpu.r[reg::F as usize] |= flag::H;
+ 8
}
- if (hl as i32 + n as i32) & !0xffff != 0 {
- state.cpu.r[reg::F as usize] |= flag::CY;
- }
+ fn adc(&mut self, x: u8) {
+ // ADD a number and the carry flag to A and store the result in A
+ let carry = (self.cpu.r[reg::F as usize] & flag::CY) >> 4;
+ let res = x as u16 + self.cpu.r[reg::A as usize] as u16 + carry as u16;
- 8
-}
+ self.cpu.r[reg::F as usize] = 0;
-pub fn adc(state: &mut GBState, x: u8) {
- // ADD a number and the carry flag to A and store the result in A
- let carry = (state.cpu.r[reg::F as usize] & flag::CY) >> 4;
- let res = x as u16 + state.cpu.r[reg::A as usize] as u16 + carry as u16;
+ if (x & 0xf) + ((self.cpu.r[reg::A as usize] & 0xf) + carry) > 0xf {
+ self.cpu.r[reg::F as usize] |= flag::H;
+ }
- state.cpu.r[reg::F as usize] = 0;
+ if res > 0xff {
+ self.cpu.r[reg::F as usize] |= flag::CY;
+ }
- if (x & 0xf) + ((state.cpu.r[reg::A as usize] & 0xf) + carry) > 0xf {
- state.cpu.r[reg::F as usize] |= flag::H;
- }
+ self.cpu.r[reg::A as usize] = res as u8;
- if res > 0xff {
- state.cpu.r[reg::F as usize] |= flag::CY;
+ if self.cpu.r[reg::A as usize] == 0 {
+ self.cpu.r[reg::F as usize] |= flag::ZF;
+ }
}
- state.cpu.r[reg::A as usize] = res as u8;
+ fn sub(&mut self, x: u8) {
+ // SUB a number to A and store the result in A
+ self.cpu.r[reg::F as usize] = flag::N;
- if state.cpu.r[reg::A as usize] == 0 {
- state.cpu.r[reg::F as usize] |= flag::ZF;
- }
-}
+ if (x & 0xf) > (self.cpu.r[reg::A as usize] & 0xf) {
+ self.cpu.r[reg::F as usize] |= flag::H;
+ }
+
+ if x > self.cpu.r[reg::A as usize] {
+ self.cpu.r[reg::F as usize] |= flag::CY;
+ }
-pub fn sub(state: &mut GBState, x: u8) {
- // SUB a number to A and store the result in A
- state.cpu.r[reg::F as usize] = flag::N;
+ self.cpu.r[reg::A as usize] = self.cpu.r[reg::A as usize] - x;
- if (x & 0xf) > (state.cpu.r[reg::A as usize] & 0xf) {
- state.cpu.r[reg::F as usize] |= flag::H;
+ if self.cpu.r[reg::A as usize] == 0 {
+ self.cpu.r[reg::F as usize] |= flag::ZF;
+ }
}
- if x > state.cpu.r[reg::A as usize] {
- state.cpu.r[reg::F as usize] |= flag::CY;
- }
+ fn sbc(&mut self, x: u8) {
+ // SUB a number and the carry flag to A and store the result in A
+ let carry = (self.cpu.r[reg::F as usize] & flag::CY) >> 4;
+ self.cpu.r[reg::F as usize] = flag::N;
- state.cpu.r[reg::A as usize] = state.cpu.r[reg::A as usize] - x;
+ if (x & 0xf) > (self.cpu.r[reg::A as usize] & 0xf) - carry {
+ self.cpu.r[reg::F as usize] |= flag::H;
+ }
- if state.cpu.r[reg::A as usize] == 0 {
- state.cpu.r[reg::F as usize] |= flag::ZF;
- }
-}
+ if x as i32 > self.cpu.r[reg::A as usize] as i32 - carry as i32 {
+ self.cpu.r[reg::F as usize] |= flag::CY;
+ }
-pub fn sbc(state: &mut GBState, x: u8) {
- // SUB a number and the carry flag to A and store the result in A
- let carry = (state.cpu.r[reg::F as usize] & flag::CY) >> 4;
- state.cpu.r[reg::F as usize] = flag::N;
+ self.cpu.r[reg::A as usize] = self.cpu.r[reg::A as usize] - x - carry;
- if (x & 0xf) > (state.cpu.r[reg::A as usize] & 0xf) - carry {
- state.cpu.r[reg::F as usize] |= flag::H;
+ if self.cpu.r[reg::A as usize] == 0 {
+ self.cpu.r[reg::F as usize] |= flag::ZF;
+ }
}
- if x as i32 > state.cpu.r[reg::A as usize] as i32 - carry as i32 {
- state.cpu.r[reg::F as usize] |= flag::CY;
- }
+ fn and(&mut self, x: u8) {
+ // AND a number to A and store the result in A
+ self.cpu.r[reg::A as usize] &= x;
- state.cpu.r[reg::A as usize] = state.cpu.r[reg::A as usize] - x - carry;
+ self.cpu.r[reg::F as usize] = flag::H;
- if state.cpu.r[reg::A as usize] == 0 {
- state.cpu.r[reg::F as usize] |= flag::ZF;
+ if self.cpu.r[reg::A as usize] == 0 {
+ self.cpu.r[reg::F as usize] |= flag::ZF;
+ }
}
-}
-pub fn and(state: &mut GBState, x: u8) {
- // AND a number to A and store the result in A
- state.cpu.r[reg::A as usize] &= x;
+ fn xor(&mut self, x: u8) {
+ // XOR a number to A and store the result in A
+ self.cpu.r[reg::A as usize] ^= x;
- state.cpu.r[reg::F as usize] = flag::H;
+ self.cpu.r[reg::F as usize] = 0;
- if state.cpu.r[reg::A as usize] == 0 {
- state.cpu.r[reg::F as usize] |= flag::ZF;
+ if self.cpu.r[reg::A as usize] == 0 {
+ self.cpu.r[reg::F as usize] |= flag::ZF;
+ }
}
-}
-pub fn xor(state: &mut GBState, x: u8) {
- // XOR a number to A and store the result in A
- state.cpu.r[reg::A as usize] ^= x;
+ fn or(&mut self, x: u8) {
+ // OR a number to A and store the result in A
+ self.cpu.r[reg::A as usize] |= x;
- state.cpu.r[reg::F as usize] = 0;
+ self.cpu.r[reg::F as usize] = 0;
- if state.cpu.r[reg::A as usize] == 0 {
- state.cpu.r[reg::F as usize] |= flag::ZF;
+ if self.cpu.r[reg::A as usize] == 0 {
+ self.cpu.r[reg::F as usize] |= flag::ZF;
+ }
}
-}
-pub fn or(state: &mut GBState, x: u8) {
- // OR a number to A and store the result in A
- state.cpu.r[reg::A as usize] |= x;
+ fn cp(&mut self, x: u8) {
+ // SUB a number to A and update the flags accordingly without updating A
+ self.cpu.r[reg::F as usize] &= !(flag::H | flag::N | flag::ZF | flag::CY);
- state.cpu.r[reg::F as usize] = 0;
+ self.cpu.r[reg::F as usize] |= flag::N;
- if state.cpu.r[reg::A as usize] == 0 {
- state.cpu.r[reg::F as usize] |= flag::ZF;
- }
-}
+ if x & 0xf > self.cpu.r[reg::A as usize] & 0xf {
+ self.cpu.r[reg::F as usize] |= flag::H;
+ }
-pub fn cp(state: &mut GBState, x: u8) {
- // SUB a number to A and update the flags accordingly without updating A
- state.cpu.r[reg::F as usize] &= !(flag::H | flag::N | flag::ZF | flag::CY);
+ if x > self.cpu.r[reg::A as usize] {
+ self.cpu.r[reg::F as usize] |= flag::CY;
+ }
- state.cpu.r[reg::F as usize] |= flag::N;
+ let res = self.cpu.r[reg::A as usize] - x;
- if x & 0xf > state.cpu.r[reg::A as usize] & 0xf {
- state.cpu.r[reg::F as usize] |= flag::H;
+ if res == 0 {
+ self.cpu.r[reg::F as usize] |= flag::ZF;
+ }
}
- if x > state.cpu.r[reg::A as usize] {
- state.cpu.r[reg::F as usize] |= flag::CY;
+ fn rlc(&mut self, r_i: u8) -> Result<(), MemError> {
+ // ROTATE LEFT the input register
+ let mut n = self.r_reg(r_i)?;
+ self.cpu.r[reg::F as usize] &= !(flag::H | flag::N | flag::ZF | flag::CY);
+ self.cpu.r[reg::F as usize] |= (n >> 7) << 4;
+ n <<= 1;
+ n |= (self.cpu.r[reg::F as usize] & flag::CY) >> 4;
+ self.w_reg(r_i, n)
}
- let res = state.cpu.r[reg::A as usize] - x;
-
- if res == 0 {
- state.cpu.r[reg::F as usize] |= flag::ZF;
+ fn rrc(&mut self, r_i: u8) -> Result<(), MemError> {
+ // ROTATE RIGHT the input register
+ let mut n = self.r_reg(r_i)?;
+ self.cpu.r[reg::F as usize] &= !(flag::H | flag::N | flag::ZF | flag::CY);
+ self.cpu.r[reg::F as usize] |= (n & 1) << 4;
+ n >>= 1;
+ n |= ((self.cpu.r[reg::F as usize] & flag::CY) >> 4) << 7;
+ self.w_reg(r_i, n)
}
-}
-pub fn rlc(state: &mut GBState, r_i: u8) -> Result<(), MemError> {
- // ROTATE LEFT the input register
- let mut n = state.r_reg(r_i)?;
- state.cpu.r[reg::F as usize] &= !(flag::H | flag::N | flag::ZF | flag::CY);
- state.cpu.r[reg::F as usize] |= (n >> 7) << 4;
- n <<= 1;
- n |= (state.cpu.r[reg::F as usize] & flag::CY) >> 4;
- state.w_reg(r_i, n)
-}
+ fn rl(&mut self, r_i: u8) -> Result<(), MemError> {
+ // ROTATE LEFT THROUGH CARRY the input register
+ // (RLC IS ROTATE AND RL IS ROTATE THROUGH CARRY ! IT DOESN'T MAKE ANY SENSE !!)
+ let mut n = self.r_reg(r_i)?;
+ let carry = (self.cpu.r[reg::F as usize] & flag::CY) >> 4;
-pub fn rrc(state: &mut GBState, r_i: u8) -> Result<(), MemError> {
- // ROTATE RIGHT the input register
- let mut n = state.r_reg(r_i)?;
- state.cpu.r[reg::F as usize] &= !(flag::H | flag::N | flag::ZF | flag::CY);
- state.cpu.r[reg::F as usize] |= (n & 1) << 4;
- n >>= 1;
- n |= ((state.cpu.r[reg::F as usize] & flag::CY) >> 4) << 7;
- state.w_reg(r_i, n)
-}
+ self.cpu.r[reg::F as usize] &= !(flag::H | flag::N | flag::ZF | flag::CY);
+ self.cpu.r[reg::F as usize] |= (n >> 7) << 4;
+ n <<= 1;
+ n |= carry;
+ self.w_reg(r_i, n)
+ }
-pub fn rl(state: &mut GBState, r_i: u8) -> Result<(), MemError> {
- // ROTATE LEFT THROUGH CARRY the input register
- // (RLC IS ROTATE AND RL IS ROTATE THROUGH CARRY ! IT DOESN'T MAKE ANY SENSE !!)
- let mut n = state.r_reg(r_i)?;
- let carry = (state.cpu.r[reg::F as usize] & flag::CY) >> 4;
-
- state.cpu.r[reg::F as usize] &= !(flag::H | flag::N | flag::ZF | flag::CY);
- state.cpu.r[reg::F as usize] |= (n >> 7) << 4;
- n <<= 1;
- n |= carry;
- state.w_reg(r_i, n)
-}
+ fn rr(&mut self, r_i: u8) -> Result<(), MemError> {
+ // ROTATE RIGHT THROUGH CARRY the input register
+ let mut n = self.r_reg(r_i)?;
+ let carry = (self.cpu.r[reg::F as usize] & flag::CY) >> 4;
-pub fn rr(state: &mut GBState, r_i: u8) -> Result<(), MemError> {
- // ROTATE RIGHT THROUGH CARRY the input register
- let mut n = state.r_reg(r_i)?;
- let carry = (state.cpu.r[reg::F as usize] & flag::CY) >> 4;
+ self.cpu.r[reg::F as usize] &= !(flag::H | flag::N | flag::ZF | flag::CY);
+ self.cpu.r[reg::F as usize] |= (n & 1) << 4;
+ n >>= 1;
+ n |= carry << 7;
+ self.w_reg(r_i, n)
+ }
- state.cpu.r[reg::F as usize] &= !(flag::H | flag::N | flag::ZF | flag::CY);
- state.cpu.r[reg::F as usize] |= (n & 1) << 4;
- n >>= 1;
- n |= carry << 7;
- state.w_reg(r_i, n)
-}
+ fn sla(&mut self, r_i: u8) -> Result<(), MemError> {
+ // Shift left Arithmetic (b0=0) the input register
+ let mut n = self.r_reg(r_i)?;
-pub fn sla(state: &mut GBState, r_i: u8) -> Result<(), MemError> {
- // Shift left Arithmetic (b0=0) the input register
- let mut n = state.r_reg(r_i)?;
+ self.cpu.r[reg::F as usize] &= !(flag::H | flag::N | flag::ZF | flag::CY);
+ self.cpu.r[reg::F as usize] |= (n >> 7) << 4;
+ n <<= 1;
- state.cpu.r[reg::F as usize] &= !(flag::H | flag::N | flag::ZF | flag::CY);
- state.cpu.r[reg::F as usize] |= (n >> 7) << 4;
- n <<= 1;
+ if n == 0 {
+ self.cpu.r[reg::F as usize] |= flag::ZF;
+ }
- if n == 0 {
- state.cpu.r[reg::F as usize] |= flag::ZF;
+ self.w_reg(r_i, n)
}
- state.w_reg(r_i, n)
-}
+ fn sra(&mut self, r_i: u8) -> Result<(), MemError> {
+ // Shift right Arithmetic (b7=b7) the input register
+ let mut n = self.r_reg(r_i)?;
-pub fn sra(state: &mut GBState, r_i: u8) -> Result<(), MemError> {
- // Shift right Arithmetic (b7=b7) the input register
- let mut n = state.r_reg(r_i)?;
+ self.cpu.r[reg::F as usize] &= !(flag::H | flag::N | flag::ZF | flag::CY);
+ self.cpu.r[reg::F as usize] |= (n & 0b1) << 4;
+ let b7 = n & 0b10000000;
+ n >>= 1;
+ n |= b7;
- state.cpu.r[reg::F as usize] &= !(flag::H | flag::N | flag::ZF | flag::CY);
- state.cpu.r[reg::F as usize] |= (n & 0b1) << 4;
- let b7 = n & 0b10000000;
- n >>= 1;
- n |= b7;
+ if n == 0 {
+ self.cpu.r[reg::F as usize] |= flag::ZF;
+ }
- if n == 0 {
- state.cpu.r[reg::F as usize] |= flag::ZF;
+ self.w_reg(r_i, n)
}
- state.w_reg(r_i, n)
-}
+ fn swap(&mut self, r_i: u8) -> Result<(), MemError> {
+ // Swap the high nibble and low nibble
+ let mut n = self.r_reg(r_i)?;
-pub fn swap(state: &mut GBState, r_i: u8) -> Result<(), MemError> {
- // Swap the high nibble and low nibble
- let mut n = state.r_reg(r_i)?;
+ let nibble_low = n & 0b1111;
+ let nibble_high = n >> 4;
- let nibble_low = n & 0b1111;
- let nibble_high = n >> 4;
+ self.cpu.r[reg::F as usize] &= !(flag::H | flag::N | flag::ZF | flag::CY);
- state.cpu.r[reg::F as usize] &= !(flag::H | flag::N | flag::ZF | flag::CY);
+ n = nibble_high | (nibble_low << 4);
- n = nibble_high | (nibble_low << 4);
+ if n == 0 {
+ self.cpu.r[reg::F as usize] |= flag::ZF;
+ }
- if n == 0 {
- state.cpu.r[reg::F as usize] |= flag::ZF;
+ self.w_reg(r_i, n)
}
- state.w_reg(r_i, n)
-}
+ fn srl(&mut self, r_i: u8) -> Result<(), MemError> {
+ // Shift right Logical (b7=0) the input register
+ let mut n = self.r_reg(r_i)?;
-pub fn srl(state: &mut GBState, r_i: u8) -> Result<(), MemError> {
- // Shift right Logical (b7=0) the input register
- let mut n = state.r_reg(r_i)?;
+ self.cpu.r[reg::F as usize] &= !(flag::H | flag::N | flag::ZF | flag::CY);
+ self.cpu.r[reg::F as usize] |= (n & 0b1) << 4;
+ n >>= 1;
- state.cpu.r[reg::F as usize] &= !(flag::H | flag::N | flag::ZF | flag::CY);
- state.cpu.r[reg::F as usize] |= (n & 0b1) << 4;
- n >>= 1;
+ if n == 0 {
+ self.cpu.r[reg::F as usize] |= flag::ZF;
+ }
- if n == 0 {
- state.cpu.r[reg::F as usize] |= flag::ZF;
+ self.w_reg(r_i, n)
}
- state.w_reg(r_i, n)
-}
+ fn bit(&mut self, n1: u8, n2: u8) -> Result<(), MemError> {
+ let z = (((self.r_reg(n2)? >> n1) & 1) ^ 1) << 7;
-pub fn bit(state: &mut GBState, n1: u8, n2: u8) -> Result<(), MemError> {
- let z = (((state.r_reg(n2)? >> n1) & 1) ^ 1) << 7;
+ self.cpu.r[reg::F as usize] &= !(flag::N | flag::ZF);
+ self.cpu.r[reg::F as usize] |= flag::H | z;
+ Ok(())
+ }
- state.cpu.r[reg::F as usize] &= !(flag::N | flag::ZF);
- state.cpu.r[reg::F as usize] |= flag::H | z;
- Ok(())
-}
+ fn set(&mut self, n1: u8, n2: u8) -> Result<(), MemError> {
+ self.w_reg(n2, self.r_reg(n2)? | (1 << n1))
+ }
-pub fn set(state: &mut GBState, n1: u8, n2: u8) -> Result<(), MemError> {
- state.w_reg(n2, state.r_reg(n2)? | (1 << n1))
-}
+ fn res(&mut self, n1: u8, n2: u8) -> Result<(), MemError> {
+ self.w_reg(n2, self.r_reg(n2)? & !(1 << n1))
+ }
-pub fn res(state: &mut GBState, n1: u8, n2: u8) -> Result<(), MemError> {
- state.w_reg(n2, state.r_reg(n2)? & !(1 << n1))
-}
+ // I don't remember why I separated op00, op01, op10 and op11 AND I'M NOT GOING TO CHANGE IT
+ // BECAUSE I LOVE CHAOS
-// I don't remember why I separated op00, op01, op10 and op11 AND I'M NOT GOING TO CHANGE IT
-// BECAUSE I LOVE CHAOS
-
-pub fn op00(state: &mut GBState, n1: u8, n2: u8) -> Result<u64, MemError> {
- // Dispatcher for the instructions starting with 0b00 based on their 3 LSB
- match n2 {
- 0b000 => match n1 {
- 0b000 => Ok(4),
- 0b001 => ldnnsp(state),
- 0b010 => todo!("STOP"),
- 0b011 => jr8(state),
- _ => jrcc8(state, n1),
- },
- 0b001 => match n1 {
- 0b001 | 0b011 | 0b101 | 0b111 => Ok(addhlrr(state, n1 >> 1)),
- 0b000 | 0b010 | 0b100 | 0b110 => {
- let p = r_16b_from_pc(state)?;
- ldrr16(state, n1 >> 1, p);
- Ok(12)
+ fn op00(&mut self, n1: u8, n2: u8) -> Result<u64, MemError> {
+ // Dispatcher for the instructions starting with 0b00 based on their 3 LSB
+ match n2 {
+ 0b000 => match n1 {
+ 0b000 => Ok(4),
+ 0b001 => self.ldnnsp(),
+ 0b010 => todo!("STOP"),
+ 0b011 => self.jr8(),
+ _ => self.jrcc8(n1),
+ },
+ 0b001 => match n1 {
+ 0b001 | 0b011 | 0b101 | 0b111 => Ok(self.addhlrr(n1 >> 1)),
+ 0b000 | 0b010 | 0b100 | 0b110 => {
+ let p = self.r_16b_from_pc()?;
+ self.ldrr16(n1 >> 1, p);
+ Ok(12)
+ }
+ _ => panic!(),
+ },
+ 0b010 => self.ld00a(n1),
+ 0b011 => match n1 {
+ 0b001 | 0b011 | 0b101 | 0b111 => Ok(self.dec16(n1 >> 1)),
+ 0b000 | 0b010 | 0b100 | 0b110 => Ok(self.inc16(n1 >> 1)),
+ _ => panic!(),
+ },
+ 0b100 => self.inc8(n1),
+ 0b101 => self.dec8(n1),
+ 0b110 => self.ldr8(n1),
+ 0b111 => {
+ match n1 {
+ 0b000 => self.rlc(7)?,
+ 0b001 => self.rrc(7)?,
+ 0b010 => self.rl(7)?,
+ 0b011 => self.rr(7)?,
+ 0b100 => self.daa(),
+ 0b101 => self.cpl(),
+ 0b110 => self.scf(),
+ 0b111 => self.ccf(),
+ _ => panic!(),
+ };
+ Ok(4)
}
_ => panic!(),
- },
- 0b010 => ld00a(state, n1),
- 0b011 => match n1 {
- 0b001 | 0b011 | 0b101 | 0b111 => Ok(dec16(state, n1 >> 1)),
- 0b000 | 0b010 | 0b100 | 0b110 => Ok(inc16(state, n1 >> 1)),
- _ => panic!(),
- },
- 0b100 => inc8(state, n1),
- 0b101 => dec8(state, n1),
- 0b110 => ldr8(state, n1),
- 0b111 => {
- match n1 {
- 0b000 => rlc(state, 7)?,
- 0b001 => rrc(state, 7)?,
- 0b010 => rl(state, 7)?,
- 0b011 => rr(state, 7)?,
- 0b100 => daa(state),
- 0b101 => cpl(state),
- 0b110 => scf(state),
- 0b111 => ccf(state),
- _ => panic!(),
- };
+ }
+ }
+
+ fn op01(&mut self, n1: u8, n2: u8) -> Result<u64, MemError> {
+ // Dispatcher for the instructions starting with 0b01 (LD r,r and HALT)
+ if n1 == 0b110 && n2 == 0b110 {
+ self.mem.halt = true;
Ok(4)
+ } else {
+ self.ldrr(n1, n2)?;
+
+ if n1 == 0b110 || n2 == 0b110 {
+ Ok(8)
+ } else {
+ Ok(4)
+ }
}
- _ => panic!(),
}
-}
-pub fn op01(state: &mut GBState, n1: u8, n2: u8) -> Result<u64, MemError> {
- // Dispatcher for the instructions starting with 0b01 (LD r,r and HALT)
- if n1 == 0b110 && n2 == 0b110 {
- state.mem.halt = true;
- Ok(4)
- } else {
- ldrr(state, n1, n2)?;
+ fn op10(&mut self, n1: u8, n2: u8) -> Result<u64, MemError> {
+ // Dispatcher for the instructions starting with 0b10 (Arithmetic)
+ match n1 {
+ 0b000 => self.add(self.r_reg(n2)?),
+ 0b001 => self.adc(self.r_reg(n2)?),
+ 0b010 => self.sub(self.r_reg(n2)?),
+ 0b011 => self.sbc(self.r_reg(n2)?),
+ 0b100 => self.and(self.r_reg(n2)?),
+ 0b101 => self.xor(self.r_reg(n2)?),
+ 0b110 => self.or(self.r_reg(n2)?),
+ 0b111 => self.cp(self.r_reg(n2)?),
+ _ => panic!(),
+ }
- if n1 == 0b110 || n2 == 0b110 {
+ if n2 == 0b110 {
Ok(8)
} else {
Ok(4)
}
}
-}
-pub fn op10(state: &mut GBState, n1: u8, n2: u8) -> Result<u64, MemError> {
- // Dispatcher for the instructions starting with 0b10 (Arithmetic)
- match n1 {
- 0b000 => add(state, state.r_reg(n2)?),
- 0b001 => adc(state, state.r_reg(n2)?),
- 0b010 => sub(state, state.r_reg(n2)?),
- 0b011 => sbc(state, state.r_reg(n2)?),
- 0b100 => and(state, state.r_reg(n2)?),
- 0b101 => xor(state, state.r_reg(n2)?),
- 0b110 => or(state, state.r_reg(n2)?),
- 0b111 => cp(state, state.r_reg(n2)?),
- _ => panic!(),
- }
-
- if n2 == 0b110 {
- Ok(8)
- } else {
- Ok(4)
- }
-}
-
-pub fn op11(state: &mut GBState, n1: u8, n2: u8) -> Result<u64, MemError> {
- match n2 {
- 0b000 => match n1 {
- 0b100 => {
- let n = r_8b_from_pc(state)?;
- ldnna(state, n as u16 | 0xff00)?;
- Ok(12)
- }
- 0b101 => addsp8(state),
- 0b110 => {
- let n = r_8b_from_pc(state)?;
- ldann(state, n as u16 | 0xff00)?;
- Ok(12)
- }
- 0b111 => {
- let n = r_8b_from_pc(state)?;
- ldrr16(state, reg::HL, n as u16 + state.cpu.sp);
- Ok(12)
- }
- _ => retcc(state, n1 & 0b11),
- },
- 0b001 => match n1 {
- 0b001 => ret(state),
- 0b011 => {
- state.mem.ime = true;
-
- ret(state)
- }
- 0b101 => Ok(jphl(state)),
- 0b111 => Ok(ldsphl(state)),
- _ => {
- let p = pop(state)?;
- state.cpu.r[(n1 >> 1) as usize * 2 + 1] = (p & 0xff) as u8;
- state.cpu.r[(n1 >> 1) as usize * 2] = (p >> 8) as u8;
- Ok(12)
- }
- },
- 0b010 => match n1 {
- 0b100 => {
- ldnna(state, state.cpu.r[reg::C as usize] as u16 | 0xff00)?;
- Ok(8)
- }
- 0b101 => {
- let nn = r_16b_from_pc(state)?;
- ldnna(state, nn)?;
- Ok(16)
- }
+ fn op11(&mut self, n1: u8, n2: u8) -> Result<u64, MemError> {
+ match n2 {
+ 0b000 => match n1 {
+ 0b100 => {
+ let n = self.r_8b_from_pc()?;
+ self.ldnna(n as u16 | 0xff00)?;
+ Ok(12)
+ }
+ 0b101 => self.addsp8(),
+ 0b110 => {
+ let n = self.r_8b_from_pc()?;
+ self.ldann(n as u16 | 0xff00)?;
+ Ok(12)
+ }
+ 0b111 => {
+ let n = self.r_8b_from_pc()?;
+ self.ldrr16(reg::HL, n as u16 + self.cpu.sp);
+ Ok(12)
+ }
+ _ => self.retcc(n1 & 0b11),
+ },
+ 0b001 => match n1 {
+ 0b001 => self.ret(),
+ 0b011 => {
+ self.mem.ime = true;
+
+ self.ret()
+ }
+ 0b101 => Ok(self.jphl()),
+ 0b111 => Ok(self.ldsphl()),
+ _ => {
+ let p = self.pop()?;
+ self.cpu.r[(n1 >> 1) as usize * 2 + 1] = (p & 0xff) as u8;
+ self.cpu.r[(n1 >> 1) as usize * 2] = (p >> 8) as u8;
+ Ok(12)
+ }
+ },
+ 0b010 => match n1 {
+ 0b100 => {
+ self.ldnna(self.cpu.r[reg::C as usize] as u16 | 0xff00)?;
+ Ok(8)
+ }
+ 0b101 => {
+ let nn = self.r_16b_from_pc()?;
+ self.ldnna(nn)?;
+ Ok(16)
+ }
+ 0b110 => {
+ self.ldann(self.cpu.r[reg::C as usize] as u16 | 0xff00)?;
+ Ok(8)
+ }
+ 0b111 => {
+ let nn = self.r_16b_from_pc()?;
+ self.ldann(nn)?;
+ Ok(16)
+ }
+ _ => self.jpcc16(n1 & 0b11),
+ },
+ 0b011 => match n1 {
+ 0b000 => self.jp16(),
+ 0b001 => self.op_bitwise(), // Bitwise operations
+ 0b010 | 0b011 | 0b100 | 0b101 => unimplemented!(),
+ 0b110 => {
+ self.mem.ime = false;
+ Ok(4)
+ }
+ 0b111 => {
+ self.mem.ime = true;
+ Ok(4)
+ }
+ _ => panic!(),
+ },
+ 0b100 => self.callcc(n1 & 0b11),
+ 0b101 => match n1 {
+ 0b001 => self.call(),
+ 0b011 | 0b101 | 0b111 => unimplemented!(),
+ _ => {
+ let value = self.cpu.r[(n1 >> 1) as usize * 2 + 1] as u16
+ | ((self.cpu.r[(n1 >> 1) as usize * 2] as u16) << 8);
+ self.push(value)?;
+ Ok(16)
+ }
+ },
0b110 => {
- ldann(state, state.cpu.r[reg::C as usize] as u16 | 0xff00)?;
+ let p = self.r_8b_from_pc()?;
+
+ match n1 {
+ 0b000 => self.add(p),
+ 0b001 => self.adc(p),
+ 0b010 => self.sub(p),
+ 0b011 => self.sbc(p),
+ 0b100 => self.and(p),
+ 0b101 => self.xor(p),
+ 0b110 => self.or(p),
+ 0b111 => self.cp(p),
+ _ => panic!(),
+ }
Ok(8)
}
0b111 => {
- let nn = r_16b_from_pc(state)?;
- ldann(state, nn)?;
+ let p = n1 << 3;
+
+ self.push(self.cpu.pc)?;
+ self.cpu.pc = p as u16;
Ok(16)
- }
- _ => jpcc16(state, n1 & 0b11),
- },
- 0b011 => match n1 {
- 0b000 => jp16(state),
- 0b001 => op_bitwise(state), // Bitwise operations
- 0b010 | 0b011 | 0b100 | 0b101 => unimplemented!(),
- 0b110 => {
- state.mem.ime = false;
- Ok(4)
- }
- 0b111 => {
- state.mem.ime = true;
- Ok(4)
- }
+ } // RST
_ => panic!(),
- },
- 0b100 => callcc(state, n1 & 0b11),
- 0b101 => match n1 {
- 0b001 => call(state),
- 0b011 | 0b101 | 0b111 => unimplemented!(),
- _ => {
- let value = state.cpu.r[(n1 >> 1) as usize * 2 + 1] as u16
- | ((state.cpu.r[(n1 >> 1) as usize * 2] as u16) << 8);
- push(state, value)?;
- Ok(16)
- }
- },
- 0b110 => {
- let p = r_8b_from_pc(state)?;
-
- match n1 {
- 0b000 => add(state, p),
- 0b001 => adc(state, p),
- 0b010 => sub(state, p),
- 0b011 => sbc(state, p),
- 0b100 => and(state, p),
- 0b101 => xor(state, p),
- 0b110 => or(state, p),
- 0b111 => cp(state, p),
- _ => panic!(),
- }
- Ok(8)
}
- 0b111 => {
- let p = n1 << 3;
+ }
- push(state, state.cpu.pc)?;
- state.cpu.pc = p as u16;
+ fn op_bitwise(&mut self) -> Result<u64, MemError> {
+ let p = self.r_8b_from_pc()?;
+ let opcode = p >> 6;
+ let n1 = p >> 3 & 0b111;
+ let n2 = p & 0b111;
+
+ match opcode {
+ 0b00 => match n1 {
+ 0b000 => self.rlc(n2),
+ 0b001 => self.rrc(n2),
+ 0b010 => self.rl(n2),
+ 0b011 => self.rr(n2),
+ 0b100 => self.sla(n2),
+ 0b101 => self.sra(n2),
+ 0b110 => self.swap(n2),
+ 0b111 => self.srl(n2),
+ _ => panic!(),
+ },
+ 0b01 => self.bit(n1, n2),
+ 0b10 => self.res(n1, n2),
+ 0b11 => self.set(n1, n2),
+ _ => panic!(),
+ }?;
+ if n2 == 0b110 {
Ok(16)
- } // RST
- _ => panic!(),
+ } else {
+ Ok(8)
+ }
}
-}
-pub fn op_bitwise(state: &mut GBState) -> Result<u64, MemError> {
- let p = r_8b_from_pc(state)?;
- let opcode = p >> 6;
- let n1 = p >> 3 & 0b111;
- let n2 = p & 0b111;
-
- match opcode {
- 0b00 => match n1 {
- 0b000 => rlc(state, n2),
- 0b001 => rrc(state, n2),
- 0b010 => rl(state, n2),
- 0b011 => rr(state, n2),
- 0b100 => sla(state, n2),
- 0b101 => sra(state, n2),
- 0b110 => swap(state, n2),
- 0b111 => srl(state, n2),
+ pub fn exec_opcode(&mut self) -> Result<u64, MemError> {
+ let opcode = self.mem.r(self.cpu.pc)?;
+
+ if self.is_debug {
+ println!(
+ "{:02x}:{:04x} = {:02x} (IME: {})",
+ self.mem.rom_bank, self.cpu.pc, opcode, self.mem.ime
+ );
+ }
+
+ self.cpu.pc += 1;
+
+ let n1 = (opcode >> 3) & 0b111;
+ let n2 = opcode & 0b111;
+
+ match opcode >> 6 {
+ 0b00 => self.op00(n1, n2),
+ 0b01 => self.op01(n1, n2),
+ 0b10 => self.op10(n1, n2),
+ 0b11 => self.op11(n1, n2),
_ => panic!(),
- },
- 0b01 => bit(state, n1, n2),
- 0b10 => res(state, n1, n2),
- 0b11 => set(state, n1, n2),
- _ => panic!(),
- }?;
- if n2 == 0b110 {
- Ok(16)
- } else {
- Ok(8)
+ }
}
}
diff --git a/src/serial.rs b/src/serial.rs
new file mode 100644
index 0000000..577723d
--- /dev/null
+++ b/src/serial.rs
@@ -0,0 +1,136 @@
+use std::fs::File;
+use std::io::{Read, Write};
+use std::sync::mpsc::{self, Receiver, Sender};
+use std::sync::{Arc, Mutex};
+use std::thread;
+
+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 struct UnconnectedSerial {}
+
+impl Serial for UnconnectedSerial {
+ fn write(&mut self, byte: u8) {
+ println!("Writing {} to unconnected serial", byte);
+ }
+
+ fn read(&mut self) -> u8 {
+ println!("Reading 0 from unconnected serial");
+ 0
+ }
+
+ fn new_transfer(&mut self) -> bool {
+ false
+ }
+
+ fn clock_master(&mut self) -> bool {
+ false
+ }
+
+ fn set_clock_master(&mut self, clock_master: bool) {}
+}
+
+pub struct FIFOPackage {
+ t: bool,
+ value: u8,
+}
+
+pub struct FIFOSerial {
+ input: Receiver<u8>,
+ output: Sender<FIFOPackage>,
+ clock_change: Receiver<bool>,
+ last_read_byte: u8,
+ clock_master: bool,
+}
+
+impl FIFOSerial {
+ pub fn new(input_path: String, output_path: String) -> FIFOSerial {
+ let (tx, input) = mpsc::channel::<u8>();
+ let (clock_tx, clock_change) = mpsc::channel::<bool>();
+ thread::spawn(move || {
+ let mut input_f = File::open(input_path).unwrap();
+ loop {
+ let mut byte = [0, 0];
+
+ input_f.read(&mut byte).unwrap();
+ if byte[0] == 1 {
+ tx.send(byte[1]).unwrap();
+ } else {
+ clock_tx.send(byte[1] == 0).unwrap();
+ }
+ }
+ });
+
+ let (output, rx) = mpsc::channel::<FIFOPackage>();
+ thread::spawn(move || {
+ let mut output_f = File::create(output_path).unwrap();
+ for b in rx.iter() {
+ if b.t {
+ output_f.write(&[1, b.value]).unwrap();
+ } else {
+ output_f.write(&[0, b.value]).unwrap();
+ }
+ }
+ });
+
+ FIFOSerial {
+ input,
+ output,
+ clock_change,
+ last_read_byte: 0xff,
+ clock_master: false,
+ }
+ }
+}
+
+impl Serial for FIFOSerial {
+ fn write(&mut self, byte: u8) {
+ println!("Writing {} to fifo serial", byte);
+ self.output.send(FIFOPackage {
+ t: true,
+ value: byte,
+ });
+ }
+
+ fn read(&mut self) -> u8 {
+ println!("Reading {} from fifo serial", self.last_read_byte);
+ self.last_read_byte
+ }
+
+ fn new_transfer(&mut self) -> bool {
+ match self.input.try_recv() {
+ Ok(byte) => {
+ println!("Received: {}", byte);
+ self.last_read_byte = byte;
+ true
+ }
+ _ => false,
+ }
+ }
+ fn clock_master(&mut self) -> bool {
+ match self.clock_change.try_recv() {
+ Ok(byte) => {
+ println!("Received clock change, master: {}", byte);
+ self.clock_master = byte;
+ }
+ _ => {}
+ };
+ self.clock_master
+ }
+
+ fn set_clock_master(&mut self, clock_master: bool) {
+ self.clock_master = clock_master;
+ self.output.send(FIFOPackage {
+ t: false,
+ value: (if clock_master { 1 } else { 0 }),
+ });
+ }
+}
diff --git a/src/state.rs b/src/state.rs
index d2f93d9..49bea41 100644
--- a/src/state.rs
+++ b/src/state.rs
@@ -1,6 +1,7 @@
use crate::audio::Audio;
use crate::consts::{PROGRAM_START_ADDRESS, STACK_START_ADDRESS};
use crate::display::Display;
+use crate::serial::Serial;
use std::fs::File;
use std::io::{Read, Write};
@@ -127,6 +128,8 @@ pub struct Memory {
pub audio: Audio,
+ pub serial: Box<dyn Serial>,
+
pub ime: bool,
pub div: u8,
@@ -146,6 +149,10 @@ pub struct Memory {
pub timer_enabled: bool,
pub timer_speed: u8,
+
+ pub serial_data: u8,
+
+ pub serial_control: u8,
}
#[derive(Debug)]
@@ -155,8 +162,13 @@ pub enum MemError {
NotUsable,
}
+mod serial_control_flags {
+ pub const CLOCK_SELECT: u8 = 0b1;
+ pub const TRANSFER_ENABLE: u8 = 0b10000000;
+}
+
impl Memory {
- pub fn new() -> Self {
+ pub fn new(serial: Box<dyn Serial>) -> Self {
let mut display = Display::new();
display.cls();
@@ -190,6 +202,32 @@ impl Memory {
tma: 0,
timer_enabled: false,
timer_speed: 0,
+ serial,
+ serial_control: 0,
+ serial_data: 0,
+ }
+ }
+
+ pub fn update_serial(&mut self) {
+ if self.serial.new_transfer() {
+ println!("TRANSFER ENABLED");
+ self.serial_control |= serial_control_flags::TRANSFER_ENABLE;
+ }
+
+ if self.serial.clock_master() {
+ self.serial_control |= serial_control_flags::CLOCK_SELECT;
+ } else {
+ self.serial_control &= !serial_control_flags::CLOCK_SELECT;
+ }
+
+ if (self.serial_control & serial_control_flags::CLOCK_SELECT != 0
+ || self.serial.new_transfer())
+ && self.serial_control & serial_control_flags::TRANSFER_ENABLE != 0
+ {
+ self.serial.write(self.serial_data);
+ self.serial_data = self.serial.read();
+ self.serial_control &= !serial_control_flags::TRANSFER_ENABLE;
+ self.io[0x0f] |= 0b1000;
}
}
@@ -331,8 +369,8 @@ pub struct GBState {
}
impl GBState {
- pub fn new() -> Self {
- let mem = Memory::new();
+ pub fn new(serial: Box<dyn Serial>) -> Self {
+ let mem = Memory::new(serial);
Self {
cpu: CPU::new(),