summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md10
-rw-r--r--README.md6
-rw-r--r--src/luma.rs35
-rw-r--r--src/luma/app/draw_video.rs17
-rw-r--r--src/luma/app/run.rs2
-rw-r--r--src/luma/cpu.rs37
-rw-r--r--src/luma/cpu/add.rs50
-rw-r--r--src/luma/cpu/boot.rs75
-rw-r--r--src/luma/cpu/compare.rs70
-rw-r--r--src/luma/cpu/decode_arm.rs8
-rw-r--r--src/luma/cpu/decode_thumb.rs39
-rw-r--r--src/luma/cpu/isa_arithmetic.rs122
-rw-r--r--src/luma/cpu/isa_bitwise.rs75
-rw-r--r--src/luma/cpu/isa_branch.rs (renamed from src/luma/cpu/branch.rs)28
-rw-r--r--src/luma/cpu/isa_logic.rs111
-rw-r--r--src/luma/cpu/isa_memory.rs122
-rw-r--r--src/luma/cpu/isa_move.rs (renamed from src/luma/cpu/move.rs)71
-rw-r--r--src/luma/cpu/load.rs69
-rw-r--r--src/luma/cpu/store.rs80
-rw-r--r--src/luma/cpu/subtract.rs50
-rw-r--r--src/luma/cpu_handle.rs6
-rw-r--r--src/luma/cpu_handle/drop.rs (renamed from src/luma/cpu_handle/kill.rs)13
-rw-r--r--src/luma/instruction.rs12
-rw-r--r--src/luma/state.rs117
-rw-r--r--src/luma/state/read.rs74
-rw-r--r--src/luma/state/write.rs116
-rw-r--r--test.s12
27 files changed, 865 insertions, 562 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 57d7825..f361f14 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,13 @@
+# 0.2E
+
+* Update readme
+* Implement remaining data-processing instructions with register operands in Thumb
+* Extend memory writes according to region
+* Fix colour decoding
+* Improve logging
+* Update code structure
+* Implement read-only memory
+
# 0.2D
* Reformat changelog in Markdown
diff --git a/README.md b/README.md
index ee88b42..e7c525d 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,7 @@ The emulator tries to read the configuration file at `${HOME}/.luma.toml`. If su
* `image`: The path to the image file
`video`:
- * `scale`: The scale modifier applied to the screen (1-4294967295)
+ * `scale`: The scale modifier applied to the screen (1‐4294967295)
If a path is parsed as a terminal parameter, the configuration at that location is read instead.
@@ -30,13 +30,13 @@ If a path is parsed as a terminal parameter, the configuration at that location
Currently, the emulator has limited support for the Arm instruction set. All of the instructions used in the provided test program are – however – implemented.
-The entire memory space (`0x00000000` to `0x0E00FFFF`) is available, however, no I/O-mapped addresses are currently functional.
+The entire memory space (`0x00000000` to `0x0E00FFFF`) is available, however, no I/O‐mapped addresses are currently functional.
Improved support is, of course, planned.
# Copyright & License
-Copyright 2021-2023 Gabriel Bjørnager Jensen.
+Copyright 2021‐2023 Gabriel Bjørnager Jensen.
This program 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.
diff --git a/src/luma.rs b/src/luma.rs
index 26c0014..9fd490a 100644
--- a/src/luma.rs
+++ b/src/luma.rs
@@ -21,8 +21,6 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use sdl2::pixels::Color;
-
pub mod app;
pub mod configuration;
pub mod cpu;
@@ -32,7 +30,7 @@ pub mod state;
pub const VERSION: (u32, u32) = (
0x0, // major
- 0x2D, // minor
+ 0x2E, // minor
);
pub enum Error {
@@ -51,7 +49,7 @@ impl Error {
Error::OutOfBounds( address) => format!("out-of-bounds address {address:#010X} (limit is {:#010X})", MEMORY_LENGTH),
};
- eprintln!("trap: {message}");
+ eprintln!("\u{1B}[1m\u{1B}[91mtrap\u{1B}[0m: {message}");
}
}
@@ -67,17 +65,26 @@ pub const SCREEN_SIZE: (u8, u8) = (
0xA0, // height
);
-pub const fn decode_colour(colour: u16) -> Color {
- let red = ((colour & 0b0000000000011111) << 0x3) as u8;
- let green = ((colour & 0b0000001111100000) >> 0x2) as u8;
- let blue = ((colour & 0b0111110000000000) >> 0x7) as u8;
+macro_rules! log {
+ () => {
+ eprintln!();
+ };
- return Color::RGB(red, green, blue);
+ ($($message: tt)*) => {{
+ if cfg!(debug_assertions) {
+ eprintln!("{}", format!($($message)?));
+ }
+ }};
}
+pub(crate) use log;
+
+macro_rules! log_assignment {
+ ($name: expr, $value: expr) => {{
+ use crate::luma::log;
-pub fn log(message: &str) {
- // This optimises the function away.
- if cfg!(debug_assertions) {
- eprintln!("{message}");
- }
+ if cfg!(debug_assertions) {
+ log!("\u{1B}[3m\u{B7} \u{1B}[1m{}\u{1B}[22m = {}\u{1B}[0m", format!($name), format!($value));
+ }
+ }};
}
+pub(crate) use log_assignment;
diff --git a/src/luma/app/draw_video.rs b/src/luma/app/draw_video.rs
index 5df407e..4acb012 100644
--- a/src/luma/app/draw_video.rs
+++ b/src/luma/app/draw_video.rs
@@ -21,7 +21,7 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::luma::{decode_colour, SCREEN_SIZE};
+use crate::luma::SCREEN_SIZE;
use crate::luma::app::App;
use sdl2::pixels::Color;
@@ -33,10 +33,11 @@ impl App {
let mut palette: [Color; 0x100] = [Color::RGB(0x00, 0x00, 0x00); 0x100];
- for (index, value) in (&agb_palette[0x0..0x100]).into_iter().enumerate() {
- let colour = decode_colour(*value);
+ for (index, element) in palette.iter_mut().enumerate() {
+ let value = unsafe { *agb_palette.get_unchecked(index) };
- palette[index] = colour;
+ let colour = decode_colour(value);
+ *element = colour;
}
for pixel_y in 0x0..SCREEN_SIZE.1 {
@@ -60,3 +61,11 @@ impl App {
self.canvas.present();
}
}
+
+fn decode_colour(colour: u16) -> Color {
+ let red = ((colour & 0b0000000000011111) as f32 / 31.0 * 255.0) as u8;
+ let green = ((colour & 0b0000001111100000) as f32 / 992.0 * 255.0) as u8;
+ let blue = ((colour & 0b0111110000000000) as f32 / 31744.0 * 255.0) as u8;
+
+ return Color::RGB(red, green, blue);
+}
diff --git a/src/luma/app/run.rs b/src/luma/app/run.rs
index 959bc71..6c00153 100644
--- a/src/luma/app/run.rs
+++ b/src/luma/app/run.rs
@@ -53,8 +53,6 @@ impl App {
self.sync_video(frame_start);
}
- cpu.kill()?;
-
return Ok(());
}
}
diff --git a/src/luma/cpu.rs b/src/luma/cpu.rs
index a31c47d..28f6844 100644
--- a/src/luma/cpu.rs
+++ b/src/luma/cpu.rs
@@ -27,28 +27,16 @@ use crate::luma::state::State;
use std::sync::{Arc, Mutex};
use std::sync::atomic::AtomicBool;
-pub mod add;
pub mod boot;
-pub mod branch;
-pub mod compare;
-pub mod r#continue;
pub mod decode_arm;
pub mod decode_thumb;
-pub mod load;
-pub mod r#move;
-pub mod store;
-pub mod subtract;
-
-pub type Decoder = fn(&mut Cpu) -> Instruction;
-
-pub struct Cpu {
- state: Arc<Mutex<State>>,
- cycle: u64,
- dead: Arc<AtomicBool>,
-
- instruction_size: u32,
- decoder: Decoder,
-}
+pub mod isa_arithmetic;
+pub mod isa_bitwise;
+pub mod isa_branch;
+pub mod isa_logic;
+pub mod isa_memory;
+pub mod isa_move;
+pub mod r#continue;
mod exchange;
mod test_predicate;
@@ -61,6 +49,17 @@ pub use exchange::*;
#[allow(unused_imports)]
pub use test_predicate::*;
+pub type Decoder = fn(&mut Cpu) -> Instruction;
+
+pub struct Cpu {
+ state: Arc<Mutex<State>>,
+ cycle: u64,
+ dead: Arc<AtomicBool>,
+
+ instruction_size: u32,
+ decoder: Decoder,
+}
+
impl Cpu {
pub fn new(state: State) -> Self {
return Self {
diff --git a/src/luma/cpu/add.rs b/src/luma/cpu/add.rs
deleted file mode 100644
index c2594a3..0000000
--- a/src/luma/cpu/add.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- 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::log;
-use crate::luma::cpu::Cpu;
-
-impl Cpu {
- pub(super) fn add_immediate(&mut self, destination: u8, base: u8, immediate: u32) {
- log(&format!("add r{destination}, r{base}, {immediate:#X}"));
-
- let mut state = self.state.lock().unwrap();
-
- let base_value = state.read_register(base);
-
- let value = base_value.wrapping_add(immediate);
- state.write_register(destination, value);
- }
-
- pub(super) fn add_register(&mut self, destination: u8, base: u8, add: u8) {
- log(&format!("add r{destination}, r{base}, r{add}"));
-
- let mut state = self.state.lock().unwrap();
-
- let base_value = state.read_register(base);
- let add_value = state.read_register(add);
-
- let value = base_value.wrapping_add(add_value);
- state.write_register(destination, value);
- }
-}
diff --git a/src/luma/cpu/boot.rs b/src/luma/cpu/boot.rs
index f92176b..4a089eb 100644
--- a/src/luma/cpu/boot.rs
+++ b/src/luma/cpu/boot.rs
@@ -73,32 +73,55 @@ impl Cpu {
use Instruction::*;
match instruction {
- AddImmediate( destination, base, immediate) => self.add_immediate( destination, base, immediate),
- AddRegister( destination, base, immediate) => self.add_register( destination, base, immediate),
- Branch( offset) => self.branch( offset),
- BranchExchange( source) => self.branch_exchange( source),
- BranchLink( offset) => self.branch_link( offset),
- BranchLinkPrefix( offset) => self.branch_link_prefix( offset),
- BranchLinkSuffix( offset) => self.branch_link_suffix( offset),
- CompareImmediate( register, immediate) => self.compare_immediate( register, immediate),
- CompareRegister( left, right) => self.compare_register( left, right),
- LoadHalfword( destination, base, offset) => self.load_halfword( destination, base, offset),
- LoadImmediateOffset( destination, base, offset) => self.load_immediate_offset( destination, base, offset),
- LoadPc( destination, offset) => self.load_pc( destination, offset),
- MoveImmediate( destination, immediate) => self.move_immediate( destination, immediate),
- MoveImmediateArithmeticShiftRight( destination, base, shift) => self.move_immediate_arithmetic_shift_right(destination, base, shift),
- MoveImmediateLogicalShiftLeftImmediate( destination, base, shift) => self.move_immediate_logical_shift_left( destination, base, shift),
- MoveImmediateLogicalShiftRightImmediate(destination, base, shift) => self.move_immediate_logical_shift_right( destination, base, shift),
- MoveRegister( destination, source) => self.move_register( destination, source),
- MoveRegisterArithmeticShiftRight( destination, base, shift) => self.move_register_arithmetic_shift_right( destination, base, shift),
- MoveRegisterLogicalShiftLeftImmediate( destination, base, shift) => self.move_register_logical_shift_left( destination, base, shift),
- MoveRegisterLogicalShiftRightImmediate( destination, base, shift) => self.move_register_logical_shift_right( destination, base, shift),
- StoreByteImmediateOffset( source, base, offset) => self.store_byte_immediate_offset( source, base, offset),
- StoreByteRegisterOffset( source, base, offset) => self.store_byte_register_offset( source, base, offset),
- StoreHalfword( source, base, offset) => self.store_halfword( source, base, offset),
- StoreImmediateOffset( source, base, offset) => self.store_immediate_offset( source, base, offset),
- SubtractImmediate( destination, base, immediate) => self.subtract_immediate( destination, base, immediate),
- SubtractRegister( destination, base, immediate) => self.subtract_register( destination, base, immediate),
+ // Arithmetic:
+ AddCarryRegister( rd, rn, rm) => self.isa_add_carry_register( rd, rn, rm),
+ AddImmediate( rd, rn, imm) => self.isa_add_immediate( rd, rn, imm),
+ AddRegister( rd, rn, rm) => self.isa_add_register( rd, rn, rm),
+ MultiplyRegister( rd, rm, rs) => self.isa_multiply_register( rd, rm, rs),
+ ReverseSubtractImmediate(rd, rm, imm) => self.isa_reverse_subtract_immediate(rd, rm, imm),
+ SubtractCarryRegister( rd, rn, rm) => self.isa_subtract_carry_register( rd, rn, rm),
+ SubtractImmediate( rd, rn, imm) => self.isa_subtract_immediate( rd, rn, imm),
+ SubtractRegister( rd, rn, rm) => self.isa_subtract_register( rd, rn, rm),
+
+ // Bitwise:
+ AndRegister( rd, rn, rm) => self.isa_and_register( rd, rn, rm),
+ BitClearRegister( rd, rn, rm) => self.isa_bit_clear_register( rd, rn, rm),
+ LogicalOrRegister( rd, rn, rm) => self.isa_logical_or_register( rd, rn, rm),
+ ExclusiveOrRegister(rd, rn, rm) => self.isa_exclusive_or_register(rd, rn, rm),
+
+ // Branch:
+ Branch( imm) => self.isa_branch( imm),
+ BranchExchange( rm) => self.isa_branch_exchange( rm),
+ BranchLink( imm) => self.isa_branch_link( imm),
+ BranchLinkPrefix(imm) => self.isa_branch_link_prefix(imm),
+ BranchLinkSuffix(imm) => self.isa_branch_link_suffix(imm),
+
+ // Logic:
+ CompareImmediate( rn, imm) => self.isa_compare_immediate( rn, imm),
+ CompareNegativeRegister(rn, rm) => self.isa_compare_negative_register(rn, rm),
+ CompareRegister( rn, rm) => self.isa_compare_register( rn, rm),
+ TestRegister( rn, rm) => self.isa_test_register( rn, rm),
+
+ // Memory:
+ 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, rn) => self.isa_load_pc( rd, rn),
+ 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),
+
+ // Move:
+ MoveImmediate( rd, imm) => self.isa_move_immediate( rd, imm),
+ MoveImmediateArithmeticShiftRight( rd, rm, rs) => self.isa_move_immediate_arithmetic_shift_right(rd, rm, rs),
+ MoveImmediateLogicalShiftLeftImmediate( rd, rm, rs) => self.isa_move_immediate_logical_shift_left( rd, rm, rs),
+ MoveImmediateLogicalShiftRightImmediate(rd, rm, rs) => self.isa_move_immediate_logical_shift_right( rd, rm, rs),
+ MoveNotRegister( rd, rm) => self.isa_move_not_register( rd, rm),
+ MoveRegister( rd, rm) => self.isa_move_register( rd, rm),
+ MoveRegisterArithmeticShiftRight( rd, rm, rs) => self.isa_move_register_arithmetic_shift_right( rd, rm, rs),
+ MoveRegisterLogicalShiftLeftImmediate( rd, rm, rs) => self.isa_move_register_logical_shift_left( rd, rm, rs),
+ MoveRegisterLogicalShiftRightImmediate( rd, rm, rs) => self.isa_move_register_logical_shift_right( rd, rm, rs),
+ MoveRegisterRotateRight( rd, rm, rs) => self.isa_move_register_rotate_right( rd, rm, rs),
Undefined => {},
};
diff --git a/src/luma/cpu/compare.rs b/src/luma/cpu/compare.rs
deleted file mode 100644
index 4aa3bc4..0000000
--- a/src/luma/cpu/compare.rs
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- 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::log;
-use crate::luma::cpu::Cpu;
-
-impl Cpu {
- pub(super) fn compare_immediate(&mut self, register: u8, immediate: u32) {
- log(&format!("cmp r{register}, {immediate:#X}"));
-
- let mut state = self.state.lock().unwrap();
-
- let register_value = state.read_register(register);
-
- let (temporary, c) = register_value.overflowing_sub(immediate);
-
- let v = false; // ???
- let z = temporary == 0x0;
- let n = temporary & 0b10000000000000000000000000000000 != 0x0;
-
- let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
- cpsr |= (v as u32) << 0x1C;
- cpsr |= (c as u32) << 0x1D;
- cpsr |= (z as u32) << 0x1E;
- cpsr |= (n as u32) << 0x1F;
- state.write_cpsr(cpsr);
- }
-
- pub(super) fn compare_register(&mut self, left: u8, right: u8) {
- log(&format!("cmp r{left}, r{right}"));
-
- let mut state = self.state.lock().unwrap();
-
- let left_value = state.read_register(left);
- let right_value = state.read_register(right);
-
- let (temporary, c) = left_value.overflowing_sub(right_value);
-
- let v = false; // ???
- let z = temporary == 0x0;
- let n = temporary & 0b10000000000000000000000000000000 != 0x0;
-
- let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
- cpsr |= (v as u32) << 0x1C;
- cpsr |= (c as u32) << 0x1D;
- cpsr |= (z as u32) << 0x1E;
- cpsr |= (n as u32) << 0x1F;
- state.write_cpsr(cpsr);
- }
-}
diff --git a/src/luma/cpu/decode_arm.rs b/src/luma/cpu/decode_arm.rs
index 9b4377c..2e7e882 100644
--- a/src/luma/cpu/decode_arm.rs
+++ b/src/luma/cpu/decode_arm.rs
@@ -37,7 +37,8 @@ impl Cpu {
drop(state);
- log(&format!("{opcode:#034b} @ {address:#010X} - ({})", self.cycle));
+ log!();
+ log!("\u{1B}[1m{opcode:032b}\u{1B}[0m @ \u{1B}[1m{address:08X}\u{1B}[0m - ({})", self.cycle);
return decode(address, opcode);
}
@@ -156,7 +157,7 @@ fn decode(address: u32, opcode: u32) -> Instruction {
0b001 => {
let immediate = (opcode & 0b00000000000000000000000011111111) as u8;
- let rotate = (opcode & 0b00000000000000000000111100000000).wrapping_shr(0x8) as u8;
+ let rotate = (opcode & 0b00000000000000000000111100000000).wrapping_shr(0x8);
let destination = (opcode & 0b00000000000000001111000000000000).wrapping_shr(0xC) as u8;
@@ -164,7 +165,8 @@ fn decode(address: u32, opcode: u32) -> Instruction {
let _s = opcode & 0b00000000000100000000000000000000 != 0x0;
- let immediate = (immediate as u32).rotate_right(rotate as u32);
+ let rotate = rotate << 0x1;
+ let immediate = (immediate as u32).rotate_right(rotate);
match (opcode & 0b00000001111000000000000000000000).wrapping_shr(0x15) {
0b0000 => {},
diff --git a/src/luma/cpu/decode_thumb.rs b/src/luma/cpu/decode_thumb.rs
index c8eacb8..43cbcfa 100644
--- a/src/luma/cpu/decode_thumb.rs
+++ b/src/luma/cpu/decode_thumb.rs
@@ -40,7 +40,8 @@ impl Cpu {
drop(state);
- log(&format!("{opcode:#018b} @ {address:#010X} - ({})", self.cycle));
+ log!();
+ log!(" \u{1B}[1m{opcode:016b}\u{1B}[0m @ \u{1B}[1m{address:08X}\u{1B}[0m - ({})", self.cycle);
match (opcode & 0b1110000000000000).wrapping_shr(0xD) {
0b000 => {
@@ -115,42 +116,42 @@ impl Cpu {
0b0 => {
match (opcode & 0b0000010000000000).wrapping_shr(0xA) {
0b0 => {
- let destination = (opcode & 0b0000000000000111) as u8;
+ let left = (opcode & 0b0000000000000111) as u8;
- let base = (opcode & 0b0000000000111000).wrapping_shr(0x3) as u8;
+ let right = (opcode & 0b0000000000111000).wrapping_shr(0x3) as u8;
match (opcode & 0b0000001111000000).wrapping_shr(0x6) {
- 0b0000 => {},
+ 0b0000 => return AndRegister(left, left, right),
- 0b0001 => {},
+ 0b0001 => return ExclusiveOrRegister(left, left, right),
- 0b0010 => {},
+ 0b0010 => return MoveRegisterLogicalShiftLeftImmediate(left, left, right),
- 0b0011 => {},
+ 0b0011 => return MoveRegisterLogicalShiftRightImmediate(left, left, right),
- 0b0100 => {},
+ 0b0100 => return MoveRegisterArithmeticShiftRight(left, left, right),
- 0b0101 => {},
+ 0b0101 => return AddCarryRegister(left, left, right),
- 0b0110 => {},
+ 0b0110 => return SubtractCarryRegister(left, left, right),
- 0b0111 => {},
+ 0b0111 => return MoveRegisterRotateRight(left, left, right),
- 0b1000 => {},
+ 0b1000 => return TestRegister(left, right),
- 0b1001 => {},
+ 0b1001 => return ReverseSubtractImmediate(left, right, 0x0),
- 0b1010 => return CompareRegister(destination, base),
+ 0b1010 => return CompareRegister(left, right),
- 0b1011 => {},
+ 0b1011 => return CompareNegativeRegister(left, right),
- 0b1100 => {},
+ 0b1100 => return LogicalOrRegister(left, right, right),
- 0b1101 => {},
+ 0b1101 => return MultiplyRegister(left, left, right),
- 0b1110 => {},
+ 0b1110 => return BitClearRegister(left, left, right),
- 0b1111 => {},
+ 0b1111 => return MoveNotRegister(left, right),
_ => unsafe { unreachable_unchecked() },
}
diff --git a/src/luma/cpu/isa_arithmetic.rs b/src/luma/cpu/isa_arithmetic.rs
new file mode 100644
index 0000000..3fe8830
--- /dev/null
+++ b/src/luma/cpu/isa_arithmetic.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::luma::log;
+use crate::luma::cpu::Cpu;
+
+impl Cpu {
+ pub(super) fn isa_add_carry_register(&mut self, rd: u8, rn: u8, rm: u8) {
+ log!("adc r{rd}, r{rn}, r{rm}");
+
+ let mut state = self.state.lock().unwrap();
+
+ let rn_value = state.read_register(rn);
+ let rm_value = state.read_register(rm);
+
+ let (mut value, c) = rn_value.overflowing_add(rm_value);
+ value += c as u32;
+ state.write_register(rd, value);
+ }
+
+ pub(super) fn isa_add_immediate(&mut self, rd: u8, rn: u8, imm: u32) {
+ log!("add r{rd}, r{rn}, {imm:#X}");
+
+ let mut state = self.state.lock().unwrap();
+
+ let rn_value = state.read_register(rn);
+
+ let value = rn_value.wrapping_add(imm);
+ state.write_register(rd, value);
+ }
+
+ pub(super) fn isa_add_register(&mut self, rd: u8, rn: u8, rm: u8) {
+ log!("add r{rd}, r{rn}, r{rm}");
+
+ let mut state = self.state.lock().unwrap();
+
+ let rn_value = state.read_register(rn);
+ let rm_value = state.read_register(rm);
+
+ let value = rn_value.wrapping_add(rm_value);
+ state.write_register(rd, value);
+ }
+
+ pub(super) fn isa_multiply_register(&mut self, rd: u8, rm: u8, rs: u8) {
+ log!("mul r{rd}, r{rm}, r{rs}");
+
+ let mut state = self.state.lock().unwrap();
+
+ let rm_value = state.read_register(rm);
+ let rs_value = state.read_register(rs);
+
+ let result = rm_value.wrapping_mul(rs_value);
+ state.write_register(rd, result);
+ }
+
+ pub(super) fn isa_reverse_subtract_immediate(&mut self, rd: u8, rm: u8, imm: u32) {
+ log!("rsb r{rd}, r{rm}, {imm:#X}");
+
+ let mut state = self.state.lock().unwrap();
+
+ let rm_value = state.read_register(rm);
+
+ let result = imm.wrapping_sub(rm_value);
+ state.write_register(rd, result);
+ }
+
+ pub(super) fn isa_subtract_carry_register(&mut self, rd: u8, rn: u8, rm: u8) {
+ log!("sbc r{rd}, r{rn}, r{rm}");
+
+ let mut state = self.state.lock().unwrap();
+
+ let rn_value = state.read_register(rn);
+ let rm_value = state.read_register(rm);
+
+ let (mut value, c) = rn_value.overflowing_sub(rm_value);
+ value += (!c) as u32;
+ state.write_register(rd, value);
+ }
+
+ pub(super) fn isa_subtract_immediate(&mut self, rd: u8, rn: u8, imm: u32) {
+ log!("sub r{rd}, r{rn}, {imm:#X}");
+
+ let mut state = self.state.lock().unwrap();
+
+ let rn_value = state.read_register(rn);
+
+ let value = rn_value.wrapping_sub(imm);
+ state.write_register(rd, value);
+ }
+
+ pub(super) fn isa_subtract_register(&mut self, rd: u8, rn: u8, rm: u8) {
+ log!("sub r{rd}, r{rn}, r{rm}");
+
+ let mut state = self.state.lock().unwrap();
+
+ let rn_value = state.read_register(rn);
+ let rm_value = state.read_register(rm);
+
+ let value = rn_value.wrapping_sub(rm_value);
+ state.write_register(rd, value);
+ }
+}
diff --git a/src/luma/cpu/isa_bitwise.rs b/src/luma/cpu/isa_bitwise.rs
new file mode 100644
index 0000000..f2ed822
--- /dev/null
+++ b/src/luma/cpu/isa_bitwise.rs
@@ -0,0 +1,75 @@
+/*
+ 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::log;
+use crate::luma::cpu::Cpu;
+
+impl Cpu {
+ pub(super) fn isa_and_register(&mut self, rd: u8, rn: u8, rm: u8) {
+ log!("and r{rd}, r{rn}, r{rm}");
+
+ let mut state = self.state.lock().unwrap();
+
+ let rn_value = state.read_register(rn);
+ let rm_value = state.read_register(rm);
+
+ let result = rn_value & rm_value;
+ state.write_register(rd, result);
+ }
+
+ pub(super) fn isa_bit_clear_register(&mut self, rd: u8, rn: u8, rm: u8) {
+ log!("bic r{rd}, r{rn}, r{rm}");
+
+ let mut state = self.state.lock().unwrap();
+
+ let rn_value = state.read_register(rn);
+ let rm_value = state.read_register(rm);
+
+ let result = rn_value & !rm_value;
+ state.write_register(rd, result);
+ }
+
+ pub(super) fn isa_exclusive_or_register(&mut self, rd: u8, rn: u8, rm: u8) {
+ log!("eor r{rd}, r{rn}, r{rm}");
+
+ let mut state = self.state.lock().unwrap();
+
+ let rn_value = state.read_register(rn);
+ let rm_value = state.read_register(rm);
+
+ let result = rn_value ^ rm_value;
+ state.write_register(rd, result);
+ }
+
+ pub(super) fn isa_logical_or_register(&mut self, rd: u8, rn: u8, rm: u8) {
+ log!("eor r{rd}, r{rn}, r{rm}");
+
+ let mut state = self.state.lock().unwrap();
+
+ let rn_value = state.read_register(rn);
+ let rm_value = state.read_register(rm);
+
+ let result = rn_value | rm_value;
+ state.write_register(rd, result);
+ }
+}
diff --git a/src/luma/cpu/branch.rs b/src/luma/cpu/isa_branch.rs
index a29205d..a887a35 100644
--- a/src/luma/cpu/branch.rs
+++ b/src/luma/cpu/isa_branch.rs
@@ -25,23 +25,23 @@ use crate::luma::log;
use crate::luma::cpu::{Cpu, exchange};
impl Cpu {
- pub(super) fn branch(&mut self, offset: i32) {
+ pub(super) fn isa_branch(&mut self, imm: i32) {
let mut state = self.state.lock().unwrap();
- let mut target = state.read_register(0xF).wrapping_add_signed(offset);
+ let mut target = state.read_register(0xF).wrapping_add_signed(imm);
- log(&format!("b {target:#X}"));
+ log!("b {target:#X}");
target = target.wrapping_add(self.instruction_size);
state.write_register(0xF, target);
}
- pub(super) fn branch_exchange(&mut self, source: u8) {
+ pub(super) fn isa_branch_exchange(&mut self, rm: u8) {
let mut state = self.state.lock().unwrap();
- log(&format!("bx r{source}"));
+ log!("bx r{rm}");
- let mut target = state.read_register(source);
+ let mut target = state.read_register(rm);
let t = target & 0b00000000000000000000000000000001 != 0x0;
exchange!(self, t);
@@ -54,32 +54,32 @@ impl Cpu {
state.write_register(0xF, target);
}
- pub(super) fn branch_link(&mut self, offset: i32) {
+ pub(super) fn isa_branch_link(&mut self, imm: i32) {
let mut state = self.state.lock().unwrap();
- let mut target = state.read_register(0xF).wrapping_add_signed(offset);
+ let mut target = state.read_register(0xF).wrapping_add_signed(imm);
- log(&format!("bl {target:#X}"));
+ log!("bl {target:#X}");
target = target.wrapping_add(self.instruction_size);
state.write_register(0xF, target);
}
- pub(super) fn branch_link_prefix(&mut self, offset: i32) {
+ pub(super) fn isa_branch_link_prefix(&mut self, imm: i32) {
let mut state = self.state.lock().unwrap();
- let target = state.read_register(0xF).wrapping_add_signed(offset);
+ let target = state.read_register(0xF).wrapping_add_signed(imm);
state.write_register(0xE, target);
}
- pub(super) fn branch_link_suffix(&mut self, offset: i32) {
+ pub(super) fn isa_branch_link_suffix(&mut self, imm: i32) {
let mut state = self.state.lock().unwrap();
- let mut branch_target = state.read_register(0xE).wrapping_add_signed(offset);
+ let mut branch_target = state.read_register(0xE).wrapping_add_signed(imm);
let link_target = state.read_register(0xF).wrapping_sub(0x2);
- log(&format!("bl {branch_target:#X}"));
+ log!("bl {branch_target:#X}");
state.write_register(0xE, link_target);
diff --git a/src/luma/cpu/isa_logic.rs b/src/luma/cpu/isa_logic.rs
new file mode 100644
index 0000000..a624f57
--- /dev/null
+++ b/src/luma/cpu/isa_logic.rs
@@ -0,0 +1,111 @@
+/*
+ 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::log;
+use crate::luma::cpu::Cpu;
+
+impl Cpu {
+ pub(super) fn isa_compare_immediate(&mut self, rn: u8, imm: u32) {
+ log!("cmp r{rn}, {imm:#X}");
+
+ let mut state = self.state.lock().unwrap();
+
+ let rn_value = state.read_register(rn);
+
+ let (temporary, c) = rn_value.overflowing_sub(imm);
+
+ let v = false; // ???
+ let z = temporary == 0x0;
+ let n = temporary & 0b10000000000000000000000000000000 != 0x0;
+
+ let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
+ cpsr |= (v as u32) << 0x1C;
+ cpsr |= (c as u32) << 0x1D;
+ cpsr |= (z as u32) << 0x1E;
+ cpsr |= (n as u32) << 0x1F;
+ state.write_cpsr(cpsr);
+ }
+
+ pub(super) fn isa_compare_negative_register(&mut self, rn: u8, rm: u8) {
+ log!("cmn r{rn}, r{rm}");
+
+ let mut state = self.state.lock().unwrap();
+
+ let rn_value = state.read_register(rn);
+ let rm_value = state.read_register(rm);
+
+ let (temporary, c) = rn_value.overflowing_add(rm_value);
+
+ let v = false; // ???
+ let z = temporary == 0x0;
+ let n = temporary & 0b10000000000000000000000000000000 != 0x0;
+
+ let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
+ cpsr |= (v as u32) << 0x1C;
+ cpsr |= (c as u32) << 0x1D;
+ cpsr |= (z as u32) << 0x1E;
+ cpsr |= (n as u32) << 0x1F;
+ state.write_cpsr(cpsr);
+ }
+
+ pub(super) fn isa_compare_register(&mut self, rn: u8, rm: u8) {
+ log!("cmp r{rn}, r{rm}");
+
+ let mut state = self.state.lock().unwrap();
+
+ let rn_value = state.read_register(rn);
+ let rm_value = state.read_register(rm);
+
+ let (temporary, c) = rn_value.overflowing_sub(rm_value);
+
+ let v = false; // ???
+ let z = temporary == 0x0;
+ let n = temporary & 0b10000000000000000000000000000000 != 0x0;
+
+ let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
+ cpsr |= (v as u32) << 0x1C;
+ cpsr |= (c as u32) << 0x1D;
+ cpsr |= (z as u32) << 0x1E;
+ cpsr |= (n as u32) << 0x1F;
+ state.write_cpsr(cpsr);
+ }
+
+ pub(super) fn isa_test_register(&mut self, rn: u8, rm: u8) {
+ log!("tst r{rn}, r{rm}");
+
+ let mut state = self.state.lock().unwrap();
+
+ let rn_value = state.read_register(rn);
+ let rm_value = state.read_register(rm);
+
+ let temporary = rn_value & !rm_value;
+
+ let z = temporary == 0x0;
+ let n = temporary & 0b10000000000000000000000000000000 != 0x0;
+
+ let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
+ cpsr |= (z as u32) << 0x1E;
+ cpsr |= (n as u32) << 0x1F;
+ state.write_cpsr(cpsr);
+ }
+}
diff --git a/src/luma/cpu/isa_memory.rs b/src/luma/cpu/isa_memory.rs
new file mode 100644
index 0000000..8b953c0
--- /dev/null
+++ b/src/luma/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::luma::log;
+use crate::luma::cpu::Cpu;
+
+impl Cpu {
+ pub(super) fn isa_load_halfword(&mut self, rd: u8, rn: u8, imm: i16) {
+ let mut state = self.state.lock().unwrap();
+
+ 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) {
+ let mut state = self.state.lock().unwrap();
+
+ log!("ldr 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_word(target);
+ state.write_register(rd, result);
+ }
+
+ pub(super) fn isa_load_pc(&mut self, rd: u8, imm: i16) {
+ // Slightly different from load_immediate_offset
+ // due to the target being forced word-aligned.
+
+ let mut state = self.state.lock().unwrap();
+
+ log!("ldr r{rd}, [pc, {imm:#X}]");
+
+ let rn_value = state.read_register(0xF) & 0b11111111111111111111111111111100;
+
+ let target = rn_value.wrapping_add_signed(imm as i32);
+
+ 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}]");
+
+ let mut state = self.state.lock().unwrap();
+
+ 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}]");
+
+ let mut state = self.state.lock().unwrap();
+
+ 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}]");
+
+ let mut state = self.state.lock().unwrap();
+
+ 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}]");
+
+ let mut state = self.state.lock().unwrap();
+
+ 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/luma/cpu/move.rs b/src/luma/cpu/isa_move.rs
index 8389b16..a9edac8 100644
--- a/src/luma/cpu/move.rs
+++ b/src/luma/cpu/isa_move.rs
@@ -25,58 +25,69 @@ use crate::luma::log;
use crate::luma::cpu::Cpu;
impl Cpu {
- pub(super) fn move_immediate(&mut self, destination: u8, immediate: u32) {
- log(&format!("mov r{destination}, {immediate:#X}"));
+ pub(super) fn isa_move_immediate(&mut self, rd: u8, immediate: u32) {
+ log!("mov r{rd}, {immediate:#X}");
let mut state = self.state.lock().unwrap();
- state.write_register(destination, immediate);
+ state.write_register(rd, immediate);
}
- pub(super) fn move_immediate_arithmetic_shift_right(&mut self, destination: u8, base: u8, shift: u8) {
- log(&format!("mov r{destination}, r{base}, ASR {shift:#X}"));
+ pub(super) fn isa_move_immediate_arithmetic_shift_right(&mut self, rd: u8, base: u8, shift: u8) {
+ log!("mov r{rd}, r{base}, ASR {shift:#X}");
let mut state = self.state.lock().unwrap();
let base_value = state.read_register(base);
let value = (base_value as i32).wrapping_shr(shift as u32) as u32;
- state.write_register(destination, value);
+ state.write_register(rd, value);
}
- pub(super) fn move_immediate_logical_shift_left(&mut self, destination: u8, base: u8, shift: u8) {
- log(&format!("mov r{destination}, r{base}, LSL {shift:#X}"));
+ pub(super) fn isa_move_immediate_logical_shift_left(&mut self, rd: u8, base: u8, shift: u8) {
+ log!("mov r{rd}, r{base}, LSL {shift:#X}");
let mut state = self.state.lock().unwrap();
let base_value = state.read_register(base);
let value = base_value.wrapping_shl(shift as u32);
- state.write_register(destination, value);
+ state.write_register(rd, value);
}
- pub(super) fn move_immediate_logical_shift_right(&mut self, destination: u8, base: u8, shift: u8) {
- log(&format!("mov r{destination}, r{base}, LSR {shift:#X}"));
+ pub(super) fn isa_move_immediate_logical_shift_right(&mut self, rd: u8, base: u8, shift: u8) {
+ log!("mov r{rd}, r{base}, LSR {shift:#X}");
let mut state = self.state.lock().unwrap();
let base_value = state.read_register(base);
let value = base_value.wrapping_shr(shift as u32);
- state.write_register(destination, value);
+ state.write_register(rd, value);
}
- pub(super) fn move_register(&mut self, destination: u8, source: u8) {
- log(&format!("mov r{destination}, r{source}"));
+ pub(super) fn isa_move_not_register(&mut self, rd: u8, rm: u8) {
+ log!("mvn r{rd}, r{rm}");
+
+ let mut state = self.state.lock().unwrap();
+
+ let rm_value = state.read_register(rm);
+
+ let result = !rm_value;
+ state.write_register(rd, result);
+ }
+
+ pub(super) fn isa_move_register(&mut self, rd: u8, source: u8) {
+ log!("mov r{rd}, r{source}");
let mut state = self.state.lock().unwrap();
let value = state.read_register(source);
- state.write_register(destination, value);
+ state.write_register(rd, value);
}
- pub(super) fn move_register_arithmetic_shift_right(&mut self, destination: u8, base: u8, shift: u8) {
- log(&format!("mov r{destination}, r{base}, ASR r{shift}"));
+ pub(super) fn isa_move_register_arithmetic_shift_right(&mut self, rd: u8, base: u8, shift: u8) {
+ log!("mov r{rd}, r{base}, ASR r{shift}");
let mut state = self.state.lock().unwrap();
@@ -84,11 +95,11 @@ impl Cpu {
let shift_value = state.read_register(shift);
let value = (base_value.wrapping_shr(shift_value)) as u32;
- state.write_register(destination, value);
+ state.write_register(rd, value);
}
- pub(super) fn move_register_logical_shift_left(&mut self, destination: u8, base: u8, shift: u8) {
- log(&format!("mov r{destination}, r{base}, LSL r{shift}"));
+ pub(super) fn isa_move_register_logical_shift_left(&mut self, rd: u8, base: u8, shift: u8) {
+ log!("mov r{rd}, r{base}, LSL r{shift}");
let mut state = self.state.lock().unwrap();
@@ -96,11 +107,11 @@ impl Cpu {
let shift_value = state.read_register(shift);
let value = base_value.wrapping_shl(shift_value);
- state.write_register(destination, value);
+ state.write_register(rd, value);
}
- pub(super) fn move_register_logical_shift_right(&mut self, destination: u8, base: u8, shift: u8) {
- log(&format!("mov r{destination}, r{base}, LSR r{shift}"));
+ pub(super) fn isa_move_register_logical_shift_right(&mut self, rd: u8, base: u8, shift: u8) {
+ log!("mov r{rd}, r{base}, LSR r{shift}");
let mut state = self.state.lock().unwrap();
@@ -108,6 +119,18 @@ impl Cpu {
let shift_value = state.read_register(shift);
let value = base_value.wrapping_shr(shift_value);
- state.write_register(destination, value);
+ state.write_register(rd, value);
+ }
+
+ pub(super) fn isa_move_register_rotate_right(&mut self, rd: u8, base: u8, rotate: u8) {
+ log!("mov r{rd}, r{base}, ROR r{rotate}");
+
+ let mut state = self.state.lock().unwrap();
+
+ let base_value = state.read_register(base);
+ let rotate_value = state.read_register(rotate);
+
+ let value = base_value.rotate_right(rotate_value);
+ state.write_register(rd, value);
}
}
diff --git a/src/luma/cpu/load.rs b/src/luma/cpu/load.rs
deleted file mode 100644
index 39a6e49..0000000
--- a/src/luma/cpu/load.rs
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- 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::log;
-use crate::luma::cpu::Cpu;
-
-impl Cpu {
- pub(super) fn load_halfword(&mut self, destination: u8, base: u8, offset: i16) {
- let mut state = self.state.lock().unwrap();
-
- log(&format!("ldrh r{destination}, [r{base}, {offset:#X}]"));
-
- let base_value = state.read_register(base);
-
- let target = base_value.wrapping_add_signed(offset as i32);
-
- let value = state.read_halfword(target) as u32;
- state.write_register(destination, value);
- }
-
- pub(super) fn load_immediate_offset(&mut self, destination: u8, base: u8, offset: i16) {
- let mut state = self.state.lock().unwrap();
-
- log(&format!("ldr r{destination}, [r{base}, {offset:#X}]"));
-
- let base_value = state.read_register(base);
-
- let target = base_value.wrapping_add_signed(offset as i32);
-
- let value = state.read_word(target);
- state.write_register(destination, value);
- }
-
- pub(super) fn load_pc(&mut self, destination: u8, offset: i16) {
- // Slightly different from load_immediate_offset
- // due to the target being forced word-aligned.
-
- let mut state = self.state.lock().unwrap();
-
- log(&format!("ldr r{destination}, [pc, {offset:#X}]"));
-
- let base_value = state.read_register(0xF) & 0b11111111111111111111111111111100;
-
- let target = base_value.wrapping_add_signed(offset as i32);
-
- let value = state.read_word(target);
- state.write_register(destination, value);
- }
-}
diff --git a/src/luma/cpu/store.rs b/src/luma/cpu/store.rs
deleted file mode 100644
index 6f073ab..0000000
--- a/src/luma/cpu/store.rs
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- 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::log;
-use crate::luma::cpu::Cpu;
-
-impl Cpu {
- pub(super) fn store_byte_immediate_offset(&mut self, source: u8, base: u8, offset: i16) {
- log(&format!("strb r{source}, [r{base}, {offset:#X}]"));
-
- let mut state = self.state.lock().unwrap();
-
- let base_value = state.read_register(base);
-
- let target = base_value.wrapping_add_signed(offset as i32);
-
- let value = state.read_register(source) as u8;
- state.write_byte(target, value);
- }
-
- pub(super) fn store_byte_register_offset(&mut self, source: u8, base: u8, offset: u8) {
- log(&format!("strb r{source}, [r{base}, r{offset}]"));
-
- let mut state = self.state.lock().unwrap();
-
- let base_value = state.read_register(base);
- let offset_value = state.read_register(offset);
-
- let target = base_value.wrapping_add(offset_value);
-
- let value = state.read_register(source) as u8;
- state.write_byte(target, value);
- }
-
- pub(super) fn store_halfword(&mut self, source: u8, base: u8, offset: i16) {
- log(&format!("strh r{source}, [r{base}, {offset:#X}]"));
-
- let mut state = self.state.lock().unwrap();
-
- let base_value = state.read_register(base);
-
- let target = base_value.wrapping_add_signed(offset as i32);
-
- let value = state.read_register(source) as u16;
- state.write_halfword(target, value);
- }
-
- pub(super) fn store_immediate_offset(&mut self, source: u8, base: u8, offset: i16) {
- log(&format!("str r{source}, [r{base}, {offset:#X}]"));
-
- let mut state = self.state.lock().unwrap();
-
- let base_value = state.read_register(base);
-
- let target = base_value.wrapping_add_signed(offset as i32);
-
- let value = state.read_register(source);
- state.write_word(target, value);
- }
-}
diff --git a/src/luma/cpu/subtract.rs b/src/luma/cpu/subtract.rs
deleted file mode 100644
index 59267ca..0000000
--- a/src/luma/cpu/subtract.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- 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::log;
-use crate::luma::cpu::Cpu;
-
-impl Cpu {
- pub(super) fn subtract_immediate(&mut self, destination: u8, base: u8, immediate: u32) {
- log(&format!("sub r{destination}, r{base}, {immediate:#X}"));
-
- let mut state = self.state.lock().unwrap();
-
- let base_value = state.read_register(base);
-
- let value = base_value.wrapping_sub(immediate);
- state.write_register(destination, value);
- }
-
- pub(super) fn subtract_register(&mut self, destination: u8, base: u8, subtract: u8) {
- log(&format!("sub r{destination}, r{base}, r{subtract}"));
-
- let mut state = self.state.lock().unwrap();
-
- let base_value = state.read_register(base);
- let subtract_value = state.read_register(subtract);
-
- let value = base_value.wrapping_sub(subtract_value);
- state.write_register(destination, value);
- }
-}
diff --git a/src/luma/cpu_handle.rs b/src/luma/cpu_handle.rs
index f83ddd2..ceacd03 100644
--- a/src/luma/cpu_handle.rs
+++ b/src/luma/cpu_handle.rs
@@ -27,15 +27,15 @@ use std::sync::{Arc, Mutex};
use std::sync::atomic::AtomicBool;
use std::thread::JoinHandle;
+pub mod drop;
pub mod dump_video;
pub mod dump_palette;
-pub mod kill;
pub struct CpuHandle {
state: Arc<Mutex<State>>,
dead: Arc<AtomicBool>,
- handle: JoinHandle<()>,
+ handle: Option<JoinHandle<()>>,
}
impl CpuHandle {
@@ -49,7 +49,7 @@ impl CpuHandle {
state: state,
dead: dead,
- handle: handle,
+ handle: Some(handle),
};
}
}
diff --git a/src/luma/cpu_handle/kill.rs b/src/luma/cpu_handle/drop.rs
index e89affa..52c2866 100644
--- a/src/luma/cpu_handle/kill.rs
+++ b/src/luma/cpu_handle/drop.rs
@@ -25,16 +25,13 @@ use crate::luma::cpu_handle::CpuHandle;
use std::sync::atomic::Ordering;
-impl CpuHandle {
- #[must_use]
- pub fn kill(self) -> Result<(), String> {
+impl Drop for CpuHandle {
+ fn drop(&mut self) {
eprintln!("got kill order");
- self.dead.store(true, Ordering::Relaxed);
- self.handle.join().unwrap();
-
- self.dead.store(false, Ordering::Relaxed);
+ let handle = self.handle.take().unwrap();
- return Ok(());
+ self.dead.store(true, Ordering::Relaxed);
+ handle.join().unwrap();
}
}
diff --git a/src/luma/instruction.rs b/src/luma/instruction.rs
index 96aa288..9de3d32 100644
--- a/src/luma/instruction.rs
+++ b/src/luma/instruction.rs
@@ -23,32 +23,44 @@
#[derive(Clone, Copy)]
pub enum Instruction {
+ AddCarryRegister( u8, u8, u8),
AddImmediate( u8, u8, u32),
AddRegister( u8, u8, u8),
+ AndRegister( u8, u8, u8),
+ BitClearRegister( u8, u8, u8),
Branch( i32),
BranchExchange( u8),
BranchLink( i32),
BranchLinkPrefix( i32),
BranchLinkSuffix( i32),
CompareImmediate( u8, u32),
+ CompareNegativeRegister( u8, u8),
CompareRegister( u8, u8),
+ ExclusiveOrRegister( u8, u8, u8),
LoadHalfword( u8, u8, i16),
LoadImmediateOffset( u8, u8, i16),
LoadPc( u8, i16),
+ LogicalOrRegister( u8, u8, u8),
MoveImmediate( u8, u32),
MoveImmediateArithmeticShiftRight( u8, u8, u8),
MoveImmediateLogicalShiftLeftImmediate( u8, u8, u8),
MoveImmediateLogicalShiftRightImmediate(u8, u8, u8),
+ MoveNotRegister( u8, u8),
MoveRegister( u8, u8),
MoveRegisterArithmeticShiftRight( u8, u8, u8),
MoveRegisterLogicalShiftLeftImmediate( u8, u8, u8),
MoveRegisterLogicalShiftRightImmediate( u8, u8, u8),
+ MoveRegisterRotateRight( u8, u8, u8),
+ MultiplyRegister( u8, u8, u8),
+ ReverseSubtractImmediate( u8, u8, u32),
StoreByteImmediateOffset( u8, u8, i16),
StoreByteRegisterOffset( u8, u8, u8),
StoreHalfword( u8, u8, i16),
StoreImmediateOffset( u8, u8, i16),
+ SubtractCarryRegister( u8, u8, u8),
SubtractImmediate( u8, u8, u32),
SubtractRegister( u8, u8, u8),
+ TestRegister( u8, u8),
Undefined,
}
diff --git a/src/luma/state.rs b/src/luma/state.rs
index 1836573..1b293ef 100644
--- a/src/luma/state.rs
+++ b/src/luma/state.rs
@@ -21,12 +21,13 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::luma::{Error, log};
-use crate::luma::{BOOTLOADER_LENGTH, IMAGE_LENGTH, MEMORY_LENGTH, PALETTE_LENGTH, VIDEO_LENGTH};
+use crate::luma::{BOOTLOADER_LENGTH, IMAGE_LENGTH, PALETTE_LENGTH, VIDEO_LENGTH};
use std::slice::{from_raw_parts, from_raw_parts_mut};
pub mod new;
+pub mod read;
+pub mod write;
pub struct State {
registers: [u32; 0x10],
@@ -35,105 +36,23 @@ pub struct State {
memory: Vec::<u32>,
}
-impl State {
- #[inline(always)]
- #[must_use]
- pub fn read_register(&self, register: u8) -> u32 {
- // Limit to 0..=15.
- let index = (register & 0b00001111) as usize;
-
- return unsafe { *self.registers.get_unchecked(index) };
- }
-
- #[inline(always)]
- pub fn write_register(&mut self, register: u8, value: u32) {
- log(&format!("* r{register} = {value:#010X}"));
-
- let index = (register & 0b00001111) as usize;
-
- unsafe { *self.registers.get_unchecked_mut(index) = value };
- }
-
- #[must_use]
- pub fn read_word(&self, address: u32) -> u32 {
- if address > MEMORY_LENGTH - 0x4 { Error::OutOfBounds( address).trap(); return 0x00000000; }
- if address % 0x4 != 0x0 { Error::BadAlignment(address, 0x4).trap(); return 0x00000000; }
-
- unsafe {
- let pointer = (self.memory.as_ptr() as *const u8).add(address as usize) as *const u32;
- return *pointer;
+macro_rules! address_unused {
+ ($address: expr) => {{
+ match $address {
+ 0x02040000..=0x02FFFFFF => true,
+ 0x03008000..=0x03FFFFFF => true,
+ 0x04000400..=0x04FFFFFF => true,
+ 0x05000400..=0x05FFFFFF => true,
+ 0x06018000..=0x06FFFFFF => true,
+ 0x07000400..=0x07FFFFFF => true,
+
+ _ => false,
}
- }
-
- pub fn write_word(&mut self, address: u32, value: u32) {
- log(&format!("* {address:#010X} = {value:#010X}"));
-
- if address > MEMORY_LENGTH - 0x4 { Error::OutOfBounds( address).trap(); return; }
- if address % 0x4 != 0x0 { Error::BadAlignment(address, 0x4).trap(); return; }
-
- unsafe {
- let pointer = (self.memory.as_mut_ptr() as *mut u8).add(address as usize) as *mut u32;
- *pointer = value;
- }
- }
-
- #[must_use]
- pub fn read_halfword(&self, address: u32) -> u16 {
- if address > MEMORY_LENGTH - 0x2 { Error::OutOfBounds( address).trap(); return 0x0000; }
- if address % 0x2 != 0x0 { Error::BadAlignment(address, 0x2).trap(); return 0x0000; }
-
- unsafe {
- let pointer = (self.memory.as_ptr() as *const u8).add(address as usize) as *const u16;
- return *pointer;
- }
- }
-
- pub fn write_halfword(&mut self, address: u32, value: u16) {
- log(&format!("* {address:#010X} = {value:#010X}"));
-
- if address > MEMORY_LENGTH - 0x2 { Error::OutOfBounds( address).trap(); return; }
- if address % 0x2 != 0x0 { Error::BadAlignment(address, 0x2).trap(); return; }
-
- unsafe {
- let pointer = (self.memory.as_mut_ptr() as *mut u8).add(address as usize) as *mut u16;
- *pointer = value;
- }
- }
-
- #[must_use]
- pub fn read_byte(&self, address: u32) -> u8 {
- if address > MEMORY_LENGTH - 0x1 { Error::OutOfBounds(address).trap(); return 0x00; }
-
- unsafe {
- let pointer = (self.memory.as_ptr() as *const u8).add(address as usize);
- return *pointer;
- }
- }
-
- pub fn write_byte(&mut self, address: u32, value: u8) {
- log(&format!("* {address:#010X} = {value:#010X}"));
-
- if address > MEMORY_LENGTH - 0x1 { Error::OutOfBounds(address).trap(); return; }
-
- unsafe {
- let pointer = (self.memory.as_mut_ptr() as *mut u8).add(address as usize);
- *pointer = value;
- }
- }
-
- #[inline(always)]
- #[must_use]
- pub fn read_cpsr(&self) -> u32 {
- return self.cpsr;
- }
-
- #[inline(always)]
- pub fn write_cpsr(&mut self, value: u32) {
- log(&format!("* cpsr = {value:#034b}"));
-
- self.cpsr = value;
- }
+ }};
+}
+pub(crate) use address_unused;
+impl State {
#[must_use]
pub fn video8<'a>(&'a self) -> &'a [u8] {
let slice = unsafe {
diff --git a/src/luma/state/read.rs b/src/luma/state/read.rs
new file mode 100644
index 0000000..f22dc86
--- /dev/null
+++ b/src/luma/state/read.rs
@@ -0,0 +1,74 @@
+/*
+ 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::{Error, MEMORY_LENGTH};
+use crate::luma::state::State;
+
+impl State {
+ #[inline(always)]
+ #[must_use]
+ pub fn read_register(&self, register: u8) -> u32 {
+ // Limit to 0..=15.
+ let index = (register & 0b00001111) as usize;
+
+ return unsafe { *self.registers.get_unchecked(index) };
+ }
+
+ #[must_use]
+ pub fn read_word(&self, address: u32) -> u32 {
+ if address > MEMORY_LENGTH - 0x4 { Error::OutOfBounds( address).trap(); return 0x00000000; }
+ if address % 0x4 != 0x0 { Error::BadAlignment(address, 0x4).trap(); return 0x00000000; }
+
+ unsafe {
+ let pointer = (self.memory.as_ptr() as *const u8).add(address as usize) as *const u32;
+ return *pointer;
+ }
+ }
+
+ #[must_use]
+ pub fn read_halfword(&self, address: u32) -> u16 {
+ if address > MEMORY_LENGTH - 0x2 { Error::OutOfBounds( address).trap(); return 0x0000; }
+ if address % 0x2 != 0x0 { Error::BadAlignment(address, 0x2).trap(); return 0x0000; }
+
+ unsafe {
+ let pointer = (self.memory.as_ptr() as *const u8).add(address as usize) as *const u16;
+ return *pointer;
+ }
+ }
+
+ #[must_use]
+ pub fn read_byte(&self, address: u32) -> u8 {
+ if address > MEMORY_LENGTH - 0x1 { Error::OutOfBounds(address).trap(); return 0x00; }
+
+ unsafe {
+ let pointer = (self.memory.as_ptr() as *const u8).add(address as usize);
+ return *pointer;
+ }
+ }
+
+ #[inline(always)]
+ #[must_use]
+ pub fn read_cpsr(&self) -> u32 {
+ return self.cpsr;
+ }
+}
diff --git a/src/luma/state/write.rs b/src/luma/state/write.rs
new file mode 100644
index 0000000..bd2b031
--- /dev/null
+++ b/src/luma/state/write.rs
@@ -0,0 +1,116 @@
+/*
+ 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::{Error, log_assignment, MEMORY_LENGTH};
+use crate::luma::state::{address_unused, State};
+
+macro_rules! read_only {
+ ($address: expr) => {{
+ match $address {
+ 0x00000000..=0x00003FFF => true,
+ 0x04000130..=0x04000131 => true, // KEYINPUT
+ 0x08000000..=0x0DFFFFFF => true,
+
+ _ => false,
+ }
+ }};
+}
+
+impl State {
+ #[inline(always)]
+ pub fn write_register(&mut self, register: u8, value: u32) {
+ log_assignment!("r{register}", "{value:#010X}");
+
+ let index = (register & 0b00001111) as usize;
+
+ unsafe { *self.registers.get_unchecked_mut(index) = value };
+ }
+
+ pub fn write_word(&mut self, address: u32, value: u32) {
+ log_assignment!("{address:#010X}", "{value:#010X}");
+
+ if address > MEMORY_LENGTH - 0x4 { Error::OutOfBounds( address).trap(); return; }
+ if address % 0x4 != 0x0 { Error::BadAlignment(address, 0x4).trap(); return; }
+
+ if read_only!(address) || address_unused!(address) { return };
+
+ unsafe {
+ let pointer = (self.memory.as_mut_ptr() as *mut u8).add(address as usize) as *mut u32;
+ *pointer = value;
+ }
+ }
+
+ pub fn write_halfword(&mut self, address: u32, value: u16) {
+ log_assignment!("{address:#010X}", "{value:#06X}");
+
+ if address > MEMORY_LENGTH - 0x2 { Error::OutOfBounds( address).trap(); return; }
+ if address % 0x2 != 0x0 { Error::BadAlignment(address, 0x2).trap(); return; }
+
+ if read_only!(address) || address_unused!(address) { return };
+
+ unsafe {
+ let pointer = (self.memory.as_mut_ptr() as *mut u8).add(address as usize) as *mut u16;
+ *pointer = value;
+ }
+ }
+
+ pub fn write_byte(&mut self, address: u32, value: u8) {
+ log_assignment!("{address:#010X}", "{value:#04X}");
+
+ if address > MEMORY_LENGTH - 0x1 { Error::OutOfBounds(address).trap(); return; }
+
+ if read_only!(address) || address_unused!(address) { return };
+
+ let memory = self.memory.as_mut_ptr() as *mut u8;
+
+ match address {
+ // Extend to halfwords:
+ | 0x05000000..=0x050003FF
+ | 0x06000000..=0x06017FFF
+ | 0x07000000..=0x070003FF => {
+ // Align to halfwords.
+ let address = address & 0b11111111111111111111111111111110;
+
+ // Repeat value.
+ let value = value as u16 | (value as u16) << 0x8;
+
+ unsafe {
+ let pointer = memory.add(address as usize) as *mut u16;
+ *pointer = value;
+ }
+ },
+
+ // Bytes are allowed:
+ _ => unsafe {
+ let pointer = memory.add(address as usize);
+ *pointer = value;
+ },
+ };
+ }
+
+ pub fn write_cpsr(&mut self, value: u32) {
+ log_assignment!("cpsr", "{value:#034b}");
+
+ self.cpsr = value;
+ }
+}
diff --git a/test.s b/test.s
index 098647e..8497dab 100644
--- a/test.s
+++ b/test.s
@@ -111,23 +111,25 @@ start:
@ - r3 is the last pixel address.
ldr r0, .videoAddr
ldr r1, .paletteIndex
- movs r2, 0x0
+ bics r2, r2
movs r3, 0x4B
lsls r3, 0x9
adds r3, r0
@ Plot pixels:
+ @ TO-DO: Plot correctly (bytewise).
.loop:
strb r1, [r0]
adds r0, r2
adds r2, 0x1
cmp r0, r3
- bge .stop @ Stop if we've reached the end.
+ bge .restart
b .loop @ Repeat loop.
- @ Stop:
-.stop:
- b .stop
+ @ Restart loop:
+.restart:
+ ldr r0, .videoAddr
+ b .loop
.endfunc
.align