From 465aadd54e61839fc8e91575ea46840ca8817e5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20L=C3=B6ffler?= Date: Sat, 28 Jan 2023 07:31:19 +0100 Subject: [PATCH] branching instructions that's all of the 8080 instruction set besides the I/O instructions god bless --- src/emulator/instructions/branch.rs | 52 +++++++++++++++++++++++++++ src/emulator/instructions/mod.rs | 1 + src/emulator/instructions/transfer.rs | 3 +- src/emulator/main.rs | 49 ++++++++++++++++++++++++- 4 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 src/emulator/instructions/branch.rs diff --git a/src/emulator/instructions/branch.rs b/src/emulator/instructions/branch.rs new file mode 100644 index 0000000..9bf60ca --- /dev/null +++ b/src/emulator/instructions/branch.rs @@ -0,0 +1,52 @@ +use crate::transfer::{pop, push}; +use crate::{get_register_pair, EmulatorState, Register}; + +/// Jump (set PC) to specified address +pub fn jump(address: u16, state: &mut EmulatorState) { + state.pc = address; +} + +/// Jump to a specified address only if `cond` is true +pub fn jump_cond(address: u16, cond: bool, state: &mut EmulatorState) { + if cond { + jump(address, state); + } +} + +/// Push the current PC to the stack and jump to the specified address +pub fn call(address: u16, state: &mut EmulatorState) { + push(state.pc, state); + jump(address, state); +} + +// Push the current PC to the stack and jump to the specified address if `cond` is true +pub fn call_cond(address: u16, cond: bool, state: &mut EmulatorState) { + if cond { + call(address, state); + } +} + +// Pop a value from the stack and jump to it +pub fn ret(state: &mut EmulatorState) { + jump(pop(state), state); +} + +// Pop a value from the stack and jump to it if `cond` is true +pub fn ret_cond(cond: bool, state: &mut EmulatorState) { + if cond { + ret(state) + } +} + +/// "Restart" at (call) a specific interrupt vector +/// +/// Panics if `vector` is 8 or above +pub fn restart(vector: u8, state: &mut EmulatorState) { + assert!(vector < 8, "Illegal restart vector"); + call((vector * 8) as u16, state); +} + +/// Load a new program counter from the HL register pair +pub fn pchl(state: &mut EmulatorState) { + state.pc = get_register_pair(Register::H, state); +} diff --git a/src/emulator/instructions/mod.rs b/src/emulator/instructions/mod.rs index 5a67dae..9197ede 100644 --- a/src/emulator/instructions/mod.rs +++ b/src/emulator/instructions/mod.rs @@ -1,2 +1,3 @@ pub mod arithmetic; +pub mod branch; pub mod transfer; diff --git a/src/emulator/instructions/transfer.rs b/src/emulator/instructions/transfer.rs index 2c1f8a1..b297fd5 100644 --- a/src/emulator/instructions/transfer.rs +++ b/src/emulator/instructions/transfer.rs @@ -1,6 +1,5 @@ use crate::{ - get_register, get_register_pair, set_register, structs::set_register_pair, EmulatorState, - Register, + get_register, get_register_pair, set_register, set_register_pair, EmulatorState, Register, }; /// Move (copy) value from source to destination register diff --git a/src/emulator/main.rs b/src/emulator/main.rs index 001fff0..30adfca 100644 --- a/src/emulator/main.rs +++ b/src/emulator/main.rs @@ -1,4 +1,4 @@ -use instructions::{arithmetic, transfer}; +use instructions::{arithmetic, branch, transfer}; use std::{env, fs}; use crate::structs::*; @@ -164,6 +164,53 @@ fn tick(state: &mut EmulatorState) { 0xf6 => arithmetic::or(state.next_byte(), state), // ORI 0xfe => arithmetic::cmp(state.next_byte(), state), // CPI + /* Branch instructions */ + // Jumps + 0xc2 => branch::jump_cond(state.next_word(), !state.cc.z, state), // JNZ + 0xc3 => branch::jump(state.next_word(), state), // JMP + 0xca => branch::jump_cond(state.next_word(), state.cc.z, state), // JZ + 0xd2 => branch::jump_cond(state.next_word(), !state.cc.c, state), // JNC + 0xda => branch::jump_cond(state.next_word(), state.cc.c, state), // JC + 0xe2 => branch::jump_cond(state.next_word(), !state.cc.p, state), // JPO + 0xea => branch::jump_cond(state.next_word(), state.cc.p, state), // JPE + 0xf2 => branch::jump_cond(state.next_word(), !state.cc.s, state), // JP + 0xfa => branch::jump_cond(state.next_word(), state.cc.s, state), // JM + + // Calls + 0xc4 => branch::call_cond(state.next_word(), !state.cc.z, state), // CNZ + 0xcc => branch::call_cond(state.next_word(), state.cc.z, state), // CZ + 0xcd => branch::call(state.next_word(), state), // CALL + 0xd4 => branch::call_cond(state.next_word(), !state.cc.c, state), // CNC + 0xdc => branch::call_cond(state.next_word(), state.cc.c, state), // CC + 0xe4 => branch::call_cond(state.next_word(), !state.cc.p, state), // CPO + 0xec => branch::call_cond(state.next_word(), state.cc.p, state), // CPE + 0xf4 => branch::call_cond(state.next_word(), !state.cc.s, state), // CP + 0xfc => branch::call_cond(state.next_word(), state.cc.s, state), // CM + + // Returns + 0xc0 => branch::ret_cond(!state.cc.z, state), // RNZ + 0xc8 => branch::ret_cond(state.cc.z, state), // RZ + 0xc9 => branch::ret(state), // RET + 0xd0 => branch::ret_cond(!state.cc.c, state), // RNC + 0xd8 => branch::ret_cond(state.cc.c, state), // RC + 0xe0 => branch::ret_cond(!state.cc.p, state), // RPO + 0xe8 => branch::ret_cond(state.cc.p, state), // RPE + 0xf0 => branch::ret_cond(!state.cc.s, state), // RP + 0xf8 => branch::ret_cond(state.cc.s, state), // RM + + // Restarts + 0xc7 => branch::restart(0, state), + 0xcf => branch::restart(1, state), + 0xd7 => branch::restart(2, state), + 0xdf => branch::restart(3, state), + 0xe7 => branch::restart(4, state), + 0xef => branch::restart(5, state), + 0xf7 => branch::restart(6, state), + 0xff => branch::restart(7, state), + + // PCHL + 0xe9 => branch::pchl(state), + _ => not_implemented(state), }