diff options
Diffstat (limited to 'src/cpu')
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; |