summaryrefslogtreecommitdiff
path: root/src/cpu
diff options
context:
space:
mode:
Diffstat (limited to 'src/cpu')
-rw-r--r--src/cpu/alu_exit_exception.rs38
-rw-r--r--src/cpu/boot.rs70
-rw-r--r--src/cpu/continue.rs33
-rw-r--r--src/cpu/enter_exception.rs59
-rw-r--r--src/cpu/exchange.rs45
-rw-r--r--src/cpu/execute.rs69
-rw-r--r--src/cpu/exit_exception.rs42
-rw-r--r--src/cpu/fetch_arm.rs55
-rw-r--r--src/cpu/fetch_thumb.rs54
-rw-r--r--src/cpu/isa_adc.rs58
-rw-r--r--src/cpu/isa_add.rs55
-rw-r--r--src/cpu/isa_and.rs53
-rw-r--r--src/cpu/isa_b.rs38
-rw-r--r--src/cpu/isa_bic.rs53
-rw-r--r--src/cpu/isa_bl.rs62
-rw-r--r--src/cpu/isa_bx.rs45
-rw-r--r--src/cpu/isa_cmn.rs47
-rw-r--r--src/cpu/isa_cmp.rs47
-rw-r--r--src/cpu/isa_eor.rs53
-rw-r--r--src/cpu/isa_memory.rs122
-rw-r--r--src/cpu/isa_mov.rs53
-rw-r--r--src/cpu/isa_mul.rs52
-rw-r--r--src/cpu/isa_mvn.rs52
-rw-r--r--src/cpu/isa_orr.rs53
-rw-r--r--src/cpu/isa_rsb.rs55
-rw-r--r--src/cpu/isa_rsc.rs58
-rw-r--r--src/cpu/isa_sbc.rs58
-rw-r--r--src/cpu/isa_sub.rs55
-rw-r--r--src/cpu/isa_swi.rs34
-rw-r--r--src/cpu/isa_teq.rs45
-rw-r--r--src/cpu/isa_tst.rs45
-rw-r--r--src/cpu/sync_cycle.rs43
-rw-r--r--src/cpu/take_state.rs36
33 files changed, 1737 insertions, 0 deletions
diff --git a/src/cpu/alu_exit_exception.rs b/src/cpu/alu_exit_exception.rs
new file mode 100644
index 0000000..de9f238
--- /dev/null
+++ b/src/cpu/alu_exit_exception.rs
@@ -0,0 +1,38 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+macro_rules! alu_exit_exception {
+ ($self: ident, $state: ident, $rd: ident, $block: block) => {{
+ match $rd {
+ 0b1111 => {
+ drop($state);
+ $self.exit_exception();
+ },
+
+ _ => {
+ $block;
+ },
+ };
+ }};
+}
+pub(crate) use alu_exit_exception;
diff --git a/src/cpu/boot.rs b/src/cpu/boot.rs
new file mode 100644
index 0000000..5c38427
--- /dev/null
+++ b/src/cpu/boot.rs
@@ -0,0 +1,70 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::{log, log_status};
+use crate::cpu::Cpu;
+use crate::cpu_handle::CpuHandle;
+
+use std::sync::atomic::Ordering;
+use std::thread::{sleep, spawn};
+use std::time::{Duration, Instant};
+
+impl Cpu {
+ pub fn boot(self) -> CpuHandle {
+ eprintln!("starting emulation at {:#010X}", self.state.lock().unwrap().read_register(0xF).wrapping_sub(0x8));
+
+ let state = self.state.clone();
+ let dead = self.dead.clone();
+
+ let handle = spawn(move || { self.run() });
+
+ return CpuHandle::new(
+ state,
+ dead,
+
+ handle,
+ );
+ }
+
+ fn run(mut self) {
+ let run_timer = Instant::now();
+
+ 'main_loop: loop {
+ if self.dead.load(Ordering::Relaxed) { break 'main_loop };
+
+ let (instruction, predicate, cpsr) = self.fetch();
+ match predicate.test(cpsr) {
+ false => log_status!("skipping due to predicate ({})", predicate.code()),
+ true => self.execute(instruction),
+ };
+ self.r#continue();
+
+ if cfg!(debug_assertions) { sleep(Duration::from_millis(125)) };
+
+ self.cycle += 0x1;
+ }
+
+ let frequency = self.cycle as f64 / run_timer.elapsed().as_micros() as f64;
+ eprintln!("emulated {} cycle(s) ({frequency:.9} MHz)", self.cycle);
+ }
+}
diff --git a/src/cpu/continue.rs b/src/cpu/continue.rs
new file mode 100644
index 0000000..7b96713
--- /dev/null
+++ b/src/cpu/continue.rs
@@ -0,0 +1,33 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::cpu::{Cpu, take_state_mut};
+
+impl Cpu {
+ pub(super) fn r#continue(&mut self) {
+ take_state_mut!(state, self);
+
+ let pc = state.read_register(0xF).wrapping_add(self.instruction_size);
+ state.write_register(0xF, pc);
+ }
+}
diff --git a/src/cpu/enter_exception.rs b/src/cpu/enter_exception.rs
new file mode 100644
index 0000000..193b8cf
--- /dev/null
+++ b/src/cpu/enter_exception.rs
@@ -0,0 +1,59 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::cpu::{Cpu, exchange, take_state_mut};
+use crate::exception::Exception;
+
+impl Cpu {
+ pub(super) fn enter_exception(&mut self, exception: Exception) {
+ take_state_mut!(state, self);
+
+ let mode = exception.mode();
+
+ state.bank(mode);
+
+ let lr = state.read_register(0xF);
+ state.write_register(0xE, lr);
+
+ let mut cpsr = state.read_cpsr();
+ state.write_spsr(mode, cpsr);
+
+ cpsr &= 0b11111111111111111111111101000000;
+ cpsr |= 0b00000000000000000000000010000000;
+ cpsr |= mode as u32;
+
+ cpsr &= match exception {
+ | Exception::FastInterruptRequest
+ | Exception::Reset => 0b11111111111111111111111110111111,
+
+ _ => 0b11111111111111111111111111111111,
+ };
+
+ state.write_cpsr(cpsr);
+
+ exchange!(self, false);
+
+ let pc = exception.vector_address().wrapping_add(0x4);
+ state.write_register(0xF, pc);
+ }
+}
diff --git a/src/cpu/exchange.rs b/src/cpu/exchange.rs
new file mode 100644
index 0000000..1e74a75
--- /dev/null
+++ b/src/cpu/exchange.rs
@@ -0,0 +1,45 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+macro_rules! exchange {
+ ($cpu: ident, $t: expr) => {{
+ use crate::log_status;
+ use crate::cpu::Fetcher;
+
+ log_status!("exchanging to {}", match $t {
+ false => "ARM",
+ true => "Thumb",
+ });
+
+ const DATA: [(u32, Fetcher); 0x2] = [
+ (0x4, Cpu::fetch_arm),
+ (0x2, Cpu::fetch_thumb),
+ ];
+
+ let index = $t as usize & 0b1;
+
+ $cpu.instruction_size = unsafe { DATA.get_unchecked(index).0 };
+ $cpu.fetcher = unsafe { DATA.get_unchecked(index).1 };
+ }};
+}
+pub(crate) use exchange;
diff --git a/src/cpu/execute.rs b/src/cpu/execute.rs
new file mode 100644
index 0000000..7987f92
--- /dev/null
+++ b/src/cpu/execute.rs
@@ -0,0 +1,69 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::cpu::Cpu;
+use crate::instruction::Instruction;
+
+impl Cpu {
+ #[inline(always)]
+ pub(super) fn execute(&mut self, instruction: Instruction) {
+ use Instruction::*;
+
+ match instruction {
+ Adc(rd, rn, shifter, s) => self.isa_adc(rd, rn, shifter, s),
+ Add(rd, rn, shifter, s) => self.isa_add(rd, rn, shifter, s),
+ And(rd, rn, shifter, s) => self.isa_and(rd, rn, shifter, s),
+ B( imm) => self.isa_b( imm),
+ Bic(rd, rn, shifter, s) => self.isa_bic(rd, rn, shifter, s),
+ Bl( imm) => self.isa_bl( imm),
+ Bl0(imm) => self.isa_bl0(imm),
+ Bl1(imm) => self.isa_bl1(imm),
+ Bx( rm) => self.isa_bx( rm),
+ Cmn(rn, shifter) => self.isa_cmn(rn, shifter),
+ Cmp(rn, shifter) => self.isa_cmp(rn, shifter),
+ Eor(rd, rn, shifter, s) => self.isa_eor(rd, rn, shifter, s),
+ Mov(rd, shifter, s) => self.isa_mov(rd, shifter, s),
+ Mul(rd, rn, shifter, s) => self.isa_mul(rd, rn, shifter, s),
+ Mvn(rd, shifter, s) => self.isa_mvn(rd, shifter, s),
+ Orr(rd, rn, shifter, s) => self.isa_orr(rd, rn, shifter, s),
+ Rsb(rd, rn, shifter, s) => self.isa_rsb(rd, rn, shifter, s),
+ Rsc(rd, rn, shifter, s) => self.isa_rsc(rd, rn, shifter, s),
+ Sbc(rd, rn, shifter, s) => self.isa_sbc(rd, rn, shifter, s),
+ Sub(rd, rn, shifter, s) => self.isa_sub(rd, rn, shifter, s),
+ Swi(imm) => self.isa_swi(imm),
+ Teq(rn, shifter) => self.isa_teq(rn, shifter),
+ Tst(rn, shifter) => self.isa_tst(rn, shifter),
+
+ // Legacy:
+ LoadHalfword( rd, rn, imm) => self.isa_load_halfword( rd, rn, imm),
+ LoadImmediateOffset( rd, rn, imm) => self.isa_load_immediate_offset( rd, rn, imm),
+ LoadPc( rd, imm) => self.isa_load_pc( rd, imm),
+ StoreByteImmediateOffset(rd, rn, imm) => self.isa_store_byte_immediate_offset(rd, rn, imm),
+ StoreByteRegisterOffset( rd, rn, rm) => self.isa_store_byte_register_offset( rd, rn, rm),
+ StoreHalfword( rd, rn, imm) => self.isa_store_halfword( rd, rn, imm),
+ StoreImmediateOffset( rd, rn, imm) => self.isa_store_immediate_offset( rd, rn, imm),
+
+ Undefined => {},
+ };
+ }
+}
diff --git a/src/cpu/exit_exception.rs b/src/cpu/exit_exception.rs
new file mode 100644
index 0000000..fa47fad
--- /dev/null
+++ b/src/cpu/exit_exception.rs
@@ -0,0 +1,42 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::cpu::{Cpu, exchange, take_state_mut};
+use crate::cpu_mode::CpuMode;
+
+impl Cpu {
+ pub(super) fn exit_exception(&mut self) {
+ take_state_mut!(state, self);
+
+ let cpsr = state.read_cpsr();
+ let old_mode = CpuMode::from_raw((cpsr & 0b00000000000000000000000000011111) as u8);
+
+ let spsr = state.read_spsr(old_mode);
+ let new_mode = CpuMode::from_raw((spsr & 0b00000000000000000000000000011111) as u8);
+
+ let t = spsr & 0b00000000000000000000000000100000 != 0x0;
+ state.write_cpsr(spsr);
+ state.bank(new_mode);
+ exchange!(self, t);
+ }
+}
diff --git a/src/cpu/fetch_arm.rs b/src/cpu/fetch_arm.rs
new file mode 100644
index 0000000..453dbe2
--- /dev/null
+++ b/src/cpu/fetch_arm.rs
@@ -0,0 +1,55 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::{Error, log};
+use crate::cpu::{Cpu, take_state};
+use crate::instruction::Instruction;
+use crate::predicate::Predicate;
+
+impl Cpu {
+ #[must_use]
+ pub(super) fn fetch_arm(&mut self) -> (Instruction, Predicate, u32) {
+ take_state!(state, self);
+
+ let address = state.read_register(0xF).wrapping_sub(0x8);
+ let opcode = state.read_word(address);
+ let cpsr = state.read_cpsr();
+
+ drop(state);
+
+ log!();
+ log!("\u{1B}[1m{opcode:032b}\u{1B}[0m @ \u{1B}[1m{address:08X}\u{1B}[0m - ({})", self.cycle);
+
+ let (instruction, predicate) = Instruction::decode_arm(opcode);
+
+ match (instruction, predicate) {
+ | (Instruction::Undefined, _)
+ | (_, Predicate::Nv)
+ => Error::InvalidArmOpcode(address, opcode).trap(),
+
+ _ => {},
+ };
+
+ return (instruction, predicate, cpsr);
+ }
+}
diff --git a/src/cpu/fetch_thumb.rs b/src/cpu/fetch_thumb.rs
new file mode 100644
index 0000000..cee6ba7
--- /dev/null
+++ b/src/cpu/fetch_thumb.rs
@@ -0,0 +1,54 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::{Error, log};
+use crate::cpu::{Cpu, take_state};
+use crate::instruction::Instruction;
+use crate::predicate::Predicate;
+
+impl Cpu {
+ pub(super) fn fetch_thumb(&mut self) -> (Instruction, Predicate, u32) {
+ take_state!(state, self);
+
+ let address = state.read_register(0xF).wrapping_sub(0x4);
+ let opcode = state.read_halfword(address);
+ let cpsr = state.read_cpsr();
+
+ drop(state);
+
+ log!();
+ log!(" \u{1B}[1m{opcode:016b}\u{1B}[0m @ \u{1B}[1m{address:08X}\u{1B}[0m - ({})", self.cycle);
+
+ let (instruction, predicate) = Instruction::decode_thumb(opcode);
+
+ match (instruction, predicate) {
+ | (Instruction::Undefined, _)
+ | (_, Predicate::Nv)
+ => Error::InvalidThumbOpcode(address, opcode).trap(),
+
+ _ => {},
+ };
+
+ return (instruction, predicate, cpsr);
+ }
+}
diff --git a/src/cpu/isa_adc.rs b/src/cpu/isa_adc.rs
new file mode 100644
index 0000000..214d289
--- /dev/null
+++ b/src/cpu/isa_adc.rs
@@ -0,0 +1,58 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_adc(&mut self, rd: u8, rn: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, r{rn}, {shifter}", match s {
+ false => "adc",
+ true => "adcs",
+ });
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let c = (state.read_cpsr() & 0b00100000000000000000000000000000) >> 0x1D;
+
+ let (mut addend, _) = state.shifter_value(shifter);
+ addend = addend.wrapping_add(c);
+
+ let (result, c) = rn_value.overflowing_add(addend);
+ let (_, v) = (rn_value as i32).overflowing_add_unsigned(addend);
+ state.write_register(rd, result);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
+ cpsr |= (v as u32) << 0x1C;
+ cpsr |= (c as u32) << 0x1D;
+ cpsr |= ((result == 0x0) as u32) << 0x1E;
+ cpsr |= result & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_add.rs b/src/cpu/isa_add.rs
new file mode 100644
index 0000000..c099c97
--- /dev/null
+++ b/src/cpu/isa_add.rs
@@ -0,0 +1,55 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_add(&mut self, rd: u8, rn: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, r{rn}, {shifter}", match s {
+ false => "add",
+ true => "adds",
+ });
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (addend, _) = state.shifter_value(shifter);
+
+ let (result, c) = rn_value.overflowing_add(addend);
+ let (_, v) = (rn_value as i32).overflowing_add_unsigned(addend);
+ state.write_register(rd, result);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
+ cpsr |= (v as u32) << 0x1C;
+ cpsr |= (c as u32) << 0x1D;
+ cpsr |= ((result == 0x0) as u32) << 0x1E;
+ cpsr |= result & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_and.rs b/src/cpu/isa_and.rs
new file mode 100644
index 0000000..1148953
--- /dev/null
+++ b/src/cpu/isa_and.rs
@@ -0,0 +1,53 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_and(&mut self, rd: u8, rn: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, r{rn}, {shifter}", match s {
+ false => "and",
+ true => "ands",
+ });
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (mask, c) = state.shifter_value(shifter);
+
+ let result = rn_value & mask;
+ state.write_register(rd, result);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00011111111111111111111111111111;
+ cpsr |= (c as u32) << 0x1D;
+ cpsr |= ((result == 0x0) as u32) << 0x1E;
+ cpsr |= result & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_b.rs b/src/cpu/isa_b.rs
new file mode 100644
index 0000000..e0ebdca
--- /dev/null
+++ b/src/cpu/isa_b.rs
@@ -0,0 +1,38 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{Cpu, take_state_mut};
+
+impl Cpu {
+ pub(super) fn isa_b(&mut self, imm: i32) {
+ take_state_mut!(state, self);
+
+ let mut target = state.read_register(0xF).wrapping_add_signed(imm);
+
+ log!("b {target:#X}");
+
+ target = target.wrapping_add(self.instruction_size);
+ state.write_register(0xF, target);
+ }
+}
diff --git a/src/cpu/isa_bic.rs b/src/cpu/isa_bic.rs
new file mode 100644
index 0000000..6647e0c
--- /dev/null
+++ b/src/cpu/isa_bic.rs
@@ -0,0 +1,53 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_bic(&mut self, rd: u8, rn: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, r{rn}, {shifter}", match s {
+ false => "bic",
+ true => "bics",
+ });
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (mask, c) = state.shifter_value(shifter);
+
+ let result = rn_value & !mask;
+ state.write_register(rd, result);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00011111111111111111111111111111;
+ cpsr |= (c as u32) << 0x1D;
+ cpsr |= ((result == 0x0) as u32) << 0x1E;
+ cpsr |= result & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_bl.rs b/src/cpu/isa_bl.rs
new file mode 100644
index 0000000..416ce4a
--- /dev/null
+++ b/src/cpu/isa_bl.rs
@@ -0,0 +1,62 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{Cpu, take_state_mut};
+
+impl Cpu {
+ pub(super) fn isa_bl(&mut self, imm: i32) {
+ take_state_mut!(state, self);
+
+ let link_target = state.read_register(0xF).wrapping_sub(self.instruction_size);
+ let mut branch_target = state.read_register(0xF).wrapping_add_signed(imm);
+
+ log!("bl {branch_target:#X}");
+
+ branch_target = branch_target.wrapping_add(self.instruction_size);
+ state.write_register(0xE, link_target);
+ state.write_register(0xF, branch_target);
+ }
+
+ pub(super) fn isa_bl0(&mut self, imm: i32) {
+ take_state_mut!(state, self);
+
+ let target = state.read_register(0xF).wrapping_add_signed(imm);
+
+ state.write_register(0xE, target);
+ }
+
+ pub(super) fn isa_bl1(&mut self, imm: i32) {
+ take_state_mut!(state, self);
+
+ let mut branch_target = state.read_register(0xE).wrapping_add_signed(imm);
+ let link_target = state.read_register(0xF).wrapping_sub(0x2);
+
+ log!("bl {branch_target:#X}");
+
+ state.write_register(0xE, link_target);
+
+ branch_target = branch_target.wrapping_add(0x2);
+ state.write_register(0xF, branch_target);
+ }
+}
diff --git a/src/cpu/isa_bx.rs b/src/cpu/isa_bx.rs
new file mode 100644
index 0000000..3307dbf
--- /dev/null
+++ b/src/cpu/isa_bx.rs
@@ -0,0 +1,45 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{Cpu, exchange, take_state_mut};
+
+impl Cpu {
+ pub(super) fn isa_bx(&mut self, rm: u8) {
+ take_state_mut!(state, self);
+
+ log!("bx r{rm}");
+
+ let mut target = state.read_register(rm);
+
+ let t = target & 0b00000000000000000000000000000001 != 0x0;
+ exchange!(self, t);
+
+ let cpsr = state.read_cpsr() & 0b11111111111111111111111111011111 | (t as u32) << 0x5;
+ state.write_cpsr(cpsr);
+
+ target &= 0b11111111111111111111111111111110;
+ target = target.wrapping_add(self.instruction_size);
+ state.write_register(0xF, target);
+ }
+}
diff --git a/src/cpu/isa_cmn.rs b/src/cpu/isa_cmn.rs
new file mode 100644
index 0000000..f69acef
--- /dev/null
+++ b/src/cpu/isa_cmn.rs
@@ -0,0 +1,47 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_cmn(&mut self, rn: u8, shifter: Shifter) {
+ log!("cmn r{rn}, {shifter}");
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (subtrahend, _) = state.shifter_value(shifter);
+
+ let (temporary, c) = rn_value.overflowing_add(subtrahend);
+ let (_, v) = (rn_value as i32).overflowing_add_unsigned(subtrahend);
+
+ let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
+ cpsr |= (v as u32) << 0x1C;
+ cpsr |= (!c as u32) << 0x1D;
+ cpsr |= ((temporary == 0x0) as u32) << 0x1E;
+ cpsr |= temporary & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ }
+}
diff --git a/src/cpu/isa_cmp.rs b/src/cpu/isa_cmp.rs
new file mode 100644
index 0000000..c5719fb
--- /dev/null
+++ b/src/cpu/isa_cmp.rs
@@ -0,0 +1,47 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_cmp(&mut self, rn: u8, shifter: Shifter) {
+ log!("cmp r{rn}, {shifter}");
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (subtrahend, _) = state.shifter_value(shifter);
+
+ let (temporary, c) = rn_value.overflowing_sub(subtrahend);
+ let (_, v) = (rn_value as i32).overflowing_sub_unsigned(subtrahend);
+
+ let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
+ cpsr |= (v as u32) << 0x1C;
+ cpsr |= (!c as u32) << 0x1D;
+ cpsr |= ((temporary == 0x0) as u32) << 0x1E;
+ cpsr |= temporary & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ }
+}
diff --git a/src/cpu/isa_eor.rs b/src/cpu/isa_eor.rs
new file mode 100644
index 0000000..22d3842
--- /dev/null
+++ b/src/cpu/isa_eor.rs
@@ -0,0 +1,53 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_eor(&mut self, rd: u8, rn: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, r{rn}, {shifter}", match s {
+ false => "eor",
+ true => "eors",
+ });
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (mask, c) = state.shifter_value(shifter);
+
+ let result = rn_value ^ mask;
+ state.write_register(rd, result);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00011111111111111111111111111111;
+ cpsr |= (c as u32) << 0x1D;
+ cpsr |= ((result == 0x0) as u32) << 0x1E;
+ cpsr |= result & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_memory.rs b/src/cpu/isa_memory.rs
new file mode 100644
index 0000000..f4abecb
--- /dev/null
+++ b/src/cpu/isa_memory.rs
@@ -0,0 +1,122 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{Cpu, take_state_mut};
+
+impl Cpu {
+ pub(super) fn isa_load_halfword(&mut self, rd: u8, rn: u8, imm: i16) {
+ take_state_mut!(state, self);
+
+ log!("ldrh r{rd}, [r{rn}, #{imm:#X}]");
+
+ let rn_value = state.read_register(rn);
+
+ let target = rn_value.wrapping_add_signed(imm as i32);
+
+ let result = state.read_halfword(target) as u32;
+ state.write_register(rd, result);
+ }
+
+ pub(super) fn isa_load_immediate_offset(&mut self, rd: u8, rn: u8, imm: i16) {
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+
+ let target = rn_value.wrapping_add_signed(imm as i32);
+
+ log!("ldr r{rd}, [r{rn}, #{imm:#X}] @ {target:#010X}");
+
+ let result = state.read_word(target);
+ state.write_register(rd, result);
+ }
+
+ pub(super) fn isa_load_pc(&mut self, rd: u8, imm: u16) {
+ // Slightly different from load_immediate_offset
+ // due to the target being forced word-aligned.
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(0xF) & 0b11111111111111111111111111111100;
+
+ let target = rn_value.wrapping_add(imm as u32);
+
+ log!("ldr r{rd}, [pc, #{imm:#X}] @ {target:#010X}");
+
+ let result = state.read_word(target);
+ state.write_register(rd, result);
+ }
+
+ pub(super) fn isa_store_byte_immediate_offset(&mut self, rd: u8, rn: u8, imm: i16) {
+ log!("strb r{rd}, [r{rn}, #{imm:#X}]");
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+
+ let target = rn_value.wrapping_add_signed(imm as i32);
+
+ let result = state.read_register(rd) as u8;
+ state.write_byte(target, result);
+ }
+
+ pub(super) fn isa_store_byte_register_offset(&mut self, rd: u8, rn: u8, rm: u8) {
+ log!("strb r{rd}, [r{rn}, r{rm}]");
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let rm_value = state.read_register(rm);
+
+ let target = rn_value.wrapping_add(rm_value);
+
+ let result = state.read_register(rd) as u8;
+ state.write_byte(target, result);
+ }
+
+ pub(super) fn isa_store_halfword(&mut self, rd: u8, rn: u8, imm: i16) {
+ log!("strh r{rd}, [r{rn}, #{imm:#X}]");
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+
+ let target = rn_value.wrapping_add_signed(imm as i32);
+
+ let result = state.read_register(rd) as u16;
+ state.write_halfword(target, result);
+ }
+
+ pub(super) fn isa_store_immediate_offset(&mut self, rd: u8, rn: u8, imm: i16) {
+ log!("str r{rd}, [r{rn}, #{imm:#X}]");
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+
+ let target = rn_value.wrapping_add_signed(imm as i32);
+
+ let result = state.read_register(rd);
+ state.write_word(target, result);
+ }
+}
diff --git a/src/cpu/isa_mov.rs b/src/cpu/isa_mov.rs
new file mode 100644
index 0000000..302128e
--- /dev/null
+++ b/src/cpu/isa_mov.rs
@@ -0,0 +1,53 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_mov(&mut self, rd: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, {shifter}", match s {
+ false => "mov",
+ true => "movs",
+ });
+
+ take_state_mut!(state, self);
+
+ let (mut value, c) = state.shifter_value(shifter);
+ // Adjust value for branch.
+ if value == 0b1111 { value = value.wrapping_add(self.instruction_size.wrapping_shl(0x1)) };
+
+ state.write_register(rd, value);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00011111111111111111111111111111;
+ cpsr |= (c as u32) << 0x1D;
+ cpsr |= ((value == 0x0) as u32) << 0x1E;
+ cpsr |= value & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_mul.rs b/src/cpu/isa_mul.rs
new file mode 100644
index 0000000..d4212f0
--- /dev/null
+++ b/src/cpu/isa_mul.rs
@@ -0,0 +1,52 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_mul(&mut self, rd: u8, rn: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, r{rn}, {shifter}", match s {
+ false => "mul",
+ true => "muls",
+ });
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (multiplicand, _) = state.shifter_value(shifter);
+
+ let result = rn_value.wrapping_mul(multiplicand);
+ state.write_register(rd, result);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00111111111111111111111111111111;
+ cpsr |= ((result == 0x0) as u32) << 0x1E;
+ cpsr |= result & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_mvn.rs b/src/cpu/isa_mvn.rs
new file mode 100644
index 0000000..a3847e4
--- /dev/null
+++ b/src/cpu/isa_mvn.rs
@@ -0,0 +1,52 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_mvn(&mut self, rd: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, {shifter}", match s {
+ false => "mvn",
+ true => "mvns",
+ });
+
+ take_state_mut!(state, self);
+
+ let (mut result, c) = state.shifter_value(shifter);
+ result = !result;
+
+ state.write_register(rd, result);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00011111111111111111111111111111;
+ cpsr |= (c as u32) << 0x1D;
+ cpsr |= ((result == 0x0) as u32) << 0x1E;
+ cpsr |= result & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_orr.rs b/src/cpu/isa_orr.rs
new file mode 100644
index 0000000..030695a
--- /dev/null
+++ b/src/cpu/isa_orr.rs
@@ -0,0 +1,53 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_orr(&mut self, rd: u8, rn: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, r{rn}, {shifter}", match s {
+ false => "orr",
+ true => "orrs",
+ });
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (mask, c) = state.shifter_value(shifter);
+
+ let result = rn_value | mask;
+ state.write_register(rd, result);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00011111111111111111111111111111;
+ cpsr |= (c as u32) << 0x1D;
+ cpsr |= ((result == 0x0) as u32) << 0x1E;
+ cpsr |= result & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_rsb.rs b/src/cpu/isa_rsb.rs
new file mode 100644
index 0000000..d4daf06
--- /dev/null
+++ b/src/cpu/isa_rsb.rs
@@ -0,0 +1,55 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_rsb(&mut self, rd: u8, rn: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, r{rn}, {shifter}", match s {
+ false => "rsb",
+ true => "rsbs",
+ });
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (minuend, _) = state.shifter_value(shifter);
+
+ let (result, c) = minuend.overflowing_sub(rn_value);
+ let (_, v) = (minuend as i32).overflowing_sub_unsigned(rn_value);
+ state.write_register(rd, result);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
+ cpsr |= (v as u32) << 0x1C;
+ cpsr |= (!c as u32) << 0x1D;
+ cpsr |= ((result == 0x0) as u32) << 0x1E;
+ cpsr |= result & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_rsc.rs b/src/cpu/isa_rsc.rs
new file mode 100644
index 0000000..414df34
--- /dev/null
+++ b/src/cpu/isa_rsc.rs
@@ -0,0 +1,58 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_rsc(&mut self, rd: u8, rn: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, r{rn}, {shifter}", match s {
+ false => "rsc",
+ true => "rscs",
+ });
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (minuend, _) = state.shifter_value(shifter);
+
+ let c = (state.read_cpsr() & 0b00100000000000000000000000000000) >> 0x1D != 0x0;
+ let subtrahend = rn_value.wrapping_sub(c as u32);
+
+ let (result, c) = minuend.overflowing_sub(subtrahend);
+ let (_, v) = (minuend as i32).overflowing_sub_unsigned(subtrahend);
+ state.write_register(rd, result);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
+ cpsr |= (v as u32) << 0x1C;
+ cpsr |= (!c as u32) << 0x1D;
+ cpsr |= ((result == 0x0) as u32) << 0x1E;
+ cpsr |= result & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_sbc.rs b/src/cpu/isa_sbc.rs
new file mode 100644
index 0000000..28a7c0d
--- /dev/null
+++ b/src/cpu/isa_sbc.rs
@@ -0,0 +1,58 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_sbc(&mut self, rd: u8, rn: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, r{rn}, {shifter}", match s {
+ false => "sbc",
+ true => "sbcs",
+ });
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let c = (state.read_cpsr() & 0b00100000000000000000000000000000) >> 0x1D;
+
+ let (mut subtrahend, _) = state.shifter_value(shifter);
+ subtrahend = subtrahend.wrapping_sub(c);
+
+ let (result, c) = rn_value.overflowing_sub(subtrahend);
+ let (_, v) = (rn_value as i32).overflowing_sub_unsigned(subtrahend);
+ state.write_register(rd, result);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
+ cpsr |= (v as u32) << 0x1C;
+ cpsr |= (!c as u32) << 0x1D;
+ cpsr |= ((result == 0x0) as u32) << 0x1E;
+ cpsr |= result & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_sub.rs b/src/cpu/isa_sub.rs
new file mode 100644
index 0000000..f8290c9
--- /dev/null
+++ b/src/cpu/isa_sub.rs
@@ -0,0 +1,55 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_sub(&mut self, rd: u8, rn: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, r{rn}, {shifter}", match s {
+ false => "sub",
+ true => "subs",
+ });
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (subtrahend, _) = state.shifter_value(shifter);
+
+ let (result, c) = rn_value.overflowing_sub(subtrahend);
+ let (_, v) = (rn_value as i32).overflowing_sub_unsigned(subtrahend);
+ state.write_register(rd, result);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
+ cpsr |= (v as u32) << 0x1C;
+ cpsr |= (!c as u32) << 0x1D;
+ cpsr |= ((result == 0x0) as u32) << 0x1E;
+ cpsr |= result & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_swi.rs b/src/cpu/isa_swi.rs
new file mode 100644
index 0000000..0575740
--- /dev/null
+++ b/src/cpu/isa_swi.rs
@@ -0,0 +1,34 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{Cpu};
+use crate::exception::Exception;
+
+impl Cpu {
+ pub(super) fn isa_swi(&mut self, imm: u32) {
+ log!("swi #{imm:#X}");
+
+ self.enter_exception(Exception::SoftwareInterrupt);
+ }
+}
diff --git a/src/cpu/isa_teq.rs b/src/cpu/isa_teq.rs
new file mode 100644
index 0000000..2bc162a
--- /dev/null
+++ b/src/cpu/isa_teq.rs
@@ -0,0 +1,45 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_teq(&mut self, rn: u8, shifter: Shifter) {
+ log!("teq r{rn}, {shifter}");
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (mask, c) = state.shifter_value(shifter);
+
+ let temporary = rn_value ^ mask;
+
+ let mut cpsr = state.read_cpsr() & 0b00011111111111111111111111111111;
+ cpsr |= (c as u32) << 0x1D;
+ cpsr |= ((temporary == 0x0) as u32) << 0x1E;
+ cpsr |= temporary & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ }
+}
diff --git a/src/cpu/isa_tst.rs b/src/cpu/isa_tst.rs
new file mode 100644
index 0000000..0c4d290
--- /dev/null
+++ b/src/cpu/isa_tst.rs
@@ -0,0 +1,45 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_tst(&mut self, rn: u8, shifter: Shifter) {
+ log!("tst r{rn}, {shifter}");
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (mask, c) = state.shifter_value(shifter);
+
+ let temporary = rn_value & mask;
+
+ let mut cpsr = state.read_cpsr() & 0b00011111111111111111111111111111;
+ cpsr |= (c as u32) << 0x1D;
+ cpsr |= ((temporary == 0x0) as u32) << 0x1E;
+ cpsr |= temporary & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ }
+}
diff --git a/src/cpu/sync_cycle.rs b/src/cpu/sync_cycle.rs
new file mode 100644
index 0000000..b343c32
--- /dev/null
+++ b/src/cpu/sync_cycle.rs
@@ -0,0 +1,43 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::cpu::Cpu;
+
+use std::thread::sleep;
+use std::time::{Duration, Instant};
+
+impl Cpu {
+ pub fn sync_cycle(&self, cycle_start: Instant) {
+ // 16*1024*1024 Hz, 59.604644775 ns
+
+ const CYCLE_DURATION: f64 = 0.000000059604644775;
+ let cycle_duration = Duration::from_secs_f64(CYCLE_DURATION);
+
+ let remaining = match cycle_duration.checked_sub(cycle_start.elapsed()) {
+ Some(value) => value,
+ None => Duration::from_secs(0x0),
+ };
+
+ sleep(remaining);
+ }
+}
diff --git a/src/cpu/take_state.rs b/src/cpu/take_state.rs
new file mode 100644
index 0000000..5b90a1f
--- /dev/null
+++ b/src/cpu/take_state.rs
@@ -0,0 +1,36 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+macro_rules! take_state {
+ ($name: ident, $cpu: ident) => {
+ let $name = $cpu.state.lock().unwrap();
+ };
+}
+pub(crate) use take_state;
+
+macro_rules! take_state_mut {
+ ($name: ident, $cpu: ident) => {
+ let mut $name = $cpu.state.lock().unwrap();
+ };
+}
+pub(crate) use take_state_mut;