summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel Bjørnager Jensen2023-11-17 20:25:22 +0100
committerGabriel Bjørnager Jensen2023-11-17 20:25:22 +0100
commit08bfb19b97d1f3267bd7ec3a66476815de4e4752 (patch)
tree1d1dd598c7957e032fb20b32fed4b4baad3463ea
parent56e743845f814239b63ddb438d406d5f717badf1 (diff)
Add logo; Implement exceptions (including banked registers); Implement software interrupts; Add custom bootloader; Update readme; Support condition-setting instructions; Implement all data-processing instructions; Support conditional execution (predicates); Rework shifter instructions; Rework pipeline; Rework file structure; Update gitignore; Update naming convention;HEAD0.47master
-rw-r--r--.gitignore3
-rw-r--r--CHANGELOG.md16
-rw-r--r--Cargo.toml4
-rw-r--r--README.md2
-rw-r--r--bootloader.ld12
-rw-r--r--bootloader.s80
-rw-r--r--luma.svg27
-rwxr-xr-xmake_test.sh12
-rw-r--r--src/agb.rs (renamed from src/luma/agb.rs)0
-rw-r--r--src/app.rs (renamed from src/luma/app.rs)1
-rw-r--r--src/app/check_events.rs (renamed from src/luma/app/check_events.rs)2
-rw-r--r--src/app/draw_video.rs (renamed from src/luma/app/draw_video.rs)4
-rw-r--r--src/app/init.rs (renamed from src/luma/app/init.rs)6
-rw-r--r--src/app/load.rs (renamed from src/luma/app/load.rs)6
-rw-r--r--src/app/main.rs69
-rw-r--r--src/app/run.rs (renamed from src/luma/app/run.rs)8
-rw-r--r--src/app/sync_video.rs (renamed from src/luma/app/sync_video.rs)2
-rw-r--r--src/configuration.rs (renamed from src/luma/configuration.rs)4
-rw-r--r--src/configuration/load.rs (renamed from src/luma/configuration/load.rs)2
-rw-r--r--src/configuration/validate.rs (renamed from src/luma/configuration/validate.rs)2
-rw-r--r--src/cpu.rs (renamed from src/luma/cpu.rs)64
-rw-r--r--src/cpu/alu_exit_exception.rs38
-rw-r--r--src/cpu/boot.rs70
-rw-r--r--src/cpu/continue.rs (renamed from src/luma/cpu/continue.rs)4
-rw-r--r--src/cpu/enter_exception.rs59
-rw-r--r--src/cpu/exchange.rs (renamed from src/luma/cpu/exchange.rs)20
-rw-r--r--src/cpu/execute.rs69
-rw-r--r--src/cpu/exit_exception.rs42
-rw-r--r--src/cpu/fetch_arm.rs55
-rw-r--r--src/cpu/fetch_thumb.rs54
-rw-r--r--src/cpu/isa_adc.rs58
-rw-r--r--src/cpu/isa_add.rs55
-rw-r--r--src/cpu/isa_and.rs53
-rw-r--r--src/cpu/isa_b.rs38
-rw-r--r--src/cpu/isa_bic.rs53
-rw-r--r--src/cpu/isa_bl.rs62
-rw-r--r--src/cpu/isa_bx.rs45
-rw-r--r--src/cpu/isa_cmn.rs47
-rw-r--r--src/cpu/isa_cmp.rs47
-rw-r--r--src/cpu/isa_eor.rs53
-rw-r--r--src/cpu/isa_memory.rs (renamed from src/luma/cpu/isa_memory.rs)38
-rw-r--r--src/cpu/isa_mov.rs53
-rw-r--r--src/cpu/isa_mul.rs52
-rw-r--r--src/cpu/isa_mvn.rs52
-rw-r--r--src/cpu/isa_orr.rs53
-rw-r--r--src/cpu/isa_rsb.rs55
-rw-r--r--src/cpu/isa_rsc.rs58
-rw-r--r--src/cpu/isa_sbc.rs58
-rw-r--r--src/cpu/isa_sub.rs55
-rw-r--r--src/cpu/isa_swi.rs34
-rw-r--r--src/cpu/isa_teq.rs45
-rw-r--r--src/cpu/isa_tst.rs45
-rw-r--r--src/cpu/sync_cycle.rs43
-rw-r--r--src/cpu/take_state.rs36
-rw-r--r--src/cpu_handle.rs (renamed from src/luma/cpu_handle.rs)8
-rw-r--r--src/cpu_handle/drop.rs (renamed from src/luma/cpu_handle/drop.rs)2
-rw-r--r--src/cpu_handle/dump_palette.rs (renamed from src/luma/cpu_handle/dump_palette.rs)4
-rw-r--r--src/cpu_handle/dump_video.rs (renamed from src/luma/cpu_handle/dump_video.rs)4
-rw-r--r--src/cpu_mode.rs66
-rw-r--r--src/exception.rs64
-rw-r--r--src/instruction.rs65
-rw-r--r--src/instruction/decode_arm.rs201
-rw-r--r--src/instruction/decode_thumb.rs267
-rw-r--r--src/luma.rs30
-rw-r--r--src/luma/cpu/boot.rs131
-rw-r--r--src/luma/cpu/decode_arm.rs258
-rw-r--r--src/luma/cpu/decode_thumb.rs312
-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.rs89
-rw-r--r--src/luma/cpu/isa_logic.rs111
-rw-r--r--src/luma/cpu/isa_move.rs136
-rw-r--r--src/luma/cpu/test_predicate.rs74
-rw-r--r--src/luma/instruction.rs66
-rw-r--r--src/main.rs74
-rw-r--r--src/predicate.rs101
-rw-r--r--src/shifter.rs65
-rw-r--r--src/shifter/extract.rs92
-rw-r--r--src/state.rs (renamed from src/luma/state.rs)29
-rw-r--r--src/state/bank.rs89
-rw-r--r--src/state/new.rs (renamed from src/luma/state/new.rs)65
-rw-r--r--src/state/read.rs (renamed from src/luma/state/read.rs)27
-rw-r--r--src/state/shifter_value.rs130
-rw-r--r--src/state/write.rs (renamed from src/luma/state/write.rs)25
-rw-r--r--test.s111
85 files changed, 3188 insertions, 1630 deletions
diff --git a/.gitignore b/.gitignore
index 61bf874..d11d9bf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,8 @@
*.agb
+*.bin
+*.dmg
*.elf
+*.gba
*.o
*.sav
vgcore.*
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f361f14..10ab25c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,19 @@
+# 0.2F
+
+* Add logo
+* Implement exceptions (including banked registers)
+* Implement software interrupts
+* Add custom bootloader
+* Update readme
+* Support condition-setting instructions
+* Implement all data-processing instructions
+* Support conditional execution (predicates)
+* Rework shifter instructions
+* Rework pipeline
+* Rework file structure
+* Update gitignore
+* Update naming convention
+
# 0.2E
* Update readme
diff --git a/Cargo.toml b/Cargo.toml
index 5ae95fa..cb185d6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "luma"
-version = "0.45.0"
+version = "0.47.0"
authors = ["Gabriel Jensen"]
edition = "2021"
description = "AGB emulator."
@@ -11,7 +11,7 @@ categories = ["emulators"]
[[bin]]
name = "luma"
-path = "src/main.rs"
+path = "src/luma.rs"
[profile.release]
codegen-units = 1
diff --git a/README.md b/README.md
index e7c525d..4c42939 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Luma
-luma is an emulator for the AGB—Game Boy Advance platform.
+Luma is an emulator for the AGB—Game Boy Advance platform.
# Usage
diff --git a/bootloader.ld b/bootloader.ld
new file mode 100644
index 0000000..c9e5ffe
--- /dev/null
+++ b/bootloader.ld
@@ -0,0 +1,12 @@
+OUTPUT_ARCH(arm);
+
+MEMORY {
+ bios : ORIGIN = 0x00000000, LENGTH = 0x00004000
+};
+
+SECTIONS {
+ .bss : {*(.bss*)} > bios
+ .data : {*(.data*)} > bios
+ .text : {*(.text*)} > bios
+ .rodata : {*(.rodata*)} > bios
+};
diff --git a/bootloader.s b/bootloader.s
new file mode 100644
index 0000000..e92e916
--- /dev/null
+++ b/bootloader.s
@@ -0,0 +1,80 @@
+.cpu arm7tdmi
+
+.arm
+b reset
+
+.arm
+b undefined_instruction
+
+.arm
+b software_interrupt
+
+.arm
+b prefetch_abort
+
+.arm
+b data_abort
+
+.arm
+b interrupt_request
+
+.arm
+b fast_interrupt_request
+
+.func
+reset:
+ ldr lr, .image_entry_point
+ bx lr
+
+.endfunc
+
+.align
+.image_entry_point:
+ .word 0x08000000
+
+.func
+undefined_instruction:
+ b undefined_instruction
+.endfunc
+
+.func
+software_interrupt:
+ ldr r0, =.software_interrupt_impl
+ mov r1, pc
+ bx r0
+ movs pc, lr
+.endfunc
+
+.thumb
+
+.func
+.thumb_func
+.software_interrupt_impl:
+ mov r0, lr
+ sub r0, #0x4
+ ldr r0, [r0]
+ bx r1
+
+.endfunc
+
+.arm
+
+.func
+prefetch_abort:
+ b prefetch_abort
+.endfunc
+
+.func
+data_abort:
+ b data_abort
+.endfunc
+
+.func
+interrupt_request:
+ b interrupt_request
+.endfunc
+
+.func
+fast_interrupt_request:
+ b fast_interrupt_request
+.endfunc
diff --git a/luma.svg b/luma.svg
new file mode 100644
index 0000000..5b733ec
--- /dev/null
+++ b/luma.svg
@@ -0,0 +1,27 @@
+<svg height="96" width="96" xmlns="http://www.w3.org/2000/svg">
+ <!-- gradients -->
+ <linearGradient id="backgroundColour" x1="0" x2="1" y1="0" y2="1">
+ <stop offset="0%" stop-color="#222222" />
+ </linearGradient>
+ <!-- masks -->
+ <mask id="foreground">
+ <polygon points="12,12 42,12 42,42 22,42 12,32" fill="white" />
+ <circle cx="22" cy="32" fill="white" r="10" />
+ <polygon points="22,6 48,6 48,32 22,32" fill="black" />
+
+ <circle cx="64" cy="32" fill="white" r="10" />
+ <circle cx="74" cy="32" fill="white" r="10" />
+ <polygon points="54,12 84,12 84,32 74,42 64,42 54,32" fill="white" />
+ <polygon points="64,6 74,6 74,32 64,32" fill="black" />
+
+ <polygon points="12,54 42,54 27,69 42,69 42,84 27,69 12,84" fill="white" />
+
+ <circle cx="64" cy="64" fill="white" r="10" />
+ <circle cx="74" cy="64" fill="white" r="10" />
+ <polygon points="64,54 74,54 84,64 84,84 54,84 54,64" fill="white" />
+ <polygon points="64,64 74,64 74,90 64,90" fill="black" />
+ </mask>
+ <!-- fills -->
+ <rect fill="#6051b2" height="96" width="96" x="0" y="0" />
+ <rect fill="#FFFFFF" height="96" mask="url(#foreground)" width="96" x="0" y="0" />
+</svg>
diff --git a/make_test.sh b/make_test.sh
index e92a652..7403f3c 100755
--- a/make_test.sh
+++ b/make_test.sh
@@ -1,14 +1,18 @@
#!/usr/bin/env sh
-echo Making object file...
+echo "Making object files..."
+arm-none-eabi-as -obootloader.o bootloader.s
arm-none-eabi-as -otest.o test.s
-echo Making binary...
+echo "Making binaries..."
arm-none-eabi-ld -Ttest.ld -otest.elf test.o
+arm-none-eabi-ld -Tbootloader.ld -obootloader.elf bootloader.o
-echo Stripping binary...
+echo "Stripping binary..."
+arm-none-eabi-strip --strip-debug --strip-unneeded bootloader.elf
arm-none-eabi-strip --strip-debug --strip-unneeded test.elf
+arm-none-eabi-objcopy -Obinary bootloader.elf bootloader.bin
arm-none-eabi-objcopy -Obinary test.elf test.agb
-echo Patching header...
+echo "Patching header (test)..."
agbsum -psitest.agb
diff --git a/src/luma/agb.rs b/src/agb.rs
index 01a77b8..01a77b8 100644
--- a/src/luma/agb.rs
+++ b/src/agb.rs
diff --git a/src/luma/app.rs b/src/app.rs
index c8e0134..45b6e90 100644
--- a/src/luma/app.rs
+++ b/src/app.rs
@@ -30,6 +30,7 @@ pub mod check_events;
pub mod draw_video;
pub mod init;
pub mod load;
+pub mod main;
pub mod run;
pub mod sync_video;
diff --git a/src/luma/app/check_events.rs b/src/app/check_events.rs
index 288a093..1dd9551 100644
--- a/src/luma/app/check_events.rs
+++ b/src/app/check_events.rs
@@ -21,7 +21,7 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::luma::app::App;
+use crate::app::App;
use sdl2::event::Event;
use std::sync::atomic::Ordering;
diff --git a/src/luma/app/draw_video.rs b/src/app/draw_video.rs
index 4acb012..a0d2016 100644
--- a/src/luma/app/draw_video.rs
+++ b/src/app/draw_video.rs
@@ -21,8 +21,8 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::luma::SCREEN_SIZE;
-use crate::luma::app::App;
+use crate::SCREEN_SIZE;
+use crate::app::App;
use sdl2::pixels::Color;
use sdl2::rect::Rect;
diff --git a/src/luma/app/init.rs b/src/app/init.rs
index 2d826aa..c7a8653 100644
--- a/src/luma/app/init.rs
+++ b/src/app/init.rs
@@ -20,9 +20,9 @@ and/or modify it under the terms of the GNU
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::luma::{SCREEN_SIZE, VERSION};
-use crate::luma::app::App;
-use crate::luma::configuration::Configuration;
+use crate::{SCREEN_SIZE, VERSION};
+use crate::app::App;
+use crate::configuration::Configuration;
use sdl2::pixels::Color;
use sdl2::render::BlendMode;
diff --git a/src/luma/app/load.rs b/src/app/load.rs
index 5c67daf..24a2385 100644
--- a/src/luma/app/load.rs
+++ b/src/app/load.rs
@@ -21,9 +21,9 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::luma::VERSION;
-use crate::luma::app::App;
-use crate::luma::state::State;
+use crate::VERSION;
+use crate::app::App;
+use crate::state::State;
use std::fs::File;
use std::io::Read;
diff --git a/src/app/main.rs b/src/app/main.rs
new file mode 100644
index 0000000..6391716
--- /dev/null
+++ b/src/app/main.rs
@@ -0,0 +1,69 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::VERSION;
+use crate::app::App;
+use crate::configuration::Configuration;
+
+use std::env::{args, var};
+use std::process::exit;
+
+impl App {
+ pub fn main() {
+ println!("\u{1B}[1mluma\u{1B}[0m {:X}.{:X}", VERSION.0, VERSION.1);
+ println!("Copyright 2021-2023 \u{1B}[1mGabriel Bj\u{F8}rnager Jensen\u{1B}[0m.");
+ println!();
+
+ let path = if let Some(path) = args().nth(0x1) { path }
+ else { default_configuration_path() };
+
+ let configuration = match Configuration::load(&path) {
+ Ok( configuration) => configuration,
+ Err(message) => panic!("unable to load configuration: {message}"),
+ };
+
+ let app = match App::init(configuration) {
+ Ok( app) => app,
+ Err(message) => panic!("unable to initialise application: {message}"),
+ };
+
+ let result = app.run();
+
+ if let Err(ref message) = result { eprintln!("\u{1B}[1m\u{1B}[91merror\u{1B}[0m: {message}") };
+
+ exit(match result {
+ Ok( ..) => 0x0,
+ Err(..) => 0x1,
+ });
+ }
+}
+
+fn default_configuration_path() -> String {
+ let home = match var("HOME") {
+ Ok( path) => path,
+ Err(..) => "/".to_string(),
+ };
+
+ let path = home + "/.luma.toml";
+ return path;
+}
diff --git a/src/luma/app/run.rs b/src/app/run.rs
index 6c00153..5bc8dc7 100644
--- a/src/luma/app/run.rs
+++ b/src/app/run.rs
@@ -21,10 +21,10 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::luma::{PALETTE_LENGTH, VIDEO_LENGTH};
-use crate::luma::app::App;
-use crate::luma::cpu::Cpu;
-use crate::luma::state::State;
+use crate::{PALETTE_LENGTH, VIDEO_LENGTH};
+use crate::app::App;
+use crate::cpu::Cpu;
+use crate::state::State;
use std::time::Instant;
diff --git a/src/luma/app/sync_video.rs b/src/app/sync_video.rs
index 10c6abc..bb863d4 100644
--- a/src/luma/app/sync_video.rs
+++ b/src/app/sync_video.rs
@@ -21,7 +21,7 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::luma::app::App;
+use crate::app::App;
use std::thread::sleep;
use std::time::{Duration, Instant};
diff --git a/src/luma/configuration.rs b/src/configuration.rs
index facbc69..183901a 100644
--- a/src/luma/configuration.rs
+++ b/src/configuration.rs
@@ -21,8 +21,8 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-pub mod load;
-pub mod validate;
+mod load;
+mod validate;
pub struct Configuration {
pub bootloader: String,
diff --git a/src/luma/configuration/load.rs b/src/configuration/load.rs
index bfd91b0..a0065fc 100644
--- a/src/luma/configuration/load.rs
+++ b/src/configuration/load.rs
@@ -21,7 +21,7 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::luma::configuration::Configuration;
+use crate::configuration::Configuration;
extern crate toml;
diff --git a/src/luma/configuration/validate.rs b/src/configuration/validate.rs
index 9cdd135..26aee61 100644
--- a/src/luma/configuration/validate.rs
+++ b/src/configuration/validate.rs
@@ -21,7 +21,7 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::luma::configuration::Configuration;
+use crate::configuration::Configuration;
impl Configuration {
pub(super) fn validate(&self) -> Result<(), String> {
diff --git a/src/luma/cpu.rs b/src/cpu.rs
index 28f6844..8b31d59 100644
--- a/src/luma/cpu.rs
+++ b/src/cpu.rs
@@ -21,35 +21,61 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::luma::instruction::Instruction;
-use crate::luma::state::State;
+use crate::instruction::Instruction;
+use crate::predicate::Predicate;
+use crate::state::State;
use std::sync::{Arc, Mutex};
use std::sync::atomic::AtomicBool;
-pub mod boot;
-pub mod decode_arm;
-pub mod decode_thumb;
-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 alu_exit_exception;
+mod boot;
+mod r#continue;
+mod enter_exception;
+mod execute;
+mod exit_exception;
+mod fetch_arm;
+mod fetch_thumb;
+
+mod isa_adc;
+mod isa_add;
+mod isa_and;
+mod isa_bic;
+mod isa_bl;
+mod isa_b;
+mod isa_bx;
+mod isa_cmn;
+mod isa_cmp;
+mod isa_eor;
+mod isa_mov;
+mod isa_mul;
+mod isa_mvn;
+mod isa_orr;
+mod isa_rsb;
+mod isa_rsc;
+mod isa_sbc;
+mod isa_sub;
+mod isa_swi;
+mod isa_teq;
+mod isa_tst;
+
+mod isa_memory;
mod exchange;
-mod test_predicate;
+mod take_state;
-// https://github.com/rust-lang/rust/issues/115966
+// <https://github.com/rust-lang/rust/issues/115966>
+
+#[allow(unused_imports)]
+pub use alu_exit_exception::*;
#[allow(unused_imports)]
pub use exchange::*;
#[allow(unused_imports)]
-pub use test_predicate::*;
+pub use take_state::*;
-pub type Decoder = fn(&mut Cpu) -> Instruction;
+pub type Fetcher = fn(&mut Cpu) -> (Instruction, Predicate, u32);
pub struct Cpu {
state: Arc<Mutex<State>>,
@@ -57,7 +83,7 @@ pub struct Cpu {
dead: Arc<AtomicBool>,
instruction_size: u32,
- decoder: Decoder,
+ fetcher: Fetcher,
}
impl Cpu {
@@ -68,11 +94,11 @@ impl Cpu {
dead: Arc::new(AtomicBool::new(false)),
instruction_size: 0x4,
- decoder: Self::decode_arm,
+ fetcher: Self::fetch_arm,
};
}
#[inline(always)]
#[must_use]
- fn decode(&mut self) -> Instruction { (self.decoder)(self) }
+ fn fetch(&mut self) -> (Instruction, Predicate, u32) { (self.fetcher)(self) }
}
diff --git a/src/cpu/alu_exit_exception.rs b/src/cpu/alu_exit_exception.rs
new file mode 100644
index 0000000..de9f238
--- /dev/null
+++ b/src/cpu/alu_exit_exception.rs
@@ -0,0 +1,38 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+macro_rules! alu_exit_exception {
+ ($self: ident, $state: ident, $rd: ident, $block: block) => {{
+ match $rd {
+ 0b1111 => {
+ drop($state);
+ $self.exit_exception();
+ },
+
+ _ => {
+ $block;
+ },
+ };
+ }};
+}
+pub(crate) use alu_exit_exception;
diff --git a/src/cpu/boot.rs b/src/cpu/boot.rs
new file mode 100644
index 0000000..5c38427
--- /dev/null
+++ b/src/cpu/boot.rs
@@ -0,0 +1,70 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::{log, log_status};
+use crate::cpu::Cpu;
+use crate::cpu_handle::CpuHandle;
+
+use std::sync::atomic::Ordering;
+use std::thread::{sleep, spawn};
+use std::time::{Duration, Instant};
+
+impl Cpu {
+ pub fn boot(self) -> CpuHandle {
+ eprintln!("starting emulation at {:#010X}", self.state.lock().unwrap().read_register(0xF).wrapping_sub(0x8));
+
+ let state = self.state.clone();
+ let dead = self.dead.clone();
+
+ let handle = spawn(move || { self.run() });
+
+ return CpuHandle::new(
+ state,
+ dead,
+
+ handle,
+ );
+ }
+
+ fn run(mut self) {
+ let run_timer = Instant::now();
+
+ 'main_loop: loop {
+ if self.dead.load(Ordering::Relaxed) { break 'main_loop };
+
+ let (instruction, predicate, cpsr) = self.fetch();
+ match predicate.test(cpsr) {
+ false => log_status!("skipping due to predicate ({})", predicate.code()),
+ true => self.execute(instruction),
+ };
+ self.r#continue();
+
+ if cfg!(debug_assertions) { sleep(Duration::from_millis(125)) };
+
+ self.cycle += 0x1;
+ }
+
+ let frequency = self.cycle as f64 / run_timer.elapsed().as_micros() as f64;
+ eprintln!("emulated {} cycle(s) ({frequency:.9} MHz)", self.cycle);
+ }
+}
diff --git a/src/luma/cpu/continue.rs b/src/cpu/continue.rs
index 6d10bc3..7b96713 100644
--- a/src/luma/cpu/continue.rs
+++ b/src/cpu/continue.rs
@@ -21,11 +21,11 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::luma::cpu::Cpu;
+use crate::cpu::{Cpu, take_state_mut};
impl Cpu {
pub(super) fn r#continue(&mut self) {
- let mut state = self.state.lock().unwrap();
+ take_state_mut!(state, self);
let pc = state.read_register(0xF).wrapping_add(self.instruction_size);
state.write_register(0xF, pc);
diff --git a/src/cpu/enter_exception.rs b/src/cpu/enter_exception.rs
new file mode 100644
index 0000000..193b8cf
--- /dev/null
+++ b/src/cpu/enter_exception.rs
@@ -0,0 +1,59 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::cpu::{Cpu, exchange, take_state_mut};
+use crate::exception::Exception;
+
+impl Cpu {
+ pub(super) fn enter_exception(&mut self, exception: Exception) {
+ take_state_mut!(state, self);
+
+ let mode = exception.mode();
+
+ state.bank(mode);
+
+ let lr = state.read_register(0xF);
+ state.write_register(0xE, lr);
+
+ let mut cpsr = state.read_cpsr();
+ state.write_spsr(mode, cpsr);
+
+ cpsr &= 0b11111111111111111111111101000000;
+ cpsr |= 0b00000000000000000000000010000000;
+ cpsr |= mode as u32;
+
+ cpsr &= match exception {
+ | Exception::FastInterruptRequest
+ | Exception::Reset => 0b11111111111111111111111110111111,
+
+ _ => 0b11111111111111111111111111111111,
+ };
+
+ state.write_cpsr(cpsr);
+
+ exchange!(self, false);
+
+ let pc = exception.vector_address().wrapping_add(0x4);
+ state.write_register(0xF, pc);
+ }
+}
diff --git a/src/luma/cpu/exchange.rs b/src/cpu/exchange.rs
index 2414831..1e74a75 100644
--- a/src/luma/cpu/exchange.rs
+++ b/src/cpu/exchange.rs
@@ -22,18 +22,24 @@
*/
macro_rules! exchange {
- ($cpu: expr, $t: expr) => {{
- use crate::luma::cpu::Decoder;
-
- const DATA: [(u32, Decoder); 0x2] = [
- (0x4, Cpu::decode_arm),
- (0x2, Cpu::decode_thumb),
+ ($cpu: ident, $t: expr) => {{
+ use crate::log_status;
+ use crate::cpu::Fetcher;
+
+ log_status!("exchanging to {}", match $t {
+ false => "ARM",
+ true => "Thumb",
+ });
+
+ const DATA: [(u32, Fetcher); 0x2] = [
+ (0x4, Cpu::fetch_arm),
+ (0x2, Cpu::fetch_thumb),
];
let index = $t as usize & 0b1;
$cpu.instruction_size = unsafe { DATA.get_unchecked(index).0 };
- $cpu.decoder = unsafe { DATA.get_unchecked(index).1 };
+ $cpu.fetcher = unsafe { DATA.get_unchecked(index).1 };
}};
}
pub(crate) use exchange;
diff --git a/src/cpu/execute.rs b/src/cpu/execute.rs
new file mode 100644
index 0000000..7987f92
--- /dev/null
+++ b/src/cpu/execute.rs
@@ -0,0 +1,69 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::cpu::Cpu;
+use crate::instruction::Instruction;
+
+impl Cpu {
+ #[inline(always)]
+ pub(super) fn execute(&mut self, instruction: Instruction) {
+ use Instruction::*;
+
+ match instruction {
+ Adc(rd, rn, shifter, s) => self.isa_adc(rd, rn, shifter, s),
+ Add(rd, rn, shifter, s) => self.isa_add(rd, rn, shifter, s),
+ And(rd, rn, shifter, s) => self.isa_and(rd, rn, shifter, s),
+ B( imm) => self.isa_b( imm),
+ Bic(rd, rn, shifter, s) => self.isa_bic(rd, rn, shifter, s),
+ Bl( imm) => self.isa_bl( imm),
+ Bl0(imm) => self.isa_bl0(imm),
+ Bl1(imm) => self.isa_bl1(imm),
+ Bx( rm) => self.isa_bx( rm),
+ Cmn(rn, shifter) => self.isa_cmn(rn, shifter),
+ Cmp(rn, shifter) => self.isa_cmp(rn, shifter),
+ Eor(rd, rn, shifter, s) => self.isa_eor(rd, rn, shifter, s),
+ Mov(rd, shifter, s) => self.isa_mov(rd, shifter, s),
+ Mul(rd, rn, shifter, s) => self.isa_mul(rd, rn, shifter, s),
+ Mvn(rd, shifter, s) => self.isa_mvn(rd, shifter, s),
+ Orr(rd, rn, shifter, s) => self.isa_orr(rd, rn, shifter, s),
+ Rsb(rd, rn, shifter, s) => self.isa_rsb(rd, rn, shifter, s),
+ Rsc(rd, rn, shifter, s) => self.isa_rsc(rd, rn, shifter, s),
+ Sbc(rd, rn, shifter, s) => self.isa_sbc(rd, rn, shifter, s),
+ Sub(rd, rn, shifter, s) => self.isa_sub(rd, rn, shifter, s),
+ Swi(imm) => self.isa_swi(imm),
+ Teq(rn, shifter) => self.isa_teq(rn, shifter),
+ Tst(rn, shifter) => self.isa_tst(rn, shifter),
+
+ // Legacy:
+ LoadHalfword( rd, rn, imm) => self.isa_load_halfword( rd, rn, imm),
+ LoadImmediateOffset( rd, rn, imm) => self.isa_load_immediate_offset( rd, rn, imm),
+ LoadPc( rd, imm) => self.isa_load_pc( rd, imm),
+ StoreByteImmediateOffset(rd, rn, imm) => self.isa_store_byte_immediate_offset(rd, rn, imm),
+ StoreByteRegisterOffset( rd, rn, rm) => self.isa_store_byte_register_offset( rd, rn, rm),
+ StoreHalfword( rd, rn, imm) => self.isa_store_halfword( rd, rn, imm),
+ StoreImmediateOffset( rd, rn, imm) => self.isa_store_immediate_offset( rd, rn, imm),
+
+ Undefined => {},
+ };
+ }
+}
diff --git a/src/cpu/exit_exception.rs b/src/cpu/exit_exception.rs
new file mode 100644
index 0000000..fa47fad
--- /dev/null
+++ b/src/cpu/exit_exception.rs
@@ -0,0 +1,42 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::cpu::{Cpu, exchange, take_state_mut};
+use crate::cpu_mode::CpuMode;
+
+impl Cpu {
+ pub(super) fn exit_exception(&mut self) {
+ take_state_mut!(state, self);
+
+ let cpsr = state.read_cpsr();
+ let old_mode = CpuMode::from_raw((cpsr & 0b00000000000000000000000000011111) as u8);
+
+ let spsr = state.read_spsr(old_mode);
+ let new_mode = CpuMode::from_raw((spsr & 0b00000000000000000000000000011111) as u8);
+
+ let t = spsr & 0b00000000000000000000000000100000 != 0x0;
+ state.write_cpsr(spsr);
+ state.bank(new_mode);
+ exchange!(self, t);
+ }
+}
diff --git a/src/cpu/fetch_arm.rs b/src/cpu/fetch_arm.rs
new file mode 100644
index 0000000..453dbe2
--- /dev/null
+++ b/src/cpu/fetch_arm.rs
@@ -0,0 +1,55 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::{Error, log};
+use crate::cpu::{Cpu, take_state};
+use crate::instruction::Instruction;
+use crate::predicate::Predicate;
+
+impl Cpu {
+ #[must_use]
+ pub(super) fn fetch_arm(&mut self) -> (Instruction, Predicate, u32) {
+ take_state!(state, self);
+
+ let address = state.read_register(0xF).wrapping_sub(0x8);
+ let opcode = state.read_word(address);
+ let cpsr = state.read_cpsr();
+
+ drop(state);
+
+ log!();
+ log!("\u{1B}[1m{opcode:032b}\u{1B}[0m @ \u{1B}[1m{address:08X}\u{1B}[0m - ({})", self.cycle);
+
+ let (instruction, predicate) = Instruction::decode_arm(opcode);
+
+ match (instruction, predicate) {
+ | (Instruction::Undefined, _)
+ | (_, Predicate::Nv)
+ => Error::InvalidArmOpcode(address, opcode).trap(),
+
+ _ => {},
+ };
+
+ return (instruction, predicate, cpsr);
+ }
+}
diff --git a/src/cpu/fetch_thumb.rs b/src/cpu/fetch_thumb.rs
new file mode 100644
index 0000000..cee6ba7
--- /dev/null
+++ b/src/cpu/fetch_thumb.rs
@@ -0,0 +1,54 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::{Error, log};
+use crate::cpu::{Cpu, take_state};
+use crate::instruction::Instruction;
+use crate::predicate::Predicate;
+
+impl Cpu {
+ pub(super) fn fetch_thumb(&mut self) -> (Instruction, Predicate, u32) {
+ take_state!(state, self);
+
+ let address = state.read_register(0xF).wrapping_sub(0x4);
+ let opcode = state.read_halfword(address);
+ let cpsr = state.read_cpsr();
+
+ drop(state);
+
+ log!();
+ log!(" \u{1B}[1m{opcode:016b}\u{1B}[0m @ \u{1B}[1m{address:08X}\u{1B}[0m - ({})", self.cycle);
+
+ let (instruction, predicate) = Instruction::decode_thumb(opcode);
+
+ match (instruction, predicate) {
+ | (Instruction::Undefined, _)
+ | (_, Predicate::Nv)
+ => Error::InvalidThumbOpcode(address, opcode).trap(),
+
+ _ => {},
+ };
+
+ return (instruction, predicate, cpsr);
+ }
+}
diff --git a/src/cpu/isa_adc.rs b/src/cpu/isa_adc.rs
new file mode 100644
index 0000000..214d289
--- /dev/null
+++ b/src/cpu/isa_adc.rs
@@ -0,0 +1,58 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_adc(&mut self, rd: u8, rn: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, r{rn}, {shifter}", match s {
+ false => "adc",
+ true => "adcs",
+ });
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let c = (state.read_cpsr() & 0b00100000000000000000000000000000) >> 0x1D;
+
+ let (mut addend, _) = state.shifter_value(shifter);
+ addend = addend.wrapping_add(c);
+
+ let (result, c) = rn_value.overflowing_add(addend);
+ let (_, v) = (rn_value as i32).overflowing_add_unsigned(addend);
+ state.write_register(rd, result);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
+ cpsr |= (v as u32) << 0x1C;
+ cpsr |= (c as u32) << 0x1D;
+ cpsr |= ((result == 0x0) as u32) << 0x1E;
+ cpsr |= result & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_add.rs b/src/cpu/isa_add.rs
new file mode 100644
index 0000000..c099c97
--- /dev/null
+++ b/src/cpu/isa_add.rs
@@ -0,0 +1,55 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_add(&mut self, rd: u8, rn: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, r{rn}, {shifter}", match s {
+ false => "add",
+ true => "adds",
+ });
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (addend, _) = state.shifter_value(shifter);
+
+ let (result, c) = rn_value.overflowing_add(addend);
+ let (_, v) = (rn_value as i32).overflowing_add_unsigned(addend);
+ state.write_register(rd, result);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
+ cpsr |= (v as u32) << 0x1C;
+ cpsr |= (c as u32) << 0x1D;
+ cpsr |= ((result == 0x0) as u32) << 0x1E;
+ cpsr |= result & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_and.rs b/src/cpu/isa_and.rs
new file mode 100644
index 0000000..1148953
--- /dev/null
+++ b/src/cpu/isa_and.rs
@@ -0,0 +1,53 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_and(&mut self, rd: u8, rn: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, r{rn}, {shifter}", match s {
+ false => "and",
+ true => "ands",
+ });
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (mask, c) = state.shifter_value(shifter);
+
+ let result = rn_value & mask;
+ state.write_register(rd, result);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00011111111111111111111111111111;
+ cpsr |= (c as u32) << 0x1D;
+ cpsr |= ((result == 0x0) as u32) << 0x1E;
+ cpsr |= result & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_b.rs b/src/cpu/isa_b.rs
new file mode 100644
index 0000000..e0ebdca
--- /dev/null
+++ b/src/cpu/isa_b.rs
@@ -0,0 +1,38 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{Cpu, take_state_mut};
+
+impl Cpu {
+ pub(super) fn isa_b(&mut self, imm: i32) {
+ take_state_mut!(state, self);
+
+ let mut target = state.read_register(0xF).wrapping_add_signed(imm);
+
+ log!("b {target:#X}");
+
+ target = target.wrapping_add(self.instruction_size);
+ state.write_register(0xF, target);
+ }
+}
diff --git a/src/cpu/isa_bic.rs b/src/cpu/isa_bic.rs
new file mode 100644
index 0000000..6647e0c
--- /dev/null
+++ b/src/cpu/isa_bic.rs
@@ -0,0 +1,53 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_bic(&mut self, rd: u8, rn: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, r{rn}, {shifter}", match s {
+ false => "bic",
+ true => "bics",
+ });
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (mask, c) = state.shifter_value(shifter);
+
+ let result = rn_value & !mask;
+ state.write_register(rd, result);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00011111111111111111111111111111;
+ cpsr |= (c as u32) << 0x1D;
+ cpsr |= ((result == 0x0) as u32) << 0x1E;
+ cpsr |= result & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_bl.rs b/src/cpu/isa_bl.rs
new file mode 100644
index 0000000..416ce4a
--- /dev/null
+++ b/src/cpu/isa_bl.rs
@@ -0,0 +1,62 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{Cpu, take_state_mut};
+
+impl Cpu {
+ pub(super) fn isa_bl(&mut self, imm: i32) {
+ take_state_mut!(state, self);
+
+ let link_target = state.read_register(0xF).wrapping_sub(self.instruction_size);
+ let mut branch_target = state.read_register(0xF).wrapping_add_signed(imm);
+
+ log!("bl {branch_target:#X}");
+
+ branch_target = branch_target.wrapping_add(self.instruction_size);
+ state.write_register(0xE, link_target);
+ state.write_register(0xF, branch_target);
+ }
+
+ pub(super) fn isa_bl0(&mut self, imm: i32) {
+ take_state_mut!(state, self);
+
+ let target = state.read_register(0xF).wrapping_add_signed(imm);
+
+ state.write_register(0xE, target);
+ }
+
+ pub(super) fn isa_bl1(&mut self, imm: i32) {
+ take_state_mut!(state, self);
+
+ let mut branch_target = state.read_register(0xE).wrapping_add_signed(imm);
+ let link_target = state.read_register(0xF).wrapping_sub(0x2);
+
+ log!("bl {branch_target:#X}");
+
+ state.write_register(0xE, link_target);
+
+ branch_target = branch_target.wrapping_add(0x2);
+ state.write_register(0xF, branch_target);
+ }
+}
diff --git a/src/cpu/isa_bx.rs b/src/cpu/isa_bx.rs
new file mode 100644
index 0000000..3307dbf
--- /dev/null
+++ b/src/cpu/isa_bx.rs
@@ -0,0 +1,45 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{Cpu, exchange, take_state_mut};
+
+impl Cpu {
+ pub(super) fn isa_bx(&mut self, rm: u8) {
+ take_state_mut!(state, self);
+
+ log!("bx r{rm}");
+
+ let mut target = state.read_register(rm);
+
+ let t = target & 0b00000000000000000000000000000001 != 0x0;
+ exchange!(self, t);
+
+ let cpsr = state.read_cpsr() & 0b11111111111111111111111111011111 | (t as u32) << 0x5;
+ state.write_cpsr(cpsr);
+
+ target &= 0b11111111111111111111111111111110;
+ target = target.wrapping_add(self.instruction_size);
+ state.write_register(0xF, target);
+ }
+}
diff --git a/src/cpu/isa_cmn.rs b/src/cpu/isa_cmn.rs
new file mode 100644
index 0000000..f69acef
--- /dev/null
+++ b/src/cpu/isa_cmn.rs
@@ -0,0 +1,47 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_cmn(&mut self, rn: u8, shifter: Shifter) {
+ log!("cmn r{rn}, {shifter}");
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (subtrahend, _) = state.shifter_value(shifter);
+
+ let (temporary, c) = rn_value.overflowing_add(subtrahend);
+ let (_, v) = (rn_value as i32).overflowing_add_unsigned(subtrahend);
+
+ let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
+ cpsr |= (v as u32) << 0x1C;
+ cpsr |= (!c as u32) << 0x1D;
+ cpsr |= ((temporary == 0x0) as u32) << 0x1E;
+ cpsr |= temporary & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ }
+}
diff --git a/src/cpu/isa_cmp.rs b/src/cpu/isa_cmp.rs
new file mode 100644
index 0000000..c5719fb
--- /dev/null
+++ b/src/cpu/isa_cmp.rs
@@ -0,0 +1,47 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_cmp(&mut self, rn: u8, shifter: Shifter) {
+ log!("cmp r{rn}, {shifter}");
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (subtrahend, _) = state.shifter_value(shifter);
+
+ let (temporary, c) = rn_value.overflowing_sub(subtrahend);
+ let (_, v) = (rn_value as i32).overflowing_sub_unsigned(subtrahend);
+
+ let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
+ cpsr |= (v as u32) << 0x1C;
+ cpsr |= (!c as u32) << 0x1D;
+ cpsr |= ((temporary == 0x0) as u32) << 0x1E;
+ cpsr |= temporary & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ }
+}
diff --git a/src/cpu/isa_eor.rs b/src/cpu/isa_eor.rs
new file mode 100644
index 0000000..22d3842
--- /dev/null
+++ b/src/cpu/isa_eor.rs
@@ -0,0 +1,53 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_eor(&mut self, rd: u8, rn: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, r{rn}, {shifter}", match s {
+ false => "eor",
+ true => "eors",
+ });
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (mask, c) = state.shifter_value(shifter);
+
+ let result = rn_value ^ mask;
+ state.write_register(rd, result);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00011111111111111111111111111111;
+ cpsr |= (c as u32) << 0x1D;
+ cpsr |= ((result == 0x0) as u32) << 0x1E;
+ cpsr |= result & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/luma/cpu/isa_memory.rs b/src/cpu/isa_memory.rs
index 8b953c0..f4abecb 100644
--- a/src/luma/cpu/isa_memory.rs
+++ b/src/cpu/isa_memory.rs
@@ -21,14 +21,14 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::luma::log;
-use crate::luma::cpu::Cpu;
+use crate::log;
+use crate::cpu::{Cpu, take_state_mut};
impl Cpu {
pub(super) fn isa_load_halfword(&mut self, rd: u8, rn: u8, imm: i16) {
- let mut state = self.state.lock().unwrap();
+ take_state_mut!(state, self);
- log!("ldrh r{rd}, [r{rn}, {imm:#X}]");
+ log!("ldrh r{rd}, [r{rn}, #{imm:#X}]");
let rn_value = state.read_register(rn);
@@ -39,38 +39,38 @@ impl Cpu {
}
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}]");
+ take_state_mut!(state, self);
let rn_value = state.read_register(rn);
let target = rn_value.wrapping_add_signed(imm as i32);
+ log!("ldr r{rd}, [r{rn}, #{imm:#X}] @ {target:#010X}");
+
let result = state.read_word(target);
state.write_register(rd, result);
}
- pub(super) fn isa_load_pc(&mut self, rd: u8, imm: i16) {
+ pub(super) fn isa_load_pc(&mut self, rd: u8, imm: u16) {
// Slightly different from load_immediate_offset
// due to the target being forced word-aligned.
- let mut state = self.state.lock().unwrap();
-
- log!("ldr r{rd}, [pc, {imm:#X}]");
+ take_state_mut!(state, self);
let rn_value = state.read_register(0xF) & 0b11111111111111111111111111111100;
- let target = rn_value.wrapping_add_signed(imm as i32);
+ let target = rn_value.wrapping_add(imm as u32);
+
+ log!("ldr r{rd}, [pc, #{imm:#X}] @ {target:#010X}");
let result = state.read_word(target);
state.write_register(rd, result);
}
pub(super) fn isa_store_byte_immediate_offset(&mut self, rd: u8, rn: u8, imm: i16) {
- log!("strb r{rd}, [r{rn}, {imm:#X}]");
+ log!("strb r{rd}, [r{rn}, #{imm:#X}]");
- let mut state = self.state.lock().unwrap();
+ take_state_mut!(state, self);
let rn_value = state.read_register(rn);
@@ -83,7 +83,7 @@ impl Cpu {
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();
+ take_state_mut!(state, self);
let rn_value = state.read_register(rn);
let rm_value = state.read_register(rm);
@@ -95,9 +95,9 @@ impl Cpu {
}
pub(super) fn isa_store_halfword(&mut self, rd: u8, rn: u8, imm: i16) {
- log!("strh r{rd}, [r{rn}, {imm:#X}]");
+ log!("strh r{rd}, [r{rn}, #{imm:#X}]");
- let mut state = self.state.lock().unwrap();
+ take_state_mut!(state, self);
let rn_value = state.read_register(rn);
@@ -108,9 +108,9 @@ impl Cpu {
}
pub(super) fn isa_store_immediate_offset(&mut self, rd: u8, rn: u8, imm: i16) {
- log!("str r{rd}, [r{rn}, {imm:#X}]");
+ log!("str r{rd}, [r{rn}, #{imm:#X}]");
- let mut state = self.state.lock().unwrap();
+ take_state_mut!(state, self);
let rn_value = state.read_register(rn);
diff --git a/src/cpu/isa_mov.rs b/src/cpu/isa_mov.rs
new file mode 100644
index 0000000..302128e
--- /dev/null
+++ b/src/cpu/isa_mov.rs
@@ -0,0 +1,53 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_mov(&mut self, rd: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, {shifter}", match s {
+ false => "mov",
+ true => "movs",
+ });
+
+ take_state_mut!(state, self);
+
+ let (mut value, c) = state.shifter_value(shifter);
+ // Adjust value for branch.
+ if value == 0b1111 { value = value.wrapping_add(self.instruction_size.wrapping_shl(0x1)) };
+
+ state.write_register(rd, value);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00011111111111111111111111111111;
+ cpsr |= (c as u32) << 0x1D;
+ cpsr |= ((value == 0x0) as u32) << 0x1E;
+ cpsr |= value & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_mul.rs b/src/cpu/isa_mul.rs
new file mode 100644
index 0000000..d4212f0
--- /dev/null
+++ b/src/cpu/isa_mul.rs
@@ -0,0 +1,52 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_mul(&mut self, rd: u8, rn: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, r{rn}, {shifter}", match s {
+ false => "mul",
+ true => "muls",
+ });
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (multiplicand, _) = state.shifter_value(shifter);
+
+ let result = rn_value.wrapping_mul(multiplicand);
+ state.write_register(rd, result);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00111111111111111111111111111111;
+ cpsr |= ((result == 0x0) as u32) << 0x1E;
+ cpsr |= result & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_mvn.rs b/src/cpu/isa_mvn.rs
new file mode 100644
index 0000000..a3847e4
--- /dev/null
+++ b/src/cpu/isa_mvn.rs
@@ -0,0 +1,52 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_mvn(&mut self, rd: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, {shifter}", match s {
+ false => "mvn",
+ true => "mvns",
+ });
+
+ take_state_mut!(state, self);
+
+ let (mut result, c) = state.shifter_value(shifter);
+ result = !result;
+
+ state.write_register(rd, result);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00011111111111111111111111111111;
+ cpsr |= (c as u32) << 0x1D;
+ cpsr |= ((result == 0x0) as u32) << 0x1E;
+ cpsr |= result & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_orr.rs b/src/cpu/isa_orr.rs
new file mode 100644
index 0000000..030695a
--- /dev/null
+++ b/src/cpu/isa_orr.rs
@@ -0,0 +1,53 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_orr(&mut self, rd: u8, rn: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, r{rn}, {shifter}", match s {
+ false => "orr",
+ true => "orrs",
+ });
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (mask, c) = state.shifter_value(shifter);
+
+ let result = rn_value | mask;
+ state.write_register(rd, result);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00011111111111111111111111111111;
+ cpsr |= (c as u32) << 0x1D;
+ cpsr |= ((result == 0x0) as u32) << 0x1E;
+ cpsr |= result & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_rsb.rs b/src/cpu/isa_rsb.rs
new file mode 100644
index 0000000..d4daf06
--- /dev/null
+++ b/src/cpu/isa_rsb.rs
@@ -0,0 +1,55 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_rsb(&mut self, rd: u8, rn: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, r{rn}, {shifter}", match s {
+ false => "rsb",
+ true => "rsbs",
+ });
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (minuend, _) = state.shifter_value(shifter);
+
+ let (result, c) = minuend.overflowing_sub(rn_value);
+ let (_, v) = (minuend as i32).overflowing_sub_unsigned(rn_value);
+ state.write_register(rd, result);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
+ cpsr |= (v as u32) << 0x1C;
+ cpsr |= (!c as u32) << 0x1D;
+ cpsr |= ((result == 0x0) as u32) << 0x1E;
+ cpsr |= result & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_rsc.rs b/src/cpu/isa_rsc.rs
new file mode 100644
index 0000000..414df34
--- /dev/null
+++ b/src/cpu/isa_rsc.rs
@@ -0,0 +1,58 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_rsc(&mut self, rd: u8, rn: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, r{rn}, {shifter}", match s {
+ false => "rsc",
+ true => "rscs",
+ });
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (minuend, _) = state.shifter_value(shifter);
+
+ let c = (state.read_cpsr() & 0b00100000000000000000000000000000) >> 0x1D != 0x0;
+ let subtrahend = rn_value.wrapping_sub(c as u32);
+
+ let (result, c) = minuend.overflowing_sub(subtrahend);
+ let (_, v) = (minuend as i32).overflowing_sub_unsigned(subtrahend);
+ state.write_register(rd, result);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
+ cpsr |= (v as u32) << 0x1C;
+ cpsr |= (!c as u32) << 0x1D;
+ cpsr |= ((result == 0x0) as u32) << 0x1E;
+ cpsr |= result & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_sbc.rs b/src/cpu/isa_sbc.rs
new file mode 100644
index 0000000..28a7c0d
--- /dev/null
+++ b/src/cpu/isa_sbc.rs
@@ -0,0 +1,58 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_sbc(&mut self, rd: u8, rn: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, r{rn}, {shifter}", match s {
+ false => "sbc",
+ true => "sbcs",
+ });
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let c = (state.read_cpsr() & 0b00100000000000000000000000000000) >> 0x1D;
+
+ let (mut subtrahend, _) = state.shifter_value(shifter);
+ subtrahend = subtrahend.wrapping_sub(c);
+
+ let (result, c) = rn_value.overflowing_sub(subtrahend);
+ let (_, v) = (rn_value as i32).overflowing_sub_unsigned(subtrahend);
+ state.write_register(rd, result);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
+ cpsr |= (v as u32) << 0x1C;
+ cpsr |= (!c as u32) << 0x1D;
+ cpsr |= ((result == 0x0) as u32) << 0x1E;
+ cpsr |= result & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_sub.rs b/src/cpu/isa_sub.rs
new file mode 100644
index 0000000..f8290c9
--- /dev/null
+++ b/src/cpu/isa_sub.rs
@@ -0,0 +1,55 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_sub(&mut self, rd: u8, rn: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, r{rn}, {shifter}", match s {
+ false => "sub",
+ true => "subs",
+ });
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (subtrahend, _) = state.shifter_value(shifter);
+
+ let (result, c) = rn_value.overflowing_sub(subtrahend);
+ let (_, v) = (rn_value as i32).overflowing_sub_unsigned(subtrahend);
+ state.write_register(rd, result);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
+ cpsr |= (v as u32) << 0x1C;
+ cpsr |= (!c as u32) << 0x1D;
+ cpsr |= ((result == 0x0) as u32) << 0x1E;
+ cpsr |= result & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_swi.rs b/src/cpu/isa_swi.rs
new file mode 100644
index 0000000..0575740
--- /dev/null
+++ b/src/cpu/isa_swi.rs
@@ -0,0 +1,34 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{Cpu};
+use crate::exception::Exception;
+
+impl Cpu {
+ pub(super) fn isa_swi(&mut self, imm: u32) {
+ log!("swi #{imm:#X}");
+
+ self.enter_exception(Exception::SoftwareInterrupt);
+ }
+}
diff --git a/src/cpu/isa_teq.rs b/src/cpu/isa_teq.rs
new file mode 100644
index 0000000..2bc162a
--- /dev/null
+++ b/src/cpu/isa_teq.rs
@@ -0,0 +1,45 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_teq(&mut self, rn: u8, shifter: Shifter) {
+ log!("teq r{rn}, {shifter}");
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (mask, c) = state.shifter_value(shifter);
+
+ let temporary = rn_value ^ mask;
+
+ let mut cpsr = state.read_cpsr() & 0b00011111111111111111111111111111;
+ cpsr |= (c as u32) << 0x1D;
+ cpsr |= ((temporary == 0x0) as u32) << 0x1E;
+ cpsr |= temporary & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ }
+}
diff --git a/src/cpu/isa_tst.rs b/src/cpu/isa_tst.rs
new file mode 100644
index 0000000..0c4d290
--- /dev/null
+++ b/src/cpu/isa_tst.rs
@@ -0,0 +1,45 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::log;
+use crate::cpu::{Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_tst(&mut self, rn: u8, shifter: Shifter) {
+ log!("tst r{rn}, {shifter}");
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (mask, c) = state.shifter_value(shifter);
+
+ let temporary = rn_value & mask;
+
+ let mut cpsr = state.read_cpsr() & 0b00011111111111111111111111111111;
+ cpsr |= (c as u32) << 0x1D;
+ cpsr |= ((temporary == 0x0) as u32) << 0x1E;
+ cpsr |= temporary & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ }
+}
diff --git a/src/cpu/sync_cycle.rs b/src/cpu/sync_cycle.rs
new file mode 100644
index 0000000..b343c32
--- /dev/null
+++ b/src/cpu/sync_cycle.rs
@@ -0,0 +1,43 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::cpu::Cpu;
+
+use std::thread::sleep;
+use std::time::{Duration, Instant};
+
+impl Cpu {
+ pub fn sync_cycle(&self, cycle_start: Instant) {
+ // 16*1024*1024 Hz, 59.604644775 ns
+
+ const CYCLE_DURATION: f64 = 0.000000059604644775;
+ let cycle_duration = Duration::from_secs_f64(CYCLE_DURATION);
+
+ let remaining = match cycle_duration.checked_sub(cycle_start.elapsed()) {
+ Some(value) => value,
+ None => Duration::from_secs(0x0),
+ };
+
+ sleep(remaining);
+ }
+}
diff --git a/src/cpu/take_state.rs b/src/cpu/take_state.rs
new file mode 100644
index 0000000..5b90a1f
--- /dev/null
+++ b/src/cpu/take_state.rs
@@ -0,0 +1,36 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+macro_rules! take_state {
+ ($name: ident, $cpu: ident) => {
+ let $name = $cpu.state.lock().unwrap();
+ };
+}
+pub(crate) use take_state;
+
+macro_rules! take_state_mut {
+ ($name: ident, $cpu: ident) => {
+ let mut $name = $cpu.state.lock().unwrap();
+ };
+}
+pub(crate) use take_state_mut;
diff --git a/src/luma/cpu_handle.rs b/src/cpu_handle.rs
index ceacd03..c4c1513 100644
--- a/src/luma/cpu_handle.rs
+++ b/src/cpu_handle.rs
@@ -21,15 +21,15 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::luma::state::State;
+use crate::state::State;
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;
+mod drop;
+mod dump_video;
+mod dump_palette;
pub struct CpuHandle {
state: Arc<Mutex<State>>,
diff --git a/src/luma/cpu_handle/drop.rs b/src/cpu_handle/drop.rs
index 52c2866..a2a4853 100644
--- a/src/luma/cpu_handle/drop.rs
+++ b/src/cpu_handle/drop.rs
@@ -21,7 +21,7 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::luma::cpu_handle::CpuHandle;
+use crate::cpu_handle::CpuHandle;
use std::sync::atomic::Ordering;
diff --git a/src/luma/cpu_handle/dump_palette.rs b/src/cpu_handle/dump_palette.rs
index c59a132..826c96b 100644
--- a/src/luma/cpu_handle/dump_palette.rs
+++ b/src/cpu_handle/dump_palette.rs
@@ -21,8 +21,8 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::luma::PALETTE_LENGTH;
-use crate::luma::cpu_handle::CpuHandle;
+use crate::PALETTE_LENGTH;
+use crate::cpu_handle::CpuHandle;
use std::ptr::copy_nonoverlapping;
diff --git a/src/luma/cpu_handle/dump_video.rs b/src/cpu_handle/dump_video.rs
index c59c467..6f7eefb 100644
--- a/src/luma/cpu_handle/dump_video.rs
+++ b/src/cpu_handle/dump_video.rs
@@ -21,8 +21,8 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::luma::VIDEO_LENGTH;
-use crate::luma::cpu_handle::CpuHandle;
+use crate::VIDEO_LENGTH;
+use crate::cpu_handle::CpuHandle;
impl CpuHandle {
pub fn dump_video(&mut self, buffer: &mut [u8]) {
diff --git a/src/cpu_mode.rs b/src/cpu_mode.rs
new file mode 100644
index 0000000..9afa665
--- /dev/null
+++ b/src/cpu_mode.rs
@@ -0,0 +1,66 @@
+/*
+ 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/>.
+*/
+
+#[repr(u8)]
+#[derive(Clone, Copy)]
+pub enum CpuMode {
+ User = 0b10000,
+ FastInterruptRequest = 0b10001,
+ InterruptRequest = 0b10010,
+ Supervisor = 0b10011,
+ Abort = 0b10111,
+ Undefined = 0b11011,
+ System = 0b11111,
+}
+
+impl CpuMode {
+ pub fn from_raw(raw: u8) -> Self {
+ use CpuMode::*;
+
+ return match raw {
+ 0b10000 => User,
+ 0b10001 => FastInterruptRequest,
+ 0b10010 => InterruptRequest,
+ 0b10011 => Supervisor,
+ 0b10111 => Abort,
+ 0b11011 => Undefined,
+ 0b11111 => System,
+
+ _ => panic!("invalid cpu mode {raw:#010b}"),
+ };
+ }
+
+ pub fn name(self) -> &'static str {
+ use CpuMode::*;
+
+ return match self {
+ Abort => "abt",
+ FastInterruptRequest => "fiq",
+ InterruptRequest => "irq",
+ Supervisor => "svc",
+ System => "sys",
+ Undefined => "und",
+ User => "usr",
+ };
+ }
+}
diff --git a/src/exception.rs b/src/exception.rs
new file mode 100644
index 0000000..301b84c
--- /dev/null
+++ b/src/exception.rs
@@ -0,0 +1,64 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::cpu_mode::CpuMode;
+
+#[repr(u8)]
+#[derive(Clone, Copy)]
+pub enum Exception {
+ Reset,
+ UndefinedInstruction,
+ SoftwareInterrupt,
+ PrefetchAbort,
+ DataAbort,
+ InterruptRequest,
+ FastInterruptRequest,
+}
+
+impl Exception {
+ pub fn mode(self) -> CpuMode {
+ return match self {
+ Self::Reset => CpuMode::Supervisor,
+ Self::UndefinedInstruction => CpuMode::Undefined,
+ Self::SoftwareInterrupt => CpuMode::Supervisor,
+ Self::PrefetchAbort => CpuMode::Abort,
+ Self::DataAbort => CpuMode::Abort,
+ Self::InterruptRequest => CpuMode::InterruptRequest,
+ Self::FastInterruptRequest => CpuMode::FastInterruptRequest,
+ };
+ }
+
+ pub fn vector_address(self) -> u32 {
+ use Exception::*;
+
+ return match self {
+ Reset => 0x00000000,
+ UndefinedInstruction => 0x00000004,
+ SoftwareInterrupt => 0x00000008,
+ PrefetchAbort => 0x0000000C,
+ DataAbort => 0x00000010,
+ InterruptRequest => 0x00000018,
+ FastInterruptRequest => 0x0000001C,
+ };
+ }
+}
diff --git a/src/instruction.rs b/src/instruction.rs
new file mode 100644
index 0000000..28338c4
--- /dev/null
+++ b/src/instruction.rs
@@ -0,0 +1,65 @@
+/*
+ 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::shifter::Shifter;
+
+mod decode_arm;
+mod decode_thumb;
+
+#[derive(Clone, Copy)]
+pub enum Instruction {
+ Adc(u8, u8, Shifter, bool),
+ Add(u8, u8, Shifter, bool),
+ And(u8, u8, Shifter, bool),
+ B( i32),
+ Bic(u8, u8, Shifter, bool),
+ Bl( i32),
+ Bl0(i32),
+ Bl1(i32),
+ Bx( u8),
+ Cmn(u8 ,Shifter),
+ Cmp(u8, Shifter),
+ Eor(u8, u8, Shifter, bool),
+ Mov(u8, Shifter, bool),
+ Mul(u8, u8, Shifter, bool),
+ Mvn(u8, Shifter, bool),
+ Orr(u8, u8, Shifter, bool),
+ Rsb(u8, u8, Shifter, bool),
+ Rsc(u8, u8, Shifter, bool),
+ Sbc(u8, u8, Shifter, bool),
+ Sub(u8, u8, Shifter, bool),
+ Swi(u32),
+ Teq(u8, Shifter),
+ Tst(u8, Shifter),
+
+ // Rework in next release:
+ LoadHalfword( u8, u8, i16),
+ LoadImmediateOffset( u8, u8, i16),
+ LoadPc( u8, u16),
+ StoreByteImmediateOffset(u8, u8, i16),
+ StoreByteRegisterOffset( u8, u8, u8),
+ StoreHalfword( u8, u8, i16),
+ StoreImmediateOffset( u8, u8, i16),
+
+ Undefined,
+}
diff --git a/src/instruction/decode_arm.rs b/src/instruction/decode_arm.rs
new file mode 100644
index 0000000..9efe859
--- /dev/null
+++ b/src/instruction/decode_arm.rs
@@ -0,0 +1,201 @@
+/*
+ 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::instruction::Instruction;
+use crate::predicate::Predicate;
+use crate::shifter::Shifter;
+
+use std::hint::unreachable_unchecked;
+
+impl Instruction {
+ #[must_use]
+ pub fn decode_arm(opcode: u32) -> (Self, Predicate) {
+ use Instruction::*;
+ use Predicate::*;
+
+ let predicate = (opcode & 0b11110000000000000000000000000000).wrapping_shr(0x1C) as u8;
+ let predicate = Predicate::from_raw(predicate);
+
+ return match (opcode & 0b00001110000000000000000000000000).wrapping_shr(0x19) {
+ 0b000 => {
+ match (opcode & 0b00000000000000000000000000010000).wrapping_shr(0x4) {
+ 0b0 => {
+ let rd = (opcode & 0b00000000000000001111000000000000).wrapping_shr(0xC) as u8;
+ let rn = (opcode & 0b00000000000011110000000000000000).wrapping_shr(0x10) as u8;
+ let s = opcode & 0b00000000000100000000000000000000 != 0x0;
+
+ let shifter = Shifter::extract(opcode);
+
+ match (opcode & 0b00000001111000000000000000000000).wrapping_shr(0x15) {
+ 0b0000 => (And(rd, rn, shifter, s), predicate),
+ 0b0001 => (Eor(rd, rn, shifter, s), predicate),
+ 0b0010 => (Sub(rd, rn, shifter,s), predicate),
+ 0b0011 => (Rsb(rd, rn, shifter, s), predicate),
+ 0b0100 => (Add(rd, rn, shifter, s), predicate),
+ 0b0101 => (Adc(rd, rn, shifter, s), predicate),
+ 0b0110 => (Sbc(rd, rn, shifter, s), predicate),
+ 0b0111 => (Rsc(rd, rn, shifter, s), predicate),
+ 0b1000 => (Tst(rd, shifter), predicate),
+ 0b1001 => (Teq(rd, shifter), predicate),
+ 0b1010 => (Cmp(rd, shifter), predicate),
+ 0b1011 => (Cmn(rd, shifter), predicate),
+ 0b1100 => (Orr(rd, rn, shifter, s), predicate),
+ 0b1101 => (Mov(rd, shifter, s), predicate),
+ 0b1110 => (Bic(rd, rn, shifter, s), predicate),
+ 0b1111 => (Mvn(rd, shifter, s), predicate),
+
+ _ => unsafe { unreachable_unchecked() },
+ }
+ },
+
+ 0b1 => {
+ match (opcode & 0b00000000000000000000000010000000).wrapping_shr(0x7) {
+ 0b0 => {
+ match opcode & 0b00000001100100000000000000000000 {
+ 0b00000001000000000000000000000000 => {
+ let rm = (opcode & 0b00000000000000000000000000001111) as u8;
+
+ match (opcode & 0b00000000011000000000000000000000).wrapping_shr(0x13) | (opcode & 0b00000000000000000000000001100000).wrapping_shr(0x5) {
+ 0b0000 => (Undefined, Al),
+ 0b0001 => (Undefined, Al),
+ 0b0010 => (Undefined, Al),
+ 0b0011 => (Undefined, Al),
+ 0b0100 => (Bx(rm), predicate),
+ 0b0101 => (Undefined, Al),
+ 0b0110 => (Undefined, Al),
+ 0b0111 => (Undefined, Al),
+ 0b1000 => (Undefined, Al),
+ 0b1001 => (Undefined, Al),
+ 0b1010 => (Undefined, Al),
+ 0b1011 => (Undefined, Al),
+ 0b1100 => (Undefined, Al),
+ 0b1101 => (Undefined, Al),
+ 0b1110 => (Undefined, Al),
+ 0b1111 => (Undefined, Al),
+
+ _ => unsafe { unreachable_unchecked() },
+ }
+ },
+
+ _ => (Undefined, Al),
+ }
+ },
+
+ 0b1 => (Undefined, Al),
+
+ _ => unsafe { unreachable_unchecked() },
+ }
+ },
+
+ _ => unsafe { unreachable_unchecked() },
+ }
+ },
+
+ 0b001 => {
+ let rd = (opcode & 0b00000000000000001111000000000000).wrapping_shr(0xC) as u8;
+ let rn = (opcode & 0b00000000000011110000000000000000).wrapping_shr(0x10) as u8;
+ let s = opcode & 0b00000000000100000000000000000000 != 0x0;
+
+ let shifter = Shifter::extract(opcode);
+
+ match (opcode & 0b00000001111000000000000000000000).wrapping_shr(0x15) {
+ 0b0000 => (And(rd, rn, shifter, s), predicate),
+ 0b0001 => (Eor(rd, rn, shifter, s), predicate),
+ 0b0010 => (Sub(rd, rn, shifter, s), predicate),
+ 0b0011 => (Rsb(rd, rn, shifter, s), predicate),
+ 0b0100 => (Add(rd, rn, shifter, s), predicate),
+ 0b0101 => (Adc(rd, rn, shifter, s), predicate),
+ 0b0110 => (Sbc(rd, rn, shifter, s), predicate),
+ 0b0111 => (Rsc(rd, rn, shifter, s), predicate),
+ 0b1000 => (Tst(rn, shifter), predicate),
+ 0b1001 => (Teq(rn, shifter), predicate),
+ 0b1010 => (Cmp(rn, shifter), predicate),
+ 0b1011 => (Cmn(rn, shifter), predicate),
+ 0b1100 => (Orr(rd, rn, shifter, s), predicate),
+ 0b1101 => (Mov(rd, shifter, s), predicate),
+ 0b1110 => (Bic(rd, rn, shifter, s), predicate),
+ 0b1111 => (Mvn(rd, shifter, s), predicate),
+
+ _ => unsafe { unreachable_unchecked() },
+ }
+ },
+
+ 0b010 => {
+ let imm = (opcode & 0b00000000000000000000111111111111) as u16;
+
+ let register = (opcode & 0b00000000000000001111000000000000).wrapping_shr(0xC) as u8;
+
+ let base = (opcode & 0b00000000000011110000000000000000).wrapping_shr(0x10) as u8;
+
+ let l = opcode & 0b00000000000100000000000000000000 != 0x0;
+ let _w = opcode & 0b00000000001000000000000000000000 != 0x0;
+ let _b = opcode & 0b00000000010000000000000000000000 != 0x0;
+ let u = opcode & 0b00000000100000000000000000000000 != 0x0;
+ let _p = opcode & 0b00000001000000000000000000000000 != 0x0;
+
+ let imm = match u {
+ false => 0x0 - imm as i16,
+ true => imm as i16,
+ };
+
+ match l {
+ false => (StoreImmediateOffset(register, base, imm), predicate),
+ true => (LoadImmediateOffset( register, base, imm), predicate),
+ }
+ },
+
+ 0b011 => (Undefined, Al),
+
+ 0b100 => (Undefined, Al),
+
+ 0b101 => {
+ let offset = opcode & 0b00000000111111111111111111111111;
+ let offset = (offset << 0x8) as i32 >> 0x6;
+
+ let l = opcode & 0b00000001000000000000000000000000 != 0x0;
+
+ match l {
+ false => (B(offset), predicate),
+ true => (Bl(offset), predicate),
+ }
+ },
+
+ 0b110 => (Undefined, Al),
+
+ 0b111 => {
+ match (opcode & 0b00000001000000000000000000000000).wrapping_shr(0x18) {
+ 0b0 => (Undefined, Al),
+
+ 0b1 => {
+ let imm = opcode & 0b00000000111111111111111111111111;
+ (Swi(imm), predicate)
+ },
+
+ _ => unsafe { unreachable_unchecked() },
+ }
+ },
+
+ _ => unsafe { unreachable_unchecked() },
+ };
+ }
+}
diff --git a/src/instruction/decode_thumb.rs b/src/instruction/decode_thumb.rs
new file mode 100644
index 0000000..5735367
--- /dev/null
+++ b/src/instruction/decode_thumb.rs
@@ -0,0 +1,267 @@
+/*
+ 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::instruction::Instruction;
+use crate::predicate::Predicate;
+use crate::shifter::Shifter;
+
+use std::hint::unreachable_unchecked;
+
+impl Instruction {
+ pub fn decode_thumb(opcode: u16) -> (Self, Predicate) {
+ use Instruction::*;
+ use Predicate::*;
+ use Shifter::*;
+
+ return match (opcode & 0b1110000000000000).wrapping_shr(0xD) {
+ 0b000 => {
+ match (opcode & 0b0001100000000000).wrapping_shr(0xB) {
+ 0b11 => {
+ match (opcode & 0b0000010000000000).wrapping_shr(0xA) {
+ 0b0 => {
+ let rd = (opcode & 0b0000000000000111) as u8;
+ let rn = ((opcode & 0b0000000000111000).wrapping_shr(0x3)) as u8;
+ let rm = ((opcode & 0b0000000111000000).wrapping_shr(0x6)) as u8;
+
+ match (opcode & 0b0000001000000000).wrapping_shr(0x9) {
+ 0b0 => (Add(rd, rn, Shifter::from_register(rm), true), Al),
+ 0b1 => (Sub(rd, rn, Shifter::from_register(rm), true), Al),
+
+ _ => unsafe { unreachable_unchecked() },
+ }
+ },
+
+ 0b1 => (Undefined, Al),
+
+ _ => unsafe { unreachable_unchecked() },
+ }
+ },
+
+ pattern => {
+ let rd = (opcode & 0b0000000000000111) as u8;
+ let rm = ((opcode & 0b0000000000111000).wrapping_shr(0x3)) as u8;
+ let imm = ((opcode & 0b0000011111000000).wrapping_shr(0x6)) as u8;
+
+ match pattern {
+ 0b00 => (Mov(rd, LogicalShiftLeftImmediate(rm, imm), true), Al),
+ 0b01 => (Mov(rd, LogicalShiftRightImmediate(rm, imm), true), Al),
+ 0b10 => (Mov(rd, ArithmeticShiftRightImmediate(rm, imm), true), Al),
+
+ _ => unsafe { unreachable_unchecked() },
+ }
+ },
+ }
+ },
+
+ 0b001 => {
+ let imm = (opcode & 0b0000000011111111) as u8;
+ let rd = ((opcode & 0b0000111100000000).wrapping_shr(0x8)) as u8;
+
+ match (opcode & 0b0001100000000000).wrapping_shr(0xB) {
+ 0b00 => (Mov(rd, Immediate(imm, 0x0), true), Al),
+ 0b01 => (Cmp(rd, Immediate(imm, 0x0)), Al),
+ 0b10 => (Add(rd, rd, Immediate(imm, 0x0), true), Al),
+ 0b11 => (Sub(rd, rd, Immediate(imm, 0x0), true), Al),
+
+ _ => unsafe { unreachable_unchecked() },
+ }
+ },
+
+ 0b010 => {
+ match (opcode & 0b0001000000000000).wrapping_shr(0xC) {
+ 0b0 => {
+ match (opcode & 0b0000100000000000).wrapping_shr(0xB) {
+ 0b0 => {
+ match (opcode & 0b0000010000000000).wrapping_shr(0xA) {
+ 0b0 => {
+ let rd = (opcode & 0b0000000000000111) as u8;
+ let rm = (opcode & 0b0000000000111000).wrapping_shr(0x3) as u8;
+
+ match (opcode & 0b0000001111000000).wrapping_shr(0x6) {
+ 0b0000 => (And(rd, rd, Shifter::from_register(rm), true), Al),
+ 0b0001 => (Eor(rd, rd, Shifter::from_register(rm), true), Al),
+ 0b0010 => (Mov(rd, LogicalShiftLeftRegister(rd, rm), true), Al),
+ 0b0011 => (Mov(rd, LogicalShiftRightRegister(rd, rm), true), Al),
+ 0b0100 => (Mov(rd, ArithmeticShiftRightRegister(rd, rm), true), Al),
+ 0b0101 => (Adc(rd, rd, Shifter::from_register(rm), true), Al),
+ 0b0110 => (Sbc(rd, rd, Shifter::from_register(rm), true), Al),
+ 0b0111 => (Mov(rd, RotateRightRegister(rd, rm), true), Al),
+ 0b1000 => (Tst(rd, Shifter::from_register(rm)), Al),
+ 0b1001 => (Rsb(rd, rm, Immediate(0x0, 0x0), true), Al),
+ 0b1010 => (Cmp(rd, Shifter::from_register(rm)), Al),
+ 0b1011 => (Cmn(rd, Shifter::from_register(rm)), Al),
+ 0b1100 => (Orr(rd, rd, Shifter::from_register(rm), true), Al),
+ 0b1101 => (Mul(rd, rd, Shifter::from_register(rm), true), Al),
+ 0b1110 => (Bic(rd, rd, Shifter::from_register(rm), true), Al),
+ 0b1111 => (Mvn(rd, Shifter::from_register(rm), true), Al),
+
+ _ => unsafe { unreachable_unchecked() },
+ }
+ },
+
+ 0b1 => {
+ let h0 = opcode & 0b0000000010000000 != 0x0;
+ let h1 = opcode & 0b0000000001000000 != 0x0;
+
+ let rd = (opcode & 0b0000000000000111) as u8 | (h0 as u8) << 0x3;
+ let rm = ((opcode & 0b0000000000111000).wrapping_shr(0x3)) as u8 | (h1 as u8) << 0x3;
+
+ match (opcode & 0b0000001100000000).wrapping_shr(0x8) {
+ 0b00 => (Undefined, Al),
+
+ // Unpredictable if rd is pc or if both rd and rm
+ // are low registers.
+ 0b01 => (Cmp(rd, Shifter::from_register(rm)), Al),
+ 0b10 => (Mov(rd, Shifter::from_register(rm), true), Al),
+ // Unpredictable if h0 is true or if rd is non-
+ // zero.
+ 0b11 => (Bx(rm), Al),
+
+ _ => unsafe { unreachable_unchecked() },
+ }
+ },
+
+ _ => unsafe { unreachable_unchecked() },
+ }
+ },
+
+ 0b1 => {
+ let imm = (opcode & 0b0000000011111111) as u8;
+ let rd = ((opcode & 0b0000011100000000).wrapping_shr(0x8)) as u8;
+
+ let imm = (imm as u16).wrapping_mul(0x4);
+
+ (LoadPc(rd, imm), Al)
+ },
+
+ _ => unsafe { unreachable_unchecked() },
+ }
+ },
+
+ 0b1 => (Undefined, Al),
+
+ _ => unsafe { unreachable_unchecked() },
+ }
+ },
+
+ 0b011 => {
+ let rd = (opcode & 0b0000000000000111) as u8;
+ let rn = ((opcode & 0b0000000000111000).wrapping_shr(0x3)) as u8;
+ let imm = (opcode & 0b0000011111000000).wrapping_shr(0x6);
+ let l = opcode & 0b0000100000000000 != 0x0;
+ let b = opcode & 0b0001000000000000 != 0x0;
+
+ let imm = imm.wrapping_shl(0x2) as i16;
+
+ match l {
+ false => match b {
+ false => (StoreImmediateOffset( rd, rn, imm), Al),
+ true => (StoreByteImmediateOffset(rd, rn, imm), Al),
+ },
+
+ true => match b {
+ false => (LoadImmediateOffset( rd, rn, imm), Al),
+ true => (StoreByteImmediateOffset(rd, rn, imm), Al), // TODO
+ },
+ }
+ },
+
+ 0b100 => {
+ match (opcode & 0b0001000000000000).wrapping_shr(0xC) {
+ 0b0 => {
+ let rd = (opcode & 0b0000000000000111) as u8;
+ let rn = ((opcode & 0b0000000000111000).wrapping_shr(0x3)) as u8;
+ let imm = (opcode & 0b0000011111000000).wrapping_shr(0x6) as u8;
+ let l = opcode & 0b0000010000000000 != 0x0;
+
+ let imm = imm.wrapping_shl(0x1) as i16;
+
+ match l {
+ false => (StoreHalfword(rd, rn, imm), Al),
+ true => (LoadHalfword( rd, rn, imm), Al),
+ }
+ },
+
+ 0b1 => (Undefined, Al),
+
+ _ => unsafe { unreachable_unchecked() },
+ }
+ },
+
+ 0b101 => (Undefined, Al),
+
+ 0b110 => {
+ match (opcode & 0b0001000000000000).wrapping_shr(0xC) {
+ 0b0 => (Undefined, Al),
+
+ 0b1 => {
+ let predicate = ((opcode & 0b0000111100000000).wrapping_shr(0x8)) as u8;
+
+ let imm = opcode & 0b0000000011111111;
+
+ match predicate {
+ 0b1111 => (Swi(imm as u32), Al),
+
+ _ => {
+ let predicate = Predicate::from_raw(predicate);
+ let imm = ((imm as u32) << 0x18) as i32 >> 0x17;
+
+ (B(imm), predicate)
+ },
+ }
+ },
+
+ _ => unsafe { unreachable_unchecked() },
+ }
+ },
+
+ 0b111 => {
+ let imm = (opcode & 0b0000011111111111) as u32;
+
+ match (opcode & 0b0001100000000000).wrapping_shr(0xB) {
+ 0b00 => {
+ let imm = imm.wrapping_shl(0x15) as i32 >> 0x14;
+ (B(imm), Al)
+ },
+
+ // Undefined in ARMv4 (later blx suffix).
+ 0b01 => (Undefined, Al),
+
+ 0b10 => {
+ let imm = imm.wrapping_shl(0x15) as i32 >> 0x9;
+ (Bl0(imm), Al)
+ },
+
+ 0b11 => {
+ let imm = imm.wrapping_shl(0x1) as i32;
+ (Bl1(imm), Al)
+ },
+
+ _ => unsafe { unreachable_unchecked() },
+ }
+ },
+
+ _ => unsafe { unreachable_unchecked() },
+ };
+ }
+}
diff --git a/src/luma.rs b/src/luma.rs
index 9fd490a..d0887fd 100644
--- a/src/luma.rs
+++ b/src/luma.rs
@@ -21,18 +21,27 @@
If not, see <https://www.gnu.org/licenses/>.
*/
+extern crate ctrlc;
+extern crate sdl2;
+extern crate toml;
+
pub mod app;
pub mod configuration;
pub mod cpu;
pub mod cpu_handle;
+pub mod cpu_mode;
+pub mod exception;
pub mod instruction;
+pub mod predicate;
+pub mod shifter;
pub mod state;
pub const VERSION: (u32, u32) = (
0x0, // major
- 0x2E, // minor
+ 0x2F, // minor
);
+#[derive(Clone, Copy)]
pub enum Error {
BadAlignment( u32, u32),
InvalidArmOpcode( u32, u32),
@@ -67,7 +76,7 @@ pub const SCREEN_SIZE: (u8, u8) = (
macro_rules! log {
() => {
- eprintln!();
+ if cfg!(debug_assertions) { eprintln!() };
};
($($message: tt)*) => {{
@@ -80,11 +89,20 @@ pub(crate) use log;
macro_rules! log_assignment {
($name: expr, $value: expr) => {{
- use crate::luma::log;
+ use crate::log;
- if cfg!(debug_assertions) {
- log!("\u{1B}[3m\u{B7} \u{1B}[1m{}\u{1B}[22m = {}\u{1B}[0m", format!($name), format!($value));
- }
+ log!("\u{1B}[3m\u{B7} \u{1B}[1m{}\u{1B}[22m = {}\u{1B}[0m", $name, $value);
}};
}
pub(crate) use log_assignment;
+
+macro_rules! log_status {
+ ($($message: tt)*) => {{
+ use crate::log;
+
+ log!("({})", format!($($message)?));
+ }};
+}
+pub(crate) use log_status;
+
+fn main() { app::App::main() }
diff --git a/src/luma/cpu/boot.rs b/src/luma/cpu/boot.rs
deleted file mode 100644
index 4a089eb..0000000
--- a/src/luma/cpu/boot.rs
+++ /dev/null
@@ -1,131 +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::cpu::Cpu;
-use crate::luma::cpu_handle::CpuHandle;
-use crate::luma::instruction::Instruction;
-
-use std::sync::atomic::Ordering;
-use std::thread::{sleep, spawn};
-use std::time::{Duration, Instant};
-
-impl Cpu {
- pub fn boot(self) -> CpuHandle {
- let state = self.state.lock().unwrap();
-
- eprintln!("starting emulation at {:#010X}", state.read_register(0xF).wrapping_sub(0x8));
-
- drop(state);
-
- let state = self.state.clone();
- let dead = self.dead.clone();
-
- let handle = spawn(move || { self.run() });
-
- return CpuHandle::new(
- state,
- dead,
-
- handle,
- );
- }
-
- fn run(mut self) {
- let run_timer = Instant::now();
-
- 'main_loop: loop {
- if self.dead.load(Ordering::Relaxed) { break 'main_loop };
-
- let instruction = self.decode();
- self.execute(instruction);
-
- if cfg!(debug_assertions) { sleep(Duration::from_millis(125)) };
-
- self.cycle += 0x1;
- }
-
- let frequency = self.cycle as f64 / run_timer.elapsed().as_micros() as f64;
- eprintln!("emulated {} cycle(s) ({frequency:.9} MHz)", self.cycle);
- }
-
- #[inline(always)]
- fn execute(&mut self, instruction: Instruction) {
- use Instruction::*;
-
- match instruction {
- // 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 => {},
- };
-
- self.r#continue();
- }
-}
diff --git a/src/luma/cpu/decode_arm.rs b/src/luma/cpu/decode_arm.rs
deleted file mode 100644
index 2e7e882..0000000
--- a/src/luma/cpu/decode_arm.rs
+++ /dev/null
@@ -1,258 +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::{Error, log};
-use crate::luma::cpu::Cpu;
-use crate::luma::instruction::Instruction;
-
-use std::hint::unreachable_unchecked;
-
-impl Cpu {
- #[must_use]
- pub(super) fn decode_arm(&mut self) -> Instruction {
- let state = self.state.lock().unwrap();
-
- let address = state.read_register(0xF).wrapping_sub(0x8);
- let opcode = state.read_word(address);
-
- drop(state);
-
- log!();
- log!("\u{1B}[1m{opcode:032b}\u{1B}[0m @ \u{1B}[1m{address:08X}\u{1B}[0m - ({})", self.cycle);
-
- return decode(address, opcode);
- }
-}
-
-#[must_use]
-fn decode(address: u32, opcode: u32) -> Instruction {
- use Instruction::*;
-
- match (opcode & 0b00001110000000000000000000000000).wrapping_shr(0x19) {
- 0b000 => {
- match (opcode & 0b00000000000000000000000000010000).wrapping_shr(0x4) {
- 0b0 => {
- let _source = (opcode & 0b00000000000000000000000000001111) as u8;
-
- let _destination = (opcode & 0b00000000000000001111000000000000).wrapping_shr(0xC) as u8;
-
- let _base = (opcode & 0b00000000000011110000000000000000).wrapping_shr(0x10) as u8;
-
- let _s = opcode & 0b00000000000100000000000000000000 != 0x0;
-
- match (opcode & 0b00000001111000000000000000000000).wrapping_shr(0x15) {
- 0b0000 => {},
-
- 0b0001 => {},
-
- 0b0010 => {},
-
- 0b0011 => {},
-
- 0b0100 => {},
-
- 0b0101 => {},
-
- 0b0110 => {},
-
- 0b0111 => {},
-
- 0b1000 => {},
-
- 0b1001 => {},
-
- 0b1010 => {},
-
- 0b1011 => {},
-
- 0b1100 => {},
-
- 0b1101 => {},
-
- 0b1110 => {},
-
- 0b1111 => {},
-
- _ => unsafe { unreachable_unchecked() },
- }
- },
-
- 0b1 => {
- let source = (opcode & 0b00000000000000000000000000001111) as u8;
-
- let _shift = (opcode & 0b00000000000000000000000011110000) as u8;
-
- let _destination = (opcode & 0b00000000000000001111000000000000).wrapping_shr(0xC) as u8;
-
- let _base = (opcode & 0b00000000000011110000000000000000).wrapping_shr(0x10) as u8;
-
- let _s = opcode & 0b00000000000100000000000000000000 != 0x0;
-
- match (opcode & 0b00000001111000000000000000000000).wrapping_shr(0x15) {
- 0b0000 => {},
-
- 0b0001 => {},
-
- 0b0010 => {},
-
- 0b0011 => {},
-
- 0b0100 => {},
-
- 0b0101 => {},
-
- 0b0110 => {},
-
- 0b0111 => {},
-
- 0b1000 => {},
-
- 0b1001 => {
- // Unpredictable if any of shift, destination, or
- // base is non-zero. Unpredictable if s is true.
-
- return BranchExchange(source);
- },
-
- 0b1010 => {},
-
- 0b1011 => {},
-
- 0b1100 => {},
-
- 0b1101 => {},
-
- 0b1110 => {},
-
- 0b1111 => {},
-
- _ => unsafe { unreachable_unchecked() },
- }
- },
-
- _ => unsafe { unreachable_unchecked() },
- }
- },
-
- 0b001 => {
- let immediate = (opcode & 0b00000000000000000000000011111111) as u8;
-
- let rotate = (opcode & 0b00000000000000000000111100000000).wrapping_shr(0x8);
-
- let destination = (opcode & 0b00000000000000001111000000000000).wrapping_shr(0xC) as u8;
-
- let base = (opcode & 0b00000000000011110000000000000000).wrapping_shr(0x10) as u8;
-
- let _s = opcode & 0b00000000000100000000000000000000 != 0x0;
-
- let rotate = rotate << 0x1;
- let immediate = (immediate as u32).rotate_right(rotate);
-
- match (opcode & 0b00000001111000000000000000000000).wrapping_shr(0x15) {
- 0b0000 => {},
-
- 0b0001 => {},
-
- 0b0010 => {},
-
- 0b0011 => {},
-
- 0b0100 => return AddImmediate(destination, base, immediate),
-
- 0b0101 => {},
-
- 0b0110 => {},
-
- 0b0111 => {},
-
- 0b1000 => {},
-
- 0b1001 => {},
-
- 0b1010 => {},
-
- 0b1011 => {},
-
- 0b1100 => {},
-
- // Unpredictable if base is non-zero.
- 0b1101 => return MoveImmediate(destination, immediate),
-
- 0b1110 => {},
-
- 0b1111 => {},
-
- _ => unsafe { unreachable_unchecked() },
- }
- },
-
- 0b010 => {
- let immediate = (opcode & 0b00000000000000000000111111111111) as u16;
-
- let register = (opcode & 0b00000000000000001111000000000000).wrapping_shr(0xC) as u8;
-
- let base = (opcode & 0b00000000000011110000000000000000).wrapping_shr(0x10) as u8;
-
- let l = opcode & 0b00000000000100000000000000000000 != 0x0;
- let _w = opcode & 0b00000000001000000000000000000000 != 0x0;
- let _b = opcode & 0b00000000010000000000000000000000 != 0x0;
- let u = opcode & 0b00000000100000000000000000000000 != 0x0;
- let _p = opcode & 0b00000001000000000000000000000000 != 0x0;
-
- let offset = match u {
- false => 0x0 - immediate as i16,
- true => 0x0 + immediate as i16,
- };
-
- return match l {
- false => StoreImmediateOffset(register, base, offset),
- true => LoadImmediateOffset( register, base, offset),
- };
- },
-
- 0b011 => {},
-
- 0b100 => {},
-
- 0b101 => {
- let offset = opcode & 0b00000000111111111111111111111111;
- let offset = (offset << 0x8) as i32 >> 0x6;
-
- let l = opcode & 0b00000001000000000000000000000000 != 0x0;
-
- return match l {
- false => Branch( offset),
- true => BranchLink(offset),
- };
- },
-
- 0b110 => {},
-
- 0b111 => {},
-
- _ => unsafe { unreachable_unchecked() },
- }
-
- Error::InvalidArmOpcode(address, opcode).trap();
- return Undefined;
-}
diff --git a/src/luma/cpu/decode_thumb.rs b/src/luma/cpu/decode_thumb.rs
deleted file mode 100644
index 43cbcfa..0000000
--- a/src/luma/cpu/decode_thumb.rs
+++ /dev/null
@@ -1,312 +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::{Error, log};
-use crate::luma::cpu::{Cpu, test_predicate};
-use crate::luma::instruction::Instruction;
-
-use std::hint::unreachable_unchecked;
-
-impl Cpu {
- pub(super) fn decode_thumb(&mut self) -> Instruction {
- use Instruction::*;
-
- let state = self.state.lock().unwrap();
-
- let address = state.read_register(0xF).wrapping_sub(0x4);
- let opcode = state.read_halfword(address);
-
- let cpsr = state.read_cpsr();
-
- drop(state);
-
- log!();
- log!(" \u{1B}[1m{opcode:016b}\u{1B}[0m @ \u{1B}[1m{address:08X}\u{1B}[0m - ({})", self.cycle);
-
- match (opcode & 0b1110000000000000).wrapping_shr(0xD) {
- 0b000 => {
- match (opcode & 0b0001100000000000).wrapping_shr(0xB) {
- 0b11 => {
- match (opcode & 0b0000010000000000).wrapping_shr(0xA) {
- 0b0 => {
- let destination = (opcode & 0b0000000000000111) as u8;
-
- let base = ((opcode & 0b0000000000111000).wrapping_shr(0x3)) as u8;
-
- let register = ((opcode & 0b0000000111000000).wrapping_shr(0x6)) as u8;
-
- match (opcode & 0b0000001000000000).wrapping_shr(0x9) {
- 0b0 => return AddRegister(destination, base, register),
-
- 0b1 => return SubtractRegister(destination, base, register),
-
- _ => unsafe { unreachable_unchecked() },
- }
- },
-
- 0b1 => {
- },
-
- _ => unsafe { unreachable_unchecked() },
- }
- },
-
- pattern => {
- let destination = (opcode & 0b0000000000000111) as u8;
-
- let base = ((opcode & 0b0000000000111000).wrapping_shr(0x3)) as u8;
-
- let immediate = ((opcode & 0b0000011111000000).wrapping_shr(0x6)) as u8;
-
- match pattern {
- 0b00 => return MoveImmediateLogicalShiftLeftImmediate(destination, base, immediate),
-
- 0b01 => return MoveImmediateLogicalShiftRightImmediate(destination, base, immediate),
-
- 0b10 => return MoveImmediateArithmeticShiftRight(destination, base, immediate),
-
- _ => unsafe { unreachable_unchecked() },
- }
- },
- }
- },
-
- 0b001 => {
- let immediate = (opcode & 0b0000000011111111) as u32;
-
- let destination = ((opcode & 0b0000111100000000).wrapping_shr(0x8)) as u8;
-
- match (opcode & 0b0001100000000000).wrapping_shr(0xB) {
- 0b00 => return MoveImmediate(destination, immediate),
-
- 0b01 => return CompareImmediate(destination, immediate),
-
- 0b10 => return AddImmediate(destination, destination, immediate),
-
- 0b11 => return SubtractImmediate(destination, destination, immediate),
-
- _ => unsafe { unreachable_unchecked() },
- }
- },
-
- 0b010 => {
- match (opcode & 0b0001000000000000).wrapping_shr(0xC) {
- 0b0 => {
- match (opcode & 0b0000100000000000).wrapping_shr(0xB) {
- 0b0 => {
- match (opcode & 0b0000010000000000).wrapping_shr(0xA) {
- 0b0 => {
- let left = (opcode & 0b0000000000000111) as u8;
-
- let right = (opcode & 0b0000000000111000).wrapping_shr(0x3) as u8;
-
- match (opcode & 0b0000001111000000).wrapping_shr(0x6) {
- 0b0000 => return AndRegister(left, left, right),
-
- 0b0001 => return ExclusiveOrRegister(left, left, right),
-
- 0b0010 => return MoveRegisterLogicalShiftLeftImmediate(left, left, right),
-
- 0b0011 => return MoveRegisterLogicalShiftRightImmediate(left, left, right),
-
- 0b0100 => return MoveRegisterArithmeticShiftRight(left, left, right),
-
- 0b0101 => return AddCarryRegister(left, left, right),
-
- 0b0110 => return SubtractCarryRegister(left, left, right),
-
- 0b0111 => return MoveRegisterRotateRight(left, left, right),
-
- 0b1000 => return TestRegister(left, right),
-
- 0b1001 => return ReverseSubtractImmediate(left, right, 0x0),
-
- 0b1010 => return CompareRegister(left, right),
-
- 0b1011 => return CompareNegativeRegister(left, right),
-
- 0b1100 => return LogicalOrRegister(left, right, right),
-
- 0b1101 => return MultiplyRegister(left, left, right),
-
- 0b1110 => return BitClearRegister(left, left, right),
-
- 0b1111 => return MoveNotRegister(left, right),
-
- _ => unsafe { unreachable_unchecked() },
- }
- },
-
- 0b1 => {
- let h0 = opcode & 0b0000000010000000 != 0x0;
- let h1 = opcode & 0b0000000001000000 != 0x0;
-
- let destination = (opcode & 0b0000000000000111) as u8;
- let destination = destination | (h0 as u8) << 0x3;
-
- let source = ((opcode & 0b0000000000111000).wrapping_shr(0x3)) as u8;
- let source = source | (h1 as u8) << 0x3;
-
- match (opcode & 0b0000001100000000).wrapping_shr(0x8) {
- 0b00 => {},
-
- // Unpredictable if destination is pc or if both it
- // and source are low registers.
- 0b01 => return CompareRegister(destination, source),
-
- 0b10 => return MoveRegister(destination, source),
-
- // Unpredictable if h0 is true or if destination is
- // non-zero.
- 0b11 => return BranchExchange(source),
-
- _ => unsafe { unreachable_unchecked() },
- }
- },
-
- _ => unsafe { unreachable_unchecked() },
- }
- },
-
- 0b1 => {
- let immediate = opcode & 0b0000000011111111;
-
- let destination = ((opcode & 0b0000011100000000).wrapping_shr(0x8)) as u8;
-
- let offset = (immediate as i16).wrapping_mul(0x4);
-
- return LoadPc(destination, offset);
- },
-
- _ => unsafe { unreachable_unchecked() },
- }
- },
-
- 0b1 => {},
-
- _ => unsafe { unreachable_unchecked() },
- }
- },
-
- 0b011 => {
- let register = (opcode & 0b0000000000000111) as u8;
-
- let base = ((opcode & 0b0000000000111000).wrapping_shr(0x3)) as u8;
-
- let immediate = (opcode & 0b0000011111000000).wrapping_shr(0x6);
-
- let _l = opcode & 0b0000100000000000 != 0x0;
- let b = opcode & 0b0001000000000000 != 0x0;
-
- let offset = immediate.wrapping_shl(0x2) as i16;
-
- return match b {
- false => StoreImmediateOffset( register, base, offset),
- true => StoreByteImmediateOffset(register, base, offset),
- };
- },
-
- 0b100 => {
- match (opcode & 0b0001000000000000).wrapping_shr(0xC) {
- 0b0 => {
- let register = (opcode & 0b0000000000000111) as u8;
-
- let base = ((opcode & 0b0000000000111000).wrapping_shr(0x3)) as u8;
-
- let immediate = (opcode & 0b0000011111000000).wrapping_shr(0x6);
-
- let l = opcode & 0b0000010000000000 != 0x0;
-
- let offset = immediate.wrapping_shl(0x1) as i16;
-
- return match l {
- false => StoreHalfword(register, base, offset),
- true => LoadHalfword( register, base, offset),
- };
- },
-
- 0b1 => {},
-
- _ => unsafe { unreachable_unchecked() },
- }
- },
-
- 0b101 => {
- },
-
- 0b110 => {
- match (opcode & 0b0001000000000000).wrapping_shr(0xC) {
- 0b0 => {},
-
- 0b1 => {
- let predicate = ((opcode & 0b0000111100000000).wrapping_shr(0x8)) as u8;
-
- if !test_predicate!(cpsr, predicate) { return Undefined };
-
- let immediate = opcode & 0b0000000011111111;
-
- let offset = ((immediate as u32) << 0x18) as i32 >> 0x17;
-
- return Branch(offset);
- },
-
- _ => unsafe { unreachable_unchecked() },
- }
- },
-
- 0b111 => {
- let immediate = (opcode & 0b0000011111111111) as u32;
-
- match (opcode & 0b0001100000000000).wrapping_shr(0xB) {
- 0b00 => {
- let offset = immediate.wrapping_shl(0x15) as i32 >> 0x14;
-
- return Branch(offset);
- },
-
- // Undefined in ARMv4 (later blx suffix).
- 0b01 => {},
-
- 0b10 => {
- let offset = immediate.wrapping_shl(0x15) as i32 >> 0x9;
-
- return BranchLinkPrefix(offset);
- },
-
- 0b11 => {
- let offset = immediate.wrapping_shl(0x1) as i32;
-
- return BranchLinkSuffix(offset);
- },
-
- _ => unsafe { unreachable_unchecked() },
- }
- },
-
- _ => unsafe { unreachable_unchecked() },
- }
-
- Error::InvalidThumbOpcode(address, opcode).trap();
- return Undefined;
- }
-}
diff --git a/src/luma/cpu/isa_arithmetic.rs b/src/luma/cpu/isa_arithmetic.rs
deleted file mode 100644
index 3fe8830..0000000
--- a/src/luma/cpu/isa_arithmetic.rs
+++ /dev/null
@@ -1,122 +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 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
deleted file mode 100644
index f2ed822..0000000
--- a/src/luma/cpu/isa_bitwise.rs
+++ /dev/null
@@ -1,75 +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 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/isa_branch.rs b/src/luma/cpu/isa_branch.rs
deleted file mode 100644
index a887a35..0000000
--- a/src/luma/cpu/isa_branch.rs
+++ /dev/null
@@ -1,89 +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, exchange};
-
-impl Cpu {
- 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(imm);
-
- log!("b {target:#X}");
-
- target = target.wrapping_add(self.instruction_size);
- state.write_register(0xF, target);
- }
-
- pub(super) fn isa_branch_exchange(&mut self, rm: u8) {
- let mut state = self.state.lock().unwrap();
-
- log!("bx r{rm}");
-
- let mut target = state.read_register(rm);
-
- let t = target & 0b00000000000000000000000000000001 != 0x0;
- exchange!(self, t);
-
- let cpsr = state.read_cpsr() & 0b11111111111111111111111111011111 | (t as u32) << 0x5;
- state.write_cpsr(cpsr);
-
- target &= 0b11111111111111111111111111111110;
- target = target.wrapping_add(self.instruction_size);
- state.write_register(0xF, target);
- }
-
- 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(imm);
-
- log!("bl {target:#X}");
-
- target = target.wrapping_add(self.instruction_size);
- state.write_register(0xF, target);
- }
-
- 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(imm);
-
- state.write_register(0xE, target);
- }
-
- 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(imm);
- let link_target = state.read_register(0xF).wrapping_sub(0x2);
-
- log!("bl {branch_target:#X}");
-
- state.write_register(0xE, link_target);
-
- branch_target = branch_target.wrapping_add(0x2);
- state.write_register(0xF, branch_target);
- }
-}
diff --git a/src/luma/cpu/isa_logic.rs b/src/luma/cpu/isa_logic.rs
deleted file mode 100644
index a624f57..0000000
--- a/src/luma/cpu/isa_logic.rs
+++ /dev/null
@@ -1,111 +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 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_move.rs b/src/luma/cpu/isa_move.rs
deleted file mode 100644
index a9edac8..0000000
--- a/src/luma/cpu/isa_move.rs
+++ /dev/null
@@ -1,136 +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 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(rd, immediate);
- }
-
- 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(rd, value);
- }
-
- 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(rd, value);
- }
-
- 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(rd, value);
- }
-
- 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(rd, value);
- }
-
- 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();
-
- let base_value = state.read_register(base) as i32;
- let shift_value = state.read_register(shift);
-
- let value = (base_value.wrapping_shr(shift_value)) as u32;
- state.write_register(rd, value);
- }
-
- 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();
-
- let base_value = state.read_register(base);
- let shift_value = state.read_register(shift);
-
- let value = base_value.wrapping_shl(shift_value);
- state.write_register(rd, value);
- }
-
- 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();
-
- let base_value = state.read_register(base);
- let shift_value = state.read_register(shift);
-
- let value = base_value.wrapping_shr(shift_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/test_predicate.rs b/src/luma/cpu/test_predicate.rs
deleted file mode 100644
index b6ed46a..0000000
--- a/src/luma/cpu/test_predicate.rs
+++ /dev/null
@@ -1,74 +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/>.
-*/
-
-macro_rules! test_predicate {
- ($cpsr: expr, $predicate: expr) => {{
- // True if predicate applies.
-
- // Code Id. Predicates
- // 0 eq Z==1
- // 1 ne Z==0
- // 2 cs, hs C==1
- // 3 cc, lo C==0
- // 4 mi N==1
- // 5 pl N==0
- // 6 vs V==1
- // 7 vc V==0
- // 8 hi C==1 && Z==0
- // 9 ls C==0 && Z==1
- // A ge N==V
- // B lt N!=V
- // C gt Z==0 && N==V
- // D le Z==1 && N!=V
- // E al true
- // F nv false
- //
- // Note that nv is always invalid on ARMv4.
-
- let v = $cpsr & 0b00010000000000000000000000000000 != 0x0;
- let c = $cpsr & 0b00100000000000000000000000000000 != 0x0;
- let z = $cpsr & 0b01000000000000000000000000000000 != 0x0;
- let n = $cpsr & 0b10000000000000000000000000000000 != 0x0;
-
- match $predicate {
- 0x0 => z,
- 0x1 => !z,
- 0x2 => c,
- 0x3 => !c,
- 0x4 => n,
- 0x5 => !n,
- 0x6 => v,
- 0x7 => !v,
- 0x8 => c && !z,
- 0x9 => !c && z,
- 0xA => n == v,
- 0xB => n != v,
- 0xC => !z && n == v,
- 0xD => z && n != v,
- 0xE => false,
- 0xF => true,
- _ => unreachable!(),
- }
- }}
-}
-pub(crate) use test_predicate;
diff --git a/src/luma/instruction.rs b/src/luma/instruction.rs
deleted file mode 100644
index 9de3d32..0000000
--- a/src/luma/instruction.rs
+++ /dev/null
@@ -1,66 +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/>.
-*/
-
-#[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/main.rs b/src/main.rs
deleted file mode 100644
index 4478a81..0000000
--- a/src/main.rs
+++ /dev/null
@@ -1,74 +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/>.
-*/
-
-extern crate ctrlc;
-extern crate sdl2;
-extern crate toml;
-
-mod luma;
-
-use crate::luma::VERSION;
-use crate::luma::app::App;
-use crate::luma::configuration::Configuration;
-
-use std::env::{args, var};
-use std::process::exit;
-
-fn main() {
- eprintln!();
- eprintln!("\u{1B}[1mluma\u{1B}[0m {:X}.{:X}", VERSION.0, VERSION.1);
- eprintln!("Copyright 2021-2023 \u{1B}[1mGabriel Bj\u{F8}rnager Jensen\u{1B}[0m.");
- eprintln!();
-
- let path = if let Some(path) = args().nth(0x1) { path }
- else { default_configuration_path() };
-
- let configuration = match Configuration::load(&path) {
- Ok( configuration) => configuration,
- Err(message) => panic!("unable to load configuration: {message}"),
- };
-
- let app = match App::init(configuration) {
- Ok( app) => app,
- Err(message) => panic!("unable to initialise application: {message}"),
- };
-
- let result = app.run();
-
- if let Err(ref message) = result { eprintln!("\u{1B}[1m\u{1B}[91merror\u{1B}[0m: {message}") };
-
- exit(match result {
- Ok( ..) => 0x0,
- Err(..) => 0x1,
- });
-}
-
-fn default_configuration_path() -> String {
- let home = match var("HOME") {
- Ok( path) => path,
- Err(..) => "/".to_string(),
- };
-
- let path = home + "/.luma.toml";
- return path;
-}
diff --git a/src/predicate.rs b/src/predicate.rs
new file mode 100644
index 0000000..737ff7f
--- /dev/null
+++ b/src/predicate.rs
@@ -0,0 +1,101 @@
+/*
+ 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 std::mem::transmute;
+
+#[derive(Clone, Copy)]
+#[repr(u8)]
+pub enum Predicate {
+ Eq = 0b0000,
+ Ne = 0b0001,
+ Cs = 0b0010, // Hs
+ Cc = 0b0011, // Lo
+ Mi = 0b0100,
+ Pl = 0b0101,
+ Vs = 0b0110,
+ Vc = 0b0111,
+ Hi = 0b1000,
+ Ls = 0b1001,
+ Ge = 0b1010,
+ Lt = 0b1011,
+ Gt = 0b1100,
+ Le = 0b1101,
+ Al = 0b1110,
+ Nv = 0b1111,
+}
+
+impl Predicate {
+ pub fn from_raw(mut raw: u8) -> Self {
+ raw &= 0b00001111;
+ return unsafe { transmute(raw) };
+ }
+
+ pub fn test(self, cpsr: u32) -> bool {
+ let v = cpsr & 0b00010000000000000000000000000000 != 0x0;
+ let c = cpsr & 0b00100000000000000000000000000000 != 0x0;
+ let z = cpsr & 0b01000000000000000000000000000000 != 0x0;
+ let n = cpsr & 0b10000000000000000000000000000000 != 0x0;
+
+ use Predicate::*;
+ return match self {
+ Eq => z,
+ Ne => !z,
+ Cs => c,
+ Cc => !c,
+ Mi => n,
+ Pl => !n,
+ Vs => v,
+ Vc => !v,
+ Hi => c && !z,
+ Ls => !c && z,
+ Ge => n == v,
+ Lt => n != v,
+ Gt => !z && n == v,
+ Le => z && n != v,
+ Al => true,
+ Nv => false, // Unpredictable in ARMv4.
+ };
+ }
+
+ pub fn code(self) -> &'static str {
+ use Predicate::*;
+ return match self {
+ Eq => "eq",
+ Ne => "ne",
+ Cs => "cs",
+ Cc => "cc",
+ Mi => "mi",
+ Pl => "pl",
+ Vs => "vs",
+ Vc => "vc",
+ Hi => "hi",
+ Ls => "ls",
+ Ge => "ge",
+ Lt => "lt",
+ Gt => "gt",
+ Le => "le",
+ Al => "al",
+ Nv => "nv",
+ };
+ }
+}
diff --git a/src/shifter.rs b/src/shifter.rs
new file mode 100644
index 0000000..b8db2ce
--- /dev/null
+++ b/src/shifter.rs
@@ -0,0 +1,65 @@
+/*
+ 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 std::fmt::{Display, Formatter};
+
+mod extract;
+
+#[derive(Clone, Copy)]
+pub enum Shifter {
+ Immediate( u8, u8),
+ ArithmeticShiftRightImmediate(u8, u8),
+ ArithmeticShiftRightRegister( u8, u8),
+ LogicalShiftLeftImmediate( u8, u8),
+ LogicalShiftLeftRegister( u8, u8),
+ LogicalShiftRightImmediate( u8, u8),
+ LogicalShiftRightRegister( u8, u8),
+ RotateRightImmediate( u8, u8),
+ RotateRightRegister( u8, u8),
+ RotateRightExtend( u8),
+}
+
+impl Shifter {
+ #[inline(always)]
+ pub const fn from_register(register: u8) -> Shifter { Shifter::LogicalShiftLeftImmediate(register, 0x0) }
+}
+
+impl Display for Shifter {
+ #[must_use]
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ use Shifter::*;
+
+ return match *self {
+ Immediate( imm, rot) => write!(f, "#{:#X}", (imm as u32).rotate_right(rot as u32)),
+ ArithmeticShiftRightImmediate(rm, imm) => write!(f, "r{rm}, ASR #{imm:#X}"),
+ ArithmeticShiftRightRegister( rm, rs) => write!(f, "r{rm}, ASR r{rs}"),
+ LogicalShiftLeftImmediate( rm, imm) => write!(f, "r{rm}, LSL #{imm:#X}"),
+ LogicalShiftLeftRegister( rm, rs) => write!(f, "r{rm}, LSL r{rs}"),
+ LogicalShiftRightImmediate( rm, imm) => write!(f, "r{rm}, LSR #{imm:#X}"),
+ LogicalShiftRightRegister( rm, rs) => write!(f, "r{rm}, LSR r{rs}"),
+ RotateRightImmediate( rm, imm) => write!(f, "r{rm}, ROR #{imm:#X}"),
+ RotateRightRegister( rm, rs) => write!(f, "r{rm}, ROR r{rs}"),
+ RotateRightExtend( rm) => write!(f, "RXX"),
+ };
+ }
+}
diff --git a/src/shifter/extract.rs b/src/shifter/extract.rs
new file mode 100644
index 0000000..d4ff6e0
--- /dev/null
+++ b/src/shifter/extract.rs
@@ -0,0 +1,92 @@
+/*
+ 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::shifter::Shifter;
+
+use std::hint::unreachable_unchecked;
+
+impl Shifter {
+ pub fn extract(opcode: u32) -> Self {
+ use Shifter::*;
+
+ match opcode & 0b00000010000000000000000000000000 != 0x0 {
+ false => {
+ let rm = (opcode & 0b00000000000000000000000000001111) as u8;
+
+ let shift = (opcode & 0b00000000000000000000000001100000).wrapping_shr(0x5) as u8;
+ let register_shift = opcode & 0b00000000000000000000000000010000 != 0x0;
+
+ match register_shift {
+ false => {
+ let imm = (opcode & 0b00000000000000000000111110000000).wrapping_shr(0x7) as u8;
+
+ return match shift {
+ 0b11 => match imm {
+ 0x0 => RotateRightExtend( rm),
+ imm => RotateRightImmediate(rm, imm),
+ },
+
+ shift => {
+ match shift {
+ 0b00 => LogicalShiftLeftImmediate(rm, imm),
+
+ 0b01 => LogicalShiftRightImmediate(rm, match imm {
+ 0x0 => 0x20,
+ imm => imm,
+ }),
+
+ 0b10 => ArithmeticShiftRightImmediate(rm, match imm {
+ 0x0 => 0x20,
+ imm => imm,
+ }),
+
+ _ => unsafe { unreachable_unchecked() },
+ }
+ },
+ };
+ },
+
+ true => {
+ let rs = (opcode & 0b0000000000000000000111100000000).wrapping_shr(0x8) as u8;
+
+ return match shift {
+ 0b00 => LogicalShiftLeftRegister( rm, rs),
+ 0b01 => LogicalShiftRightRegister( rm, rs),
+ 0b10 => ArithmeticShiftRightRegister(rm, rs),
+ 0b11 => RotateRightRegister( rm, rs),
+
+ _ => unsafe { unreachable_unchecked() },
+ };
+ },
+ };
+ },
+
+ true => {
+ let imm = (opcode & 0b00000000000000000000000011111111) as u8;
+ let rot = (opcode & 0b00000000000000000000111100000000).wrapping_shr(0x7) as u8;
+
+ return Immediate(imm, rot);
+ },
+ };
+ }
+}
diff --git a/src/luma/state.rs b/src/state.rs
index 1b293ef..6697059 100644
--- a/src/luma/state.rs
+++ b/src/state.rs
@@ -21,17 +21,23 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::luma::{BOOTLOADER_LENGTH, IMAGE_LENGTH, PALETTE_LENGTH, VIDEO_LENGTH};
+use crate::{BOOTLOADER_LENGTH, IMAGE_LENGTH, PALETTE_LENGTH, VIDEO_LENGTH};
+use crate::cpu_mode::CpuMode;
use std::slice::{from_raw_parts, from_raw_parts_mut};
+pub mod bank;
pub mod new;
pub mod read;
+pub mod shifter_value;
pub mod write;
pub struct State {
- registers: [u32; 0x10],
- cpsr: u32,
+ registers: [*mut u32; 0x10],
+ banks: Box<[[u32; 0x10]; 0x6]>,
+
+ cpsr: u32,
+ spsr: [u32; 0x6],
memory: Vec::<u32>,
}
@@ -100,4 +106,21 @@ impl State {
return slice;
}
+
+ #[must_use]
+ fn spsr_index(mode: CpuMode) -> usize {
+ use CpuMode::*;
+
+ return match mode {
+ User => 0x0,
+ System => 0x0,
+ FastInterruptRequest => 0x1,
+ InterruptRequest => 0x2,
+ Supervisor => 0x3,
+ Abort => 0x4,
+ Undefined => 0x5,
+ };
+ }
}
+
+unsafe impl Send for State { }
diff --git a/src/state/bank.rs b/src/state/bank.rs
new file mode 100644
index 0000000..7cab594
--- /dev/null
+++ b/src/state/bank.rs
@@ -0,0 +1,89 @@
+/*
+ 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 register 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 register Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero register Public License along with Luma.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::{log, log_status};
+use crate::cpu_mode::CpuMode;
+use crate::state::State;
+
+impl State {
+ pub fn bank(&mut self, mode: CpuMode) {
+ log_status!("banking to {}", mode.name());
+
+ let bank_index = bank_index(mode);
+
+ self.registers[0x0] = &mut self.banks[bank_index.0][ 0x0] as *mut u32;
+ self.registers[0x1] = &mut self.banks[bank_index.1][ 0x1] as *mut u32;
+ self.registers[0x2] = &mut self.banks[bank_index.2][ 0x2] as *mut u32;
+ self.registers[0x3] = &mut self.banks[bank_index.3][ 0x3] as *mut u32;
+ self.registers[0x4] = &mut self.banks[bank_index.4][ 0x4] as *mut u32;
+ self.registers[0x5] = &mut self.banks[bank_index.5][ 0x5] as *mut u32;
+ self.registers[0x6] = &mut self.banks[bank_index.6][ 0x6] as *mut u32;
+ self.registers[0x7] = &mut self.banks[bank_index.7][ 0x7] as *mut u32;
+ self.registers[0x8] = &mut self.banks[bank_index.8][ 0x8] as *mut u32;
+ self.registers[0x9] = &mut self.banks[bank_index.9][ 0x9] as *mut u32;
+ self.registers[0xA] = &mut self.banks[bank_index.10][0xA] as *mut u32;
+ self.registers[0xB] = &mut self.banks[bank_index.11][0xB] as *mut u32;
+ self.registers[0xC] = &mut self.banks[bank_index.12][0xC] as *mut u32;
+ self.registers[0xD] = &mut self.banks[bank_index.13][0xD] as *mut u32;
+ self.registers[0xE] = &mut self.banks[bank_index.14][0xE] as *mut u32;
+ self.registers[0xF] = &mut self.banks[bank_index.15][0xF] as *mut u32;
+
+ log!("new register layout:");
+ log!(" r0: {:#010X} r4: {:#010X} r8: {:#010X} r12: {:#010X}", self.read_register(0x0), self.read_register(0x4), self.read_register(0x8), self.read_register(0xC));
+ log!(" r1: {:#010X} r5: {:#010X} r9: {:#010X} sp: {:#010X}", self.read_register(0x1), self.read_register(0x5), self.read_register(0x9), self.read_register(0xD));
+ log!(" r2: {:#010X} r6: {:#010X} r10: {:#010X} lr: {:#010X}", self.read_register(0x2), self.read_register(0x6), self.read_register(0xA), self.read_register(0xE));
+ log!(" r3: {:#010X} r7: {:#010X} r11: {:#010X} pc: {:#010X}", self.read_register(0x3), self.read_register(0x7), self.read_register(0xB), self.read_register(0xF));
+ }
+}
+
+#[must_use]
+fn bank_index(mode: CpuMode) -> (
+ usize, // r0
+ usize, // r1
+ usize, // r2
+ usize, // r3
+ usize, // r4
+ usize, // r5
+ usize, // r6
+ usize, // r7
+ usize, // r8
+ usize, // r9
+ usize, // r10
+ usize, // r11
+ usize, // r12
+ usize, // sp
+ usize, // lr
+ usize, // pc
+) {
+ use CpuMode::*;
+
+ return match mode {
+ User => (0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0),
+ System => (0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0),
+ FastInterruptRequest => (0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0),
+ InterruptRequest => (0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x0),
+ Supervisor => (0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0),
+ Abort => (0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x0),
+ Undefined => (0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x5, 0x0),
+ };
+}
diff --git a/src/luma/state/new.rs b/src/state/new.rs
index 39caaf6..bb6f87e 100644
--- a/src/luma/state/new.rs
+++ b/src/state/new.rs
@@ -21,40 +21,55 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::luma::MEMORY_LENGTH;
-use crate::luma::state::State;
+use crate::log;
+use crate::MEMORY_LENGTH;
+use crate::cpu_mode::CpuMode;
+use crate::state::State;
+
+use std::ptr::null_mut;
impl State {
#[must_use]
pub fn new() -> Self {
- let registers: [u32; 0x10] = [
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x08000008,
- ];
-
- let cpsr = 0b00000000000000000000000000001111;
+ log!("initialising new state");
+
+ let banks = Box::new([DEFAULT_REGISTER_VALUES; 0x6]);
+
+ let cpsr = 0b00000000000000000000000000011111;
let memory: Vec::<u32> = vec![0x0; MEMORY_LENGTH as usize / 0x4];
- return Self {
- registers: registers,
- cpsr: cpsr,
+ let mut state = Self {
+ registers: [null_mut(); 0x10],
+ banks: banks,
+
+ cpsr: cpsr,
+ spsr: [0b00000000000000000000000000000000; 0x6],
memory: memory,
};
+
+ state.bank(CpuMode::System);
+
+ return state;
}
}
+
+const DEFAULT_REGISTER_VALUES: [u32; 0x10] = [
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000008,
+];
diff --git a/src/luma/state/read.rs b/src/state/read.rs
index f22dc86..927d528 100644
--- a/src/luma/state/read.rs
+++ b/src/state/read.rs
@@ -21,8 +21,13 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::luma::{Error, MEMORY_LENGTH};
-use crate::luma::state::State;
+use crate::{Error, MEMORY_LENGTH};
+use crate::cpu_mode::CpuMode;
+use crate::state::State;
+
+const MAX_BYTE_ADDRESS: u32 = MEMORY_LENGTH;
+const MAX_HALFWORD_ADDRESS: u32 = MEMORY_LENGTH - 0x1;
+const MAX_WORD_ADDRESS: u32 = MEMORY_LENGTH - 0x3;
impl State {
#[inline(always)]
@@ -31,13 +36,13 @@ impl State {
// Limit to 0..=15.
let index = (register & 0b00001111) as usize;
- return unsafe { *self.registers.get_unchecked(index) };
+ 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; }
+ if address > MAX_WORD_ADDRESS { Error::OutOfBounds( address).trap(); return 0xFFFFFFFF; }
+ if address % 0x4 != 0x0 { Error::BadAlignment(address, 0x4).trap(); return 0xFFFFFFFF; }
unsafe {
let pointer = (self.memory.as_ptr() as *const u8).add(address as usize) as *const u32;
@@ -47,8 +52,8 @@ impl State {
#[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; }
+ if address > MAX_HALFWORD_ADDRESS { Error::OutOfBounds( address).trap(); return 0xFFFF; }
+ if address % 0x2 != 0x0 { Error::BadAlignment(address, 0x2).trap(); return 0xFFFF; }
unsafe {
let pointer = (self.memory.as_ptr() as *const u8).add(address as usize) as *const u16;
@@ -58,7 +63,7 @@ impl State {
#[must_use]
pub fn read_byte(&self, address: u32) -> u8 {
- if address > MEMORY_LENGTH - 0x1 { Error::OutOfBounds(address).trap(); return 0x00; }
+ if address > MAX_BYTE_ADDRESS { Error::OutOfBounds(address).trap(); return 0xFF; }
unsafe {
let pointer = (self.memory.as_ptr() as *const u8).add(address as usize);
@@ -71,4 +76,10 @@ impl State {
pub fn read_cpsr(&self) -> u32 {
return self.cpsr;
}
+
+ #[inline(always)]
+ #[must_use]
+ pub fn read_spsr(&self, mode: CpuMode) -> u32 {
+ return unsafe { *self.spsr.get_unchecked(Self::spsr_index(mode)) };
+ }
}
diff --git a/src/state/shifter_value.rs b/src/state/shifter_value.rs
new file mode 100644
index 0000000..58821fe
--- /dev/null
+++ b/src/state/shifter_value.rs
@@ -0,0 +1,130 @@
+/*
+ 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::shifter::Shifter;
+use crate::state::State;
+
+impl State {
+ #[must_use]
+ pub fn shifter_value(&self, shifter: Shifter) -> (u32, bool) {
+ use Shifter::*;
+
+ let c = self.read_cpsr() & 0b00100000000000000000000000000000 != 0x0;
+
+ let (value, carry) = match shifter {
+ Immediate(imm, rot) => {
+ let result = (imm as u32).rotate_right(rot as u32);
+ let c = if rot == 0x0 { c } else { result & 0b10000000000000000000000000000000 != 0x0 };
+
+ (result, c)
+ },
+
+ ArithmeticShiftRightImmediate(rm, imm) => {
+ let rm_value = self.read_register(rm);
+
+ let result = (rm_value as i32).wrapping_shr(imm as u32) as u32;
+ let c = false; // TODO
+
+ (result, c)
+ },
+
+ ArithmeticShiftRightRegister(rm, rs) => {
+ let rm_value = self.read_register(rm);
+ let rs_value = self.read_register(rs);
+
+ let result = (rm_value as i32).wrapping_shr(rs_value) as u32;
+ let c = false; // TODO
+
+ (result, c)
+ },
+
+ LogicalShiftLeftImmediate(rm, imm) => {
+ let rm_value = self.read_register(rm);
+
+ let result = rm_value.wrapping_shl(imm as u32);
+ let c = false; // TODO
+
+ (result, c)
+ },
+
+ LogicalShiftLeftRegister(rm, rs) => {
+ let rm_value = self.read_register(rm);
+ let rs_value = self.read_register(rs);
+
+ let result = rm_value.wrapping_shl(rs_value);
+ let c = false; // TODO
+
+ (result, c)
+ },
+
+ LogicalShiftRightImmediate(rm, imm) => {
+ let rm_value = self.read_register(rm);
+
+ let result = rm_value.wrapping_shr(imm as u32);
+ let c = false; // TODO
+
+ (result, c)
+ },
+
+ LogicalShiftRightRegister(rm, rs) => {
+ let rm_value = self.read_register(rm);
+ let rs_value = self.read_register(rs);
+
+ let result = rm_value.wrapping_shr(rs_value);
+ let c = false; // TODO
+
+ (result, c)
+ },
+
+ RotateRightImmediate(rm, imm) => {
+ let rm_value = self.read_register(rm);
+
+ let result = rm_value.rotate_right(imm as u32);
+ let c = false; // TODO
+
+ (result, c)
+ },
+
+ RotateRightRegister(rm, rs) => {
+ let rm_value = self.read_register(rm);
+ let rs_value = self.read_register(rs);
+
+ let result = rm_value.rotate_right(rs_value);
+ let c = false; // TODO
+
+ (result, c)
+ },
+
+ RotateRightExtend(rm) => {
+ let rm_value = self.read_register(rm);
+
+ let result = 0x0_u32; // TODO
+ let c = rm_value & 0b00000000000000000000000000000001 != 0x0;
+
+ (result, c)
+ },
+ };
+
+ return (value, carry);
+ }
+}
diff --git a/src/luma/state/write.rs b/src/state/write.rs
index bd2b031..10955e2 100644
--- a/src/luma/state/write.rs
+++ b/src/state/write.rs
@@ -21,8 +21,9 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::luma::{Error, log_assignment, MEMORY_LENGTH};
-use crate::luma::state::{address_unused, State};
+use crate::{Error, log_assignment, MEMORY_LENGTH};
+use crate::cpu_mode::CpuMode;
+use crate::state::{address_unused, State};
macro_rules! read_only {
($address: expr) => {{
@@ -39,15 +40,15 @@ macro_rules! read_only {
impl State {
#[inline(always)]
pub fn write_register(&mut self, register: u8, value: u32) {
- log_assignment!("r{register}", "{value:#010X}");
+ log_assignment!(format!("r{register}"), format!("{value:#010X}"));
let index = (register & 0b00001111) as usize;
- unsafe { *self.registers.get_unchecked_mut(index) = value };
+ unsafe { **self.registers.get_unchecked_mut(index) = value };
}
pub fn write_word(&mut self, address: u32, value: u32) {
- log_assignment!("{address:#010X}", "{value:#010X}");
+ log_assignment!(format!("{address:#010X}"), format!("{value:#010X}"));
if address > MEMORY_LENGTH - 0x4 { Error::OutOfBounds( address).trap(); return; }
if address % 0x4 != 0x0 { Error::BadAlignment(address, 0x4).trap(); return; }
@@ -61,7 +62,7 @@ impl State {
}
pub fn write_halfword(&mut self, address: u32, value: u16) {
- log_assignment!("{address:#010X}", "{value:#06X}");
+ log_assignment!(format!("{address:#010X}"), format!("{value:#06X}"));
if address > MEMORY_LENGTH - 0x2 { Error::OutOfBounds( address).trap(); return; }
if address % 0x2 != 0x0 { Error::BadAlignment(address, 0x2).trap(); return; }
@@ -75,7 +76,7 @@ impl State {
}
pub fn write_byte(&mut self, address: u32, value: u8) {
- log_assignment!("{address:#010X}", "{value:#04X}");
+ log_assignment!(format!("{address:#010X}"), format!("{value:#04X}"));
if address > MEMORY_LENGTH - 0x1 { Error::OutOfBounds(address).trap(); return; }
@@ -108,9 +109,17 @@ impl State {
};
}
+ #[inline(always)]
pub fn write_cpsr(&mut self, value: u32) {
- log_assignment!("cpsr", "{value:#034b}");
+ log_assignment!("cpsr", format!("{value:#034b}"));
self.cpsr = value;
}
+
+ #[inline(always)]
+ pub fn write_spsr(&mut self, mode: CpuMode, value: u32) {
+ log_assignment!("spsr", format!("{value:#034b}"));
+
+ unsafe { *self.spsr.get_unchecked_mut(Self::spsr_index(mode)) = value };
+ }
}
diff --git a/test.s b/test.s
index 8497dab..691c4b5 100644
--- a/test.s
+++ b/test.s
@@ -1,49 +1,47 @@
-.syntax unified
-
.cpu arm7tdmi
.arm
b _start
-.byte 0x24,0xFF,0xAE,0x51
-.byte 0x69,0x9A,0xA2,0x21
-.byte 0x3D,0x84,0x82,0x0A
-.byte 0x84,0xE4,0x09,0xAD
-.byte 0x11,0x24,0x8B,0x98
-.byte 0xC0,0x81,0x7F,0x21
-.byte 0xA3,0x52,0xBE,0x19
-.byte 0x93,0x09,0xCE,0x20
-.byte 0x10,0x46,0x4A,0x4A
-.byte 0xF8,0x27,0x31,0xEC
-.byte 0x58,0xC7,0xE8,0x33
-.byte 0x82,0xE3,0xCE,0xBF
-.byte 0x85,0xF4,0xDF,0x94
-.byte 0xCE,0x4B,0x09,0xC1
-.byte 0x94,0x56,0x8A,0xC0
-.byte 0x13,0x72,0xA7,0xFC
-.byte 0x9F,0x84,0x4D,0x73
-.byte 0xA3,0xCA,0x9A,0x61
-.byte 0x58,0x97,0xA3,0x27
-.byte 0xFC,0x03,0x98,0x76
-.byte 0x23,0x1D,0xC7,0x61
-.byte 0x03,0x04,0xAE,0x56
-.byte 0xBF,0x38,0x84,0x00
-.byte 0x40,0xA7,0x0E,0xFD
-.byte 0xFF,0x52,0xFE,0x03
-.byte 0x6F,0x95,0x30,0xF1
-.byte 0x97,0xFB,0xC0,0x85
-.byte 0x60,0xD6,0x80,0x25
-.byte 0xA9,0x63,0xBE,0x03
-.byte 0x01,0x4E,0x38,0xE2
-.byte 0xF9,0xA2,0x34,0xFF
-.byte 0xBB,0x3E,0x03,0x44
-.byte 0x78,0x00,0x90,0xCB
-.byte 0x88,0x11,0x3A,0x94
-.byte 0x65,0xC0,0x7C,0x63
-.byte 0x87,0xF0,0x3C,0xAF
-.byte 0xD6,0x25,0xE4,0x8B
-.byte 0x38,0x0A,0xAC,0x72
-.byte 0x21,0xD4,0xF8,0x07
+.byte 0x24, 0xFF, 0xAE, 0x51
+.byte 0x69, 0x9A, 0xA2, 0x21
+.byte 0x3D, 0x84, 0x82, 0x0A
+.byte 0x84, 0xE4, 0x09, 0xAD
+.byte 0x11, 0x24, 0x8B, 0x98
+.byte 0xC0, 0x81, 0x7F, 0x21
+.byte 0xA3, 0x52, 0xBE, 0x19
+.byte 0x93, 0x09, 0xCE, 0x20
+.byte 0x10, 0x46, 0x4A, 0x4A
+.byte 0xF8, 0x27, 0x31, 0xEC
+.byte 0x58, 0xC7, 0xE8, 0x33
+.byte 0x82, 0xE3, 0xCE, 0xBF
+.byte 0x85, 0xF4, 0xDF, 0x94
+.byte 0xCE, 0x4B, 0x09, 0xC1
+.byte 0x94, 0x56, 0x8A, 0xC0
+.byte 0x13, 0x72, 0xA7, 0xFC
+.byte 0x9F, 0x84, 0x4D, 0x73
+.byte 0xA3, 0xCA, 0x9A, 0x61
+.byte 0x58, 0x97, 0xA3, 0x27
+.byte 0xFC, 0x03, 0x98, 0x76
+.byte 0x23, 0x1D, 0xC7, 0x61
+.byte 0x03, 0x04, 0xAE, 0x56
+.byte 0xBF, 0x38, 0x84, 0x00
+.byte 0x40, 0xA7, 0x0E, 0xFD
+.byte 0xFF, 0x52, 0xFE, 0x03
+.byte 0x6F, 0x95, 0x30, 0xF1
+.byte 0x97, 0xFB, 0xC0, 0x85
+.byte 0x60, 0xD6, 0x80, 0x25
+.byte 0xA9, 0x63, 0xBE, 0x03
+.byte 0x01, 0x4E, 0x38, 0xE2
+.byte 0xF9, 0xA2, 0x34, 0xFF
+.byte 0xBB, 0x3E, 0x03, 0x44
+.byte 0x78, 0x00, 0x90, 0xCB
+.byte 0x88, 0x11, 0x3A, 0x94
+.byte 0x65, 0xC0, 0x7C, 0x63
+.byte 0x87, 0xF0, 0x3C, 0xAF
+.byte 0xD6, 0x25, 0xE4, 0x8B
+.byte 0x38, 0x0A, 0xAC, 0x72
+.byte 0x21, 0xD4, 0xF8, 0x07
.ascii "LUMA\x0\x0\x0\x0\x0\x0\x0\x0"
@@ -80,8 +78,15 @@ nop
.arm
.func
_start:
- ldr lr, =start
- bx lr
+ mov r9, #0x1
+ rsbs r9, #0x0
+ adds r9, #0x2
+ mov r9, r9, LSL #0x1F
+ subs r9, #0x1
+ mov sp, #0x450
+ swi #0x3 @ Comment on other emulators.
+ ldr lr, =start
+ bx lr
.endfunc
.thumb
@@ -99,8 +104,8 @@ start:
ldr r1, .backgroundColour
strh r1, [r0]
ldr r1, .paletteIndex
- lsls r1, 0x1 @ Multiply by two.
- adds r0, r1 @ Apply index.
+ lsl r1, #0x1 @ Multiply by two.
+ add r0, r1 @ Apply index.
ldr r1, .foregroundColour
strh r1, [r0]
@@ -111,25 +116,29 @@ start:
@ - r3 is the last pixel address.
ldr r0, .videoAddr
ldr r1, .paletteIndex
- bics r2, r2
- movs r3, 0x4B
- lsls r3, 0x9
- adds r3, r0
+ bic r2, r2
+ mov r3, #0x4B
+ lsl r3, #0x9
+ add r3, r0
@ Plot pixels:
@ TO-DO: Plot correctly (bytewise).
.loop:
strb r1, [r0]
- adds r0, r2
- adds r2, 0x1
+ add r0, r2
+ add r2, #0x1
cmp r0, r3
bge .restart
+ @bge .stop
b .loop @ Repeat loop.
@ Restart loop:
.restart:
ldr r0, .videoAddr
b .loop
+
+.stop:
+ b .stop
.endfunc
.align