diff options
-rw-r--r-- | CHANGELOG.txt | 12 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | README.txt | 14 | ||||
-rw-r--r-- | src/luma.rs | 2 | ||||
-rw-r--r-- | src/luma/application/run.rs | 2 | ||||
-rw-r--r-- | src/luma/device.rs | 28 | ||||
-rw-r--r-- | src/luma/device/branch.rs | 55 | ||||
-rw-r--r-- | src/luma/device/condition.rs | 48 | ||||
-rw-r--r-- | src/luma/device/continue.rs | 12 | ||||
-rw-r--r-- | src/luma/device/decode_arm.rs (renamed from src/luma/device/decode.rs) | 34 | ||||
-rw-r--r-- | src/luma/device/decode_thumb.rs | 76 | ||||
-rw-r--r-- | src/luma/device/exchange.rs | 37 | ||||
-rw-r--r-- | src/luma/device/log.rs | 33 | ||||
-rw-r--r-- | src/luma/device/memory.rs | 8 | ||||
-rw-r--r-- | src/luma/device/move.rs | 4 | ||||
-rw-r--r-- | src/luma/device/new.rs | 1 | ||||
-rw-r--r-- | src/luma/device/read.rs | 10 | ||||
-rw-r--r-- | src/luma/device/store.rs | 16 | ||||
-rw-r--r-- | src/luma/device/thumb.rs | 30 | ||||
-rw-r--r-- | src/luma/device/trap.rs | 13 | ||||
-rw-r--r-- | src/luma/device/write.rs | 10 |
21 files changed, 340 insertions, 107 deletions
diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 9bab8ae..c0c0b6a 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,15 @@ +# 0.27 + +* Add support for Thumb: + * bx; + * b{cond}; + * b; +* Fix bx; +* Rework log method; +* Improve comments; +* Update readme; +* Only survive invalid opcode traps; + # 0.26 * Support bx; @@ -1,6 +1,6 @@ [package] name = "luma" -version = "0.38.0" +version = "0.39.0" authors = ["Gabriel Jensen"] edition = "2021" description = "AGB emulator." @@ -46,17 +46,23 @@ These settings are overwritten by terminal parameters (see USAGE). Currently, the emulator supports the following ARM instructions only. Others will be skipped. - * b{cond}{l} + * b{cond}{l} +/-offset_24 * bx Rm - * ldr{cond} Rn, +/-offset + * ldr{cond} Rn, +/-offset_12 * mov{cond} Rd, Rn * mov{cons}s r15, Rn - * str{cond} Rn, +/-offset + * str{cond} Rn, +/-offset_12 + +Moreover, the following Thumb instructions are supported: + + * b +/-offset_11 + * b{cond} +/-offset_8 + * bx Rm When the virtual processor boots, the default mode is the sys mode. As no supported instruction can change this mode, this is also the only mode for now. -The entire memory space (0x00000000 to 0x0E010000, inclusive) is available, +The entire memory space (0x00000000 to 0x0E010000, exclusive) is available, however, no I/O-mapped addresses are currently functional. Improved support is, of course, planned. diff --git a/src/luma.rs b/src/luma.rs index 316274e..9448ce0 100644 --- a/src/luma.rs +++ b/src/luma.rs @@ -32,7 +32,7 @@ pub struct VersionType<T> { pub const VERSION: VersionType::<u32> = VersionType::<u32> { major: 0x0, - minor: 0x26, + minor: 0x27, }; pub const CONFIGURATION_VERSION: u32 = 0x0; diff --git a/src/luma/application/run.rs b/src/luma/application/run.rs index d4052ad..9cba12c 100644 --- a/src/luma/application/run.rs +++ b/src/luma/application/run.rs @@ -57,7 +57,7 @@ impl Application { if cfg!(debug_assertions) { eprintln!("({cycle})"); } - self.device.decode(); + (self.device.decode)(&mut self.device); sleep(Duration::from_secs(0x1)); } diff --git a/src/luma/device.rs b/src/luma/device.rs index 7b84cf7..21a68c5 100644 --- a/src/luma/device.rs +++ b/src/luma/device.rs @@ -23,34 +23,28 @@ pub mod bootloader; pub mod branch; +pub mod condition; pub mod r#continue; -pub mod decode; +pub mod decode_arm; +pub mod decode_thumb; pub mod drop; +pub mod exchange; pub mod image; -pub mod store; pub mod log; pub mod memory; pub mod r#move; pub mod new; pub mod read; +pub mod store; +pub mod thumb; pub mod trap; pub mod write; -pub enum Log { - BranchOffset( i32, u32), - BranchRegister(u8, u32), - Continue( u32), - Link( u32), - Load( u8, u32, u8, i32, u32), - MoveRegister( u8, u8, u32), - MoveImmediate( u8, u32), - Store( u32, u8, u8, i32, u32), -} - pub enum Trap { - BadAlignment( u32, u32), - InvalidOpcode(u32, u32), - OutOfBounds( u32), + BadAlignment( u32, u32), + InvalidArmOpcode( u32, u32), + InvalidThumbOpcode(u32, u16), + OutOfBounds( u32), } pub enum Branch { @@ -59,6 +53,8 @@ pub enum Branch { } pub struct Device { + pub decode: fn(&mut Device), + memory: *mut u8, registers: [u32; 0x10], cpsr: u32, diff --git a/src/luma/device/branch.rs b/src/luma/device/branch.rs index 13cf895..6bc2320 100644 --- a/src/luma/device/branch.rs +++ b/src/luma/device/branch.rs @@ -21,33 +21,64 @@ see <https://www.gnu.org/licenses/>. */ -use crate::luma::device::{Branch, Device, Log}; +use crate::luma::device::{Branch, Device}; impl Device { pub fn branch(&mut self, kind: Branch) { match kind { - Branch::Offset( offset, l) => { + Branch::Offset(offset, l) => { if l { // Check the l flag. - self.registers[0xE] = self.registers[0xF] - 0x4; + // Store the address of the following instruction + // in r14 (lr). + + let pc_offset: u32 = match self.thumb() { + false => 0x4, + true => 0x2, + }; + + self.registers[0xE] = self.registers[0xF] - pc_offset; - self.log(Log::Link(self.registers[0xE])); + self.log("link", format!("r14 => r15-{pc_offset}={:#010X}", self.registers[0xE])); } - - (self.registers[0xF], _) = self.registers[0xF].overflowing_add_signed(offset + 0x8); // Add extra eight to move to the new fetch instruction. - - self.log(Log::BranchOffset(offset, self.registers[0xF] - 0x8)); + + // Add the offset to r15 (pc). + + let (address, _) = self.registers[0xF].overflowing_add_signed(offset); + + // Add extra offset to move to the new fetch + // instruction. + let pc_offset = match self.thumb() { + false => 0x8, + true => 0x4, + }; + + self.registers[0xF] = address + pc_offset; + + self.log("branch", format!("r15 => r15{offset:+}+{pc_offset} ({:#010X})", self.registers[0xF])); }, Branch::Register(register) => { + // Use the address stored in 'register' as the new + // value in r15 (pc). + let value = self.registers[register as usize]; - self.cpsr ^= (value & 0b00000000000000000000000000000001) << 0x5; + let t = value & 0b00000000000000000000000000000001 != 0x0; + + self.cpsr = self.cpsr & 0b11111111111111111111111111011111 | (t as u32) << 0x5; + self.exchange(t); let address = value & 0b11111111111111111111111111111110; - self.registers[0xF] = address + 0x8; - if value & 0b00000000000000000000000000000001 != 0x0 { eprintln!("switching to thumb") } + // Add extra offset to move to the new fetch + // instruction. + let pc_offset: u32 = match t { + false => 0x8, + true => 0x4, + }; + + self.registers[0xF] = address + pc_offset; - self.log(Log::BranchRegister(register, address)); + self.log("branch", format!("r15 => r{register}{pc_offset:+} ({:#010X})", self.registers[0xF])); }, } } diff --git a/src/luma/device/condition.rs b/src/luma/device/condition.rs new file mode 100644 index 0000000..d72993c --- /dev/null +++ b/src/luma/device/condition.rs @@ -0,0 +1,48 @@ +/* + 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::luma::device::Device; + +impl Device { + pub fn condition(&self, condition: u8) -> bool { + return match condition { + 0x0 => self.cpsr & 0b01000000000000000000000000000000 != 0x00, + 0x1 => self.cpsr & 0b01000000000000000000000000000000 == 0x00, + 0x2 => self.cpsr & 0b00100000000000000000000000000000 != 0x00, + 0x3 => self.cpsr & 0b00100000000000000000000000000000 == 0x00, + 0x4 => self.cpsr & 0b10000000000000000000000000000000 != 0x00, + 0x5 => self.cpsr & 0b10000000000000000000000000000000 == 0x00, + 0x6 => self.cpsr & 0b00010000000000000000000000000000 != 0x00, + 0x7 => self.cpsr & 0b00010000000000000000000000000000 == 0x00, + 0x8 => self.cpsr & 0b00100000000000000000000000000000 != 0x00 && self.cpsr & 0b01000000000000000000000000000000 == 0x00, + 0x9 => self.cpsr & 0b00100000000000000000000000000000 == 0x00 && self.cpsr & 0b01000000000000000000000000000000 != 0x00, + 0xA => self.cpsr & 0b00010000000000000000000000000000 >> 0x1C == self.cpsr & 0b10000000000000000000000000000000 >> 0x1F, + 0xB => self.cpsr & 0b00010000000000000000000000000000 >> 0x1C != self.cpsr & 0b10000000000000000000000000000000 >> 0x1F, + 0xC => self.cpsr & 0b01000000000000000000000000000000 == 0x00 && self.cpsr & 0b00010000000000000000000000000000 >> 0x1C == self.cpsr & 0b10000000000000000000000000000000 >> 0x1F, + 0xD => self.cpsr & 0b01000000000000000000000000000000 != 0x00 || self.cpsr & 0b00010000000000000000000000000000 >> 0x1C != self.cpsr & 0b10000000000000000000000000000000 >> 0x1F, + 0xE => true, + 0xF => false, // Unpredictable, but we ignore it. + _ => unreachable!(), + } + } +} diff --git a/src/luma/device/continue.rs b/src/luma/device/continue.rs index 5aabbb8..e1f43ec 100644 --- a/src/luma/device/continue.rs +++ b/src/luma/device/continue.rs @@ -21,12 +21,18 @@ see <https://www.gnu.org/licenses/>. */ -use crate::luma::device::{Device, Log}; +use crate::luma::device::Device; impl Device { pub fn r#continue(&mut self) { - self.registers[0xF] += 0x4; + let offset = if self.thumb() { + 0x2 + } else { + 0x4 + }; - self.log(Log::Continue(self.registers[0xF])); + self.registers[0xF] += offset; + + self.log("continue", format!("r15 => r15{offset:+}={:#010X}", self.registers[0xF])); } } diff --git a/src/luma/device/decode.rs b/src/luma/device/decode_arm.rs index cfd051a..e8a6e43 100644 --- a/src/luma/device/decode.rs +++ b/src/luma/device/decode_arm.rs @@ -24,28 +24,16 @@ use crate::luma::device::{Branch, Device, Trap}; impl Device { - pub fn decode(&mut self) { - let opcode = self.read_word(self.registers[0xF] - 0x8); - - let condition = match opcode & 0b11110000000000000000000000000000 { - 0b00000000000000000000000000000000 => self.cpsr & 0b01000000000000000000000000000000 != 0x00, - 0b00010000000000000000000000000000 => self.cpsr & 0b01000000000000000000000000000000 == 0x00, - 0b00100000000000000000000000000000 => self.cpsr & 0b00100000000000000000000000000000 != 0x00, - 0b00110000000000000000000000000000 => self.cpsr & 0b00100000000000000000000000000000 == 0x00, - 0b01000000000000000000000000000000 => self.cpsr & 0b10000000000000000000000000000000 != 0x00, - 0b01010000000000000000000000000000 => self.cpsr & 0b10000000000000000000000000000000 == 0x00, - 0b01100000000000000000000000000000 => self.cpsr & 0b00010000000000000000000000000000 != 0x00, - 0b01110000000000000000000000000000 => self.cpsr & 0b00010000000000000000000000000000 == 0x00, - 0b10000000000000000000000000000000 => self.cpsr & 0b00100000000000000000000000000000 != 0x00 && self.cpsr & 0b01000000000000000000000000000000 == 0x00, - 0b10010000000000000000000000000000 => self.cpsr & 0b00100000000000000000000000000000 == 0x00 && self.cpsr & 0b01000000000000000000000000000000 != 0x00, - 0b10100000000000000000000000000000 => self.cpsr & 0b00010000000000000000000000000000 >> 0x1C == self.cpsr & 0b10000000000000000000000000000000 >> 0x1F, - 0b10110000000000000000000000000000 => self.cpsr & 0b00010000000000000000000000000000 >> 0x1C != self.cpsr & 0b10000000000000000000000000000000 >> 0x1F, - 0b11000000000000000000000000000000 => self.cpsr & 0b01000000000000000000000000000000 == 0x00 && self.cpsr & 0b00010000000000000000000000000000 >> 0x1C == self.cpsr & 0b10000000000000000000000000000000 >> 0x1F, - 0b11010000000000000000000000000000 => self.cpsr & 0b01000000000000000000000000000000 != 0x00 || self.cpsr & 0b00010000000000000000000000000000 >> 0x1C != self.cpsr & 0b10000000000000000000000000000000 >> 0x1F, - 0b11100000000000000000000000000000 => true, - _ => return self.trap(Trap::InvalidOpcode(self.registers[0xF] - 0x8, opcode)), - }; - if !condition { return self.r#continue() } + pub fn decode_arm(&mut self) { + debug_assert!(!self.thumb()); + + let address = self.registers[0xF] - 0x8; + + let opcode = self.read_word(address); + if cfg!(debug_assertions) { eprintln!("{opcode:#034b} @ {address:#010X}") } + + let condition = ((opcode & 0b11110000000000000000000000000000) >> 0x1C) as u8; + if !self.condition(condition) { return self.r#continue() } // b{cond}{l} if opcode & 0b00001110000000000000000000000000 == 0b00001010000000000000000000000000 { @@ -101,7 +89,7 @@ impl Device { return self.r#continue(); } - self.trap(Trap::InvalidOpcode(self.registers[0xF] - 0x8, opcode)); + self.trap(Trap::InvalidArmOpcode(self.registers[0xF] - 0x8, opcode)); self.r#continue(); } diff --git a/src/luma/device/decode_thumb.rs b/src/luma/device/decode_thumb.rs new file mode 100644 index 0000000..1e57659 --- /dev/null +++ b/src/luma/device/decode_thumb.rs @@ -0,0 +1,76 @@ +/* + 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::luma::device::{Branch, Device, Trap}; + +impl Device { + pub fn decode_thumb(&mut self) { + debug_assert!(self.thumb()); + + let address = self.registers[0xF] - 0x4; + + let opcode = self.read_halfword(address); + if cfg!(debug_assertions) { eprintln!("{opcode:#018b} @ {address:#010X}") } + + // b + if opcode & 0b1111100000000000 == 0b1110000000000000 { + let offset = { + let mut offset = (opcode & 0b0000011111111111) as u32; + + offset <<= 0x1; + + if offset & 0b00000000000000000000100000000000 != 0x0 { offset |= 0b11111111111111111111000000000000 } + + offset as i32 + }; + + return self.branch(Branch::Offset(offset, false)); + } + + // b{cond} + if opcode & 0b1111000000000000 == 0b1101000000000000 { + let offset = { + let mut offset = (opcode & 0b0000000011111111) as u32; + + offset <<= 0x1; + + if offset & 0b00000000000000000000000100000000 != 0x0 { offset |= 0b11111111111111111111111000000000 } + + offset as i32 + }; + + return self.branch(Branch::Offset(offset, false)); + } + + // bx + if opcode & 0b1111111110000111 == 0b0100011100000000 { + let register = ((opcode & 0b0000000001111000) >> 0x3) as u8; + + return self.branch(Branch::Register(register)); + } + + self.trap(Trap::InvalidThumbOpcode(address, opcode)); + + self.r#continue(); + } +} diff --git a/src/luma/device/exchange.rs b/src/luma/device/exchange.rs new file mode 100644 index 0000000..32a2f8a --- /dev/null +++ b/src/luma/device/exchange.rs @@ -0,0 +1,37 @@ +/* + 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::luma::device::Device; + +impl Device { + pub fn exchange(&mut self, thumb: bool) { + let decoders = [ + Device::decode_arm, + Device::decode_thumb, + ]; + + self.decode = decoders[thumb as usize]; + + self.log("exchange", format!("T => {thumb}")); + } +} diff --git a/src/luma/device/log.rs b/src/luma/device/log.rs index 47bef4c..e0ace5d 100644 --- a/src/luma/device/log.rs +++ b/src/luma/device/log.rs @@ -21,34 +21,17 @@ see <https://www.gnu.org/licenses/>. */ -use crate::luma::device::{Device, Log}; +use crate::luma::device::Device; impl Device { - pub fn log(&mut self, kind: Log) { + pub fn log(&mut self, keyword: &str, message: String) { if cfg!(debug_assertions) { // This optimises the function away. - let kind_string = match kind { - Log::BranchOffset( ..) => "branch ", - Log::BranchRegister(..) => "branch ", - Log::Continue( ..) => "continue", - Log::Link( ..) => "link ", - Log::Load( ..) => "load ", - Log::MoveRegister( ..) => "move ", - Log::MoveImmediate( ..) => "move ", - Log::Store( ..) => "store ", - }; - - let message = match kind { - Log::BranchOffset( offset, address) => format!("r15{offset:+} => {address:#010X}"), - Log::BranchRegister(register, address) => format!("r15 => r{register} ({address:#08X})"), - Log::Continue( address) => format!("r15 => {address:#010X}"), - Log::Link( address) => format!("r14 => {address:#010X}"), - Log::Load( register, address, base, offset, value) => format!("r{register} => r{base}{offset:+}={address:#010X} ({value:#010X})"), - Log::MoveRegister( destination, source, value) => format!("r{destination} => r{source} ({value:#010X})"), - Log::MoveImmediate( register, immediate) => format!("r{register} => {immediate:#X}"), - Log::Store( address, register, base, offset, value) => format!("r{base}{offset:+}={address:#010X} => r{register} ({value:#010X})"), - }; - - eprintln!("{kind_string} : {message}"); + let padding: usize = 0x8; + + assert!(keyword.len() <= padding); + let keyword = keyword.to_string() + &" ".to_string().repeat(padding - keyword.len()); + + eprintln!("{keyword} : {message}"); } } } diff --git a/src/luma/device/memory.rs b/src/luma/device/memory.rs index f13e56b..652b133 100644 --- a/src/luma/device/memory.rs +++ b/src/luma/device/memory.rs @@ -21,10 +21,14 @@ see <https://www.gnu.org/licenses/>. */ +use crate::luma::MEMORY_SIZE; use crate::luma::device::Device; +use std::slice; + impl Device { - pub fn memory(&mut self) -> *const u8{ - return self.memory as *const u8; + #[allow(dead_code)] + pub fn memory<'a>(&mut self) -> &'a mut [u8] { + return unsafe { slice::from_raw_parts_mut(self.memory.offset(0x00000000), MEMORY_SIZE) }; } }
\ No newline at end of file diff --git a/src/luma/device/move.rs b/src/luma/device/move.rs index ba67146..6cc7dab 100644 --- a/src/luma/device/move.rs +++ b/src/luma/device/move.rs @@ -21,7 +21,7 @@ see <https://www.gnu.org/licenses/>. */ -use crate::luma::device::{Device, Log}; +use crate::luma::device::Device; impl Device { pub fn r#move(&mut self, destination: u8, source: u8, s: bool) { @@ -36,6 +36,6 @@ impl Device { } } - self.log(Log::MoveRegister(destination, source, value)); + self.log("move", format!("r{destination} => r{source} ({value:#010X})")); } }
\ No newline at end of file diff --git a/src/luma/device/new.rs b/src/luma/device/new.rs index 59a3380..c3aec4f 100644 --- a/src/luma/device/new.rs +++ b/src/luma/device/new.rs @@ -40,6 +40,7 @@ impl Device { eprintln!("starting emulation at {start:#08X}"); return Device { + decode: Device::decode_arm, memory: memory, registers: [ 0x00000000, diff --git a/src/luma/device/read.rs b/src/luma/device/read.rs index 0724075..5622136 100644 --- a/src/luma/device/read.rs +++ b/src/luma/device/read.rs @@ -27,23 +27,23 @@ use crate::luma::MEMORY_SIZE; impl Device { #[allow(dead_code)] pub fn read_byte(&mut self, address: u32) -> u8 { - if address >= MEMORY_SIZE as u32 { self.trap(Trap::OutOfBounds(address)) }; + if address >= MEMORY_SIZE as u32 { self.trap(Trap::OutOfBounds(address)) } return unsafe { *(self.memory.offset(address as isize) as *mut u8) }; } #[allow(dead_code)] pub fn read_halfword(&mut self, address: u32) -> u16 { - if address >= MEMORY_SIZE as u32 { self.trap(Trap::OutOfBounds(address)) }; - if address % 0x2 != 0x0 { self.trap(Trap::BadAlignment(address, 0x2)) }; + if address >= MEMORY_SIZE as u32 { self.trap(Trap::OutOfBounds(address)) } + if address % 0x2 != 0x0 { self.trap(Trap::BadAlignment(address, 0x2)) } return unsafe { *(self.memory.offset(address as isize) as *mut u16) }; } #[allow(dead_code)] pub fn read_word(&mut self, address: u32) -> u32 { - if address >= MEMORY_SIZE as u32 { self.trap(Trap::OutOfBounds(address)) }; - if address % 0x4 != 0x0 { self.trap(Trap::BadAlignment(address, 0x4)) }; + if address >= MEMORY_SIZE as u32 { self.trap(Trap::OutOfBounds(address)) } + if address % 0x4 != 0x0 { self.trap(Trap::BadAlignment(address, 0x4)) } return unsafe { *(self.memory.offset(address as isize) as *mut u32) }; } diff --git a/src/luma/device/store.rs b/src/luma/device/store.rs index 21fb3a7..e1df1a0 100644 --- a/src/luma/device/store.rs +++ b/src/luma/device/store.rs @@ -21,12 +21,14 @@ see <https://www.gnu.org/licenses/>. */ -use crate::luma::device::{Device, Log}; +use crate::luma::device::Device; impl Device { pub fn store(&mut self, register: u8, base: u8, immediate: u16, u: bool, _b: bool, l: bool) { // TO-DO: Byte loads/stores. + // The U flag determins the sign of the offset + // (set = unsigned). let offset = if u { 0x0 + immediate as i32 } else { @@ -35,16 +37,22 @@ impl Device { let (address, _) = self.registers[base as usize].overflowing_add_signed(offset); - if l { // Check the l flag. + if l { // Check the L flag. + // If the L flag is set, we perform a memory-to- + // register load instead. + let value = self.read_word(address); self.registers[register as usize] = value; - self.log(Log::Load(register, address, base, offset, value)); + self.log("load", format!("r{register} => r{base}{offset:+}={address:#010X} ({value:#010X})")); } else { + // Otherwise, we perform a register-to-memory + // store. + let value = self.registers[register as usize]; self.write_word(address, value); - self.log(Log::Store(address, register, base, offset, value)); + self.log("store", format!("r{base}{offset:+}={address:#010X} => r{register} ({value:#010X})")); } } } diff --git a/src/luma/device/thumb.rs b/src/luma/device/thumb.rs new file mode 100644 index 0000000..130a164 --- /dev/null +++ b/src/luma/device/thumb.rs @@ -0,0 +1,30 @@ +/* + 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::luma::device::Device; + +impl Device { + pub fn thumb(&self) -> bool { + return self.cpsr & 0b00000000000000000000000000100000 != 0x0; + } +} diff --git a/src/luma/device/trap.rs b/src/luma/device/trap.rs index 8af3622..11e3343 100644 --- a/src/luma/device/trap.rs +++ b/src/luma/device/trap.rs @@ -27,9 +27,10 @@ use crate::luma::device::{Device, Trap}; impl Device { pub fn trap(&mut self, kind: Trap) { let message = match kind { - Trap::BadAlignment( address, alignment) => format!("bad alignment of address {address:#010X} (should be {alignment}-byte aligned)"), - Trap::InvalidOpcode(address, opcode) => format!("invalid opcode {opcode:#034b} at {address:#010X}"), - Trap::OutOfBounds( address) => format!("out-of-bounds address {address:#010X} (limit is {MEMORY_SIZE:#010X})"), + Trap::BadAlignment( address, alignment) => format!("bad alignment of address {address:#010X} (should be {alignment}-byte aligned)"), + Trap::InvalidArmOpcode( address, opcode) => format!("invalid opcode {opcode:#034b} at {address:#010X}"), + Trap::InvalidThumbOpcode(address, opcode) => format!("invalid opcode {opcode:#018b} at {address:#010X}"), + Trap::OutOfBounds( address) => format!("out-of-bounds address {address:#010X} (limit is {MEMORY_SIZE:#010X})"), }; eprintln!("{message}"); @@ -55,5 +56,11 @@ impl Device { eprintln!(" spsr_svc: {:#034b}", self.spsr[0x3]); eprintln!(" spsr_abt: {:#034b}", self.spsr[0x7]); eprintln!(" spsr_und: {:#034b}", self.spsr[0xB]); + + match kind { + Trap::BadAlignment(..) => panic!("bad alignment of address"), + Trap::OutOfBounds( ..) => panic!("out-of-bounds address"), + _ => {}, + } } } diff --git a/src/luma/device/write.rs b/src/luma/device/write.rs index e234bef..1d29cbf 100644 --- a/src/luma/device/write.rs +++ b/src/luma/device/write.rs @@ -27,23 +27,23 @@ use crate::luma::MEMORY_SIZE; impl Device { #[allow(dead_code)] pub fn write_byte(&mut self, address: u32, value: u8) { - if address >= MEMORY_SIZE as u32 { self.trap(Trap::OutOfBounds(address)) }; + if address >= MEMORY_SIZE as u32 { self.trap(Trap::OutOfBounds(address)) } return unsafe { *(self.memory.offset(address as isize) as *mut u8) = value }; } #[allow(dead_code)] pub fn write_halfword(&mut self, address: u32, value: u16) { - if address >= MEMORY_SIZE as u32 { self.trap(Trap::OutOfBounds(address)) }; - if address % 0x2 != 0x0 { self.trap(Trap::BadAlignment(address, 0x2)) }; + if address >= MEMORY_SIZE as u32 { self.trap(Trap::OutOfBounds(address)) } + if address % 0x2 != 0x0 { self.trap(Trap::BadAlignment(address, 0x2)) } return unsafe { *(self.memory.offset(address as isize) as *mut u16) = value }; } #[allow(dead_code)] pub fn write_word(&mut self, address: u32, value: u32) { - if address >= MEMORY_SIZE as u32 { self.trap(Trap::OutOfBounds(address)) }; - if address % 0x4 != 0x0 { self.trap(Trap::BadAlignment(address, 0x4)) }; + if address >= MEMORY_SIZE as u32 { self.trap(Trap::OutOfBounds(address)) } + if address % 0x4 != 0x0 { self.trap(Trap::BadAlignment(address, 0x4)) } return unsafe { *(self.memory.offset(address as isize) as *mut u32) = value }; } |