summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.txt12
-rw-r--r--Cargo.toml2
-rw-r--r--README.txt14
-rw-r--r--src/luma.rs2
-rw-r--r--src/luma/application/run.rs2
-rw-r--r--src/luma/device.rs28
-rw-r--r--src/luma/device/branch.rs55
-rw-r--r--src/luma/device/condition.rs48
-rw-r--r--src/luma/device/continue.rs12
-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.rs76
-rw-r--r--src/luma/device/exchange.rs37
-rw-r--r--src/luma/device/log.rs33
-rw-r--r--src/luma/device/memory.rs8
-rw-r--r--src/luma/device/move.rs4
-rw-r--r--src/luma/device/new.rs1
-rw-r--r--src/luma/device/read.rs10
-rw-r--r--src/luma/device/store.rs16
-rw-r--r--src/luma/device/thumb.rs30
-rw-r--r--src/luma/device/trap.rs13
-rw-r--r--src/luma/device/write.rs10
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;
diff --git a/Cargo.toml b/Cargo.toml
index c751c14..d813a0b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "luma"
-version = "0.38.0"
+version = "0.39.0"
authors = ["Gabriel Jensen"]
edition = "2021"
description = "AGB emulator."
diff --git a/README.txt b/README.txt
index bb28492..f8e3154 100644
--- a/README.txt
+++ b/README.txt
@@ -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 };
}