From 08bfb19b97d1f3267bd7ec3a66476815de4e4752 Mon Sep 17 00:00:00 2001
From: Gabriel Bjørnager Jensen
Date: Fri, 17 Nov 2023 20:25:22 +0100
Subject: 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;
---
.gitignore | 3 +
CHANGELOG.md | 16 ++
Cargo.toml | 4 +-
README.md | 2 +-
bootloader.ld | 12 ++
bootloader.s | 80 +++++++++
luma.svg | 27 ++++
make_test.sh | 12 +-
src/agb.rs | 25 +++
src/app.rs | 47 ++++++
src/app/check_events.rs | 52 ++++++
src/app/draw_video.rs | 71 ++++++++
src/app/init.rs | 88 ++++++++++
src/app/load.rs | 96 +++++++++++
src/app/main.rs | 69 ++++++++
src/app/run.rs | 58 +++++++
src/app/sync_video.rs | 44 +++++
src/configuration.rs | 36 +++++
src/configuration/load.rs | 100 ++++++++++++
src/configuration/validate.rs | 32 ++++
src/cpu.rs | 104 ++++++++++++
src/cpu/alu_exit_exception.rs | 38 +++++
src/cpu/boot.rs | 70 ++++++++
src/cpu/continue.rs | 33 ++++
src/cpu/enter_exception.rs | 59 +++++++
src/cpu/exchange.rs | 45 ++++++
src/cpu/execute.rs | 69 ++++++++
src/cpu/exit_exception.rs | 42 +++++
src/cpu/fetch_arm.rs | 55 +++++++
src/cpu/fetch_thumb.rs | 54 +++++++
src/cpu/isa_adc.rs | 58 +++++++
src/cpu/isa_add.rs | 55 +++++++
src/cpu/isa_and.rs | 53 ++++++
src/cpu/isa_b.rs | 38 +++++
src/cpu/isa_bic.rs | 53 ++++++
src/cpu/isa_bl.rs | 62 +++++++
src/cpu/isa_bx.rs | 45 ++++++
src/cpu/isa_cmn.rs | 47 ++++++
src/cpu/isa_cmp.rs | 47 ++++++
src/cpu/isa_eor.rs | 53 ++++++
src/cpu/isa_memory.rs | 122 ++++++++++++++
src/cpu/isa_mov.rs | 53 ++++++
src/cpu/isa_mul.rs | 52 ++++++
src/cpu/isa_mvn.rs | 52 ++++++
src/cpu/isa_orr.rs | 53 ++++++
src/cpu/isa_rsb.rs | 55 +++++++
src/cpu/isa_rsc.rs | 58 +++++++
src/cpu/isa_sbc.rs | 58 +++++++
src/cpu/isa_sub.rs | 55 +++++++
src/cpu/isa_swi.rs | 34 ++++
src/cpu/isa_teq.rs | 45 ++++++
src/cpu/isa_tst.rs | 45 ++++++
src/cpu/sync_cycle.rs | 43 +++++
src/cpu/take_state.rs | 36 +++++
src/cpu_handle.rs | 55 +++++++
src/cpu_handle/drop.rs | 37 +++++
src/cpu_handle/dump_palette.rs | 36 +++++
src/cpu_handle/dump_video.rs | 34 ++++
src/cpu_mode.rs | 66 ++++++++
src/exception.rs | 64 ++++++++
src/instruction.rs | 65 ++++++++
src/instruction/decode_arm.rs | 201 +++++++++++++++++++++++
src/instruction/decode_thumb.rs | 267 ++++++++++++++++++++++++++++++
src/luma.rs | 30 +++-
src/luma/agb.rs | 25 ---
src/luma/app.rs | 46 ------
src/luma/app/check_events.rs | 52 ------
src/luma/app/draw_video.rs | 71 --------
src/luma/app/init.rs | 88 ----------
src/luma/app/load.rs | 96 -----------
src/luma/app/run.rs | 58 -------
src/luma/app/sync_video.rs | 44 -----
src/luma/configuration.rs | 36 -----
src/luma/configuration/load.rs | 100 ------------
src/luma/configuration/validate.rs | 32 ----
src/luma/cpu.rs | 78 ---------
src/luma/cpu/boot.rs | 131 ---------------
src/luma/cpu/continue.rs | 33 ----
src/luma/cpu/decode_arm.rs | 258 -----------------------------
src/luma/cpu/decode_thumb.rs | 312 ------------------------------------
src/luma/cpu/exchange.rs | 39 -----
src/luma/cpu/isa_arithmetic.rs | 122 --------------
src/luma/cpu/isa_bitwise.rs | 75 ---------
src/luma/cpu/isa_branch.rs | 89 ----------
src/luma/cpu/isa_logic.rs | 111 -------------
src/luma/cpu/isa_memory.rs | 122 --------------
src/luma/cpu/isa_move.rs | 136 ----------------
src/luma/cpu/test_predicate.rs | 74 ---------
src/luma/cpu_handle.rs | 55 -------
src/luma/cpu_handle/drop.rs | 37 -----
src/luma/cpu_handle/dump_palette.rs | 36 -----
src/luma/cpu_handle/dump_video.rs | 34 ----
src/luma/instruction.rs | 66 --------
src/luma/state.rs | 103 ------------
src/luma/state/new.rs | 60 -------
src/luma/state/read.rs | 74 ---------
src/luma/state/write.rs | 116 --------------
src/main.rs | 74 ---------
src/predicate.rs | 101 ++++++++++++
src/shifter.rs | 65 ++++++++
src/shifter/extract.rs | 92 +++++++++++
src/state.rs | 126 +++++++++++++++
src/state/bank.rs | 89 ++++++++++
src/state/new.rs | 75 +++++++++
src/state/read.rs | 85 ++++++++++
src/state/shifter_value.rs | 130 +++++++++++++++
src/state/write.rs | 125 +++++++++++++++
test.s | 111 +++++++------
108 files changed, 4505 insertions(+), 2947 deletions(-)
create mode 100644 bootloader.ld
create mode 100644 bootloader.s
create mode 100644 luma.svg
create mode 100644 src/agb.rs
create mode 100644 src/app.rs
create mode 100644 src/app/check_events.rs
create mode 100644 src/app/draw_video.rs
create mode 100644 src/app/init.rs
create mode 100644 src/app/load.rs
create mode 100644 src/app/main.rs
create mode 100644 src/app/run.rs
create mode 100644 src/app/sync_video.rs
create mode 100644 src/configuration.rs
create mode 100644 src/configuration/load.rs
create mode 100644 src/configuration/validate.rs
create mode 100644 src/cpu.rs
create mode 100644 src/cpu/alu_exit_exception.rs
create mode 100644 src/cpu/boot.rs
create mode 100644 src/cpu/continue.rs
create mode 100644 src/cpu/enter_exception.rs
create mode 100644 src/cpu/exchange.rs
create mode 100644 src/cpu/execute.rs
create mode 100644 src/cpu/exit_exception.rs
create mode 100644 src/cpu/fetch_arm.rs
create mode 100644 src/cpu/fetch_thumb.rs
create mode 100644 src/cpu/isa_adc.rs
create mode 100644 src/cpu/isa_add.rs
create mode 100644 src/cpu/isa_and.rs
create mode 100644 src/cpu/isa_b.rs
create mode 100644 src/cpu/isa_bic.rs
create mode 100644 src/cpu/isa_bl.rs
create mode 100644 src/cpu/isa_bx.rs
create mode 100644 src/cpu/isa_cmn.rs
create mode 100644 src/cpu/isa_cmp.rs
create mode 100644 src/cpu/isa_eor.rs
create mode 100644 src/cpu/isa_memory.rs
create mode 100644 src/cpu/isa_mov.rs
create mode 100644 src/cpu/isa_mul.rs
create mode 100644 src/cpu/isa_mvn.rs
create mode 100644 src/cpu/isa_orr.rs
create mode 100644 src/cpu/isa_rsb.rs
create mode 100644 src/cpu/isa_rsc.rs
create mode 100644 src/cpu/isa_sbc.rs
create mode 100644 src/cpu/isa_sub.rs
create mode 100644 src/cpu/isa_swi.rs
create mode 100644 src/cpu/isa_teq.rs
create mode 100644 src/cpu/isa_tst.rs
create mode 100644 src/cpu/sync_cycle.rs
create mode 100644 src/cpu/take_state.rs
create mode 100644 src/cpu_handle.rs
create mode 100644 src/cpu_handle/drop.rs
create mode 100644 src/cpu_handle/dump_palette.rs
create mode 100644 src/cpu_handle/dump_video.rs
create mode 100644 src/cpu_mode.rs
create mode 100644 src/exception.rs
create mode 100644 src/instruction.rs
create mode 100644 src/instruction/decode_arm.rs
create mode 100644 src/instruction/decode_thumb.rs
delete mode 100644 src/luma/agb.rs
delete mode 100644 src/luma/app.rs
delete mode 100644 src/luma/app/check_events.rs
delete mode 100644 src/luma/app/draw_video.rs
delete mode 100644 src/luma/app/init.rs
delete mode 100644 src/luma/app/load.rs
delete mode 100644 src/luma/app/run.rs
delete mode 100644 src/luma/app/sync_video.rs
delete mode 100644 src/luma/configuration.rs
delete mode 100644 src/luma/configuration/load.rs
delete mode 100644 src/luma/configuration/validate.rs
delete mode 100644 src/luma/cpu.rs
delete mode 100644 src/luma/cpu/boot.rs
delete mode 100644 src/luma/cpu/continue.rs
delete mode 100644 src/luma/cpu/decode_arm.rs
delete mode 100644 src/luma/cpu/decode_thumb.rs
delete mode 100644 src/luma/cpu/exchange.rs
delete mode 100644 src/luma/cpu/isa_arithmetic.rs
delete mode 100644 src/luma/cpu/isa_bitwise.rs
delete mode 100644 src/luma/cpu/isa_branch.rs
delete mode 100644 src/luma/cpu/isa_logic.rs
delete mode 100644 src/luma/cpu/isa_memory.rs
delete mode 100644 src/luma/cpu/isa_move.rs
delete mode 100644 src/luma/cpu/test_predicate.rs
delete mode 100644 src/luma/cpu_handle.rs
delete mode 100644 src/luma/cpu_handle/drop.rs
delete mode 100644 src/luma/cpu_handle/dump_palette.rs
delete mode 100644 src/luma/cpu_handle/dump_video.rs
delete mode 100644 src/luma/instruction.rs
delete mode 100644 src/luma/state.rs
delete mode 100644 src/luma/state/new.rs
delete mode 100644 src/luma/state/read.rs
delete mode 100644 src/luma/state/write.rs
delete mode 100644 src/main.rs
create mode 100644 src/predicate.rs
create mode 100644 src/shifter.rs
create mode 100644 src/shifter/extract.rs
create mode 100644 src/state.rs
create mode 100644 src/state/bank.rs
create mode 100644 src/state/new.rs
create mode 100644 src/state/read.rs
create mode 100644 src/state/shifter_value.rs
create mode 100644 src/state/write.rs
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 @@
+
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/agb.rs b/src/agb.rs
new file mode 100644
index 0000000..01a77b8
--- /dev/null
+++ b/src/agb.rs
@@ -0,0 +1,25 @@
+/*
+ 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 .
+*/
+
+pub mod arm;
+pub mod thumb;
diff --git a/src/app.rs b/src/app.rs
new file mode 100644
index 0000000..45b6e90
--- /dev/null
+++ b/src/app.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 .
+*/
+
+use sdl2::Sdl;
+use sdl2::render::WindowCanvas;
+use std::sync::Arc;
+use std::sync::atomic::AtomicBool;
+
+pub mod check_events;
+pub mod draw_video;
+pub mod init;
+pub mod load;
+pub mod main;
+pub mod run;
+pub mod sync_video;
+
+pub struct App {
+ bootloader: String,
+ image: String,
+
+ scale: u32,
+
+ got_terminate: Arc::,
+
+ sdl: Sdl,
+ canvas: WindowCanvas,
+}
diff --git a/src/app/check_events.rs b/src/app/check_events.rs
new file mode 100644
index 0000000..1dd9551
--- /dev/null
+++ b/src/app/check_events.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 .
+*/
+
+use crate::app::App;
+
+use sdl2::event::Event;
+use std::sync::atomic::Ordering;
+
+impl App {
+ pub fn check_events(&mut self) -> Result {
+ // Return true if we should quit.
+
+ let mut event_pump = match self.sdl.event_pump() {
+ Ok(pump) => pump,
+ _ => return Err("unable to get event pump".to_string()),
+ };
+
+ if self.got_terminate.load(Ordering::Relaxed) {
+ eprintln!("got terminate");
+ return Ok(true)
+ };
+
+ for event in event_pump.poll_iter() {
+ match event {
+ Event::Quit {..} => return Ok(true),
+ _ => {},
+ };
+ }
+
+ return Ok(false);
+ }
+}
diff --git a/src/app/draw_video.rs b/src/app/draw_video.rs
new file mode 100644
index 0000000..a0d2016
--- /dev/null
+++ b/src/app/draw_video.rs
@@ -0,0 +1,71 @@
+/*
+ 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 .
+*/
+
+use crate::SCREEN_SIZE;
+use crate::app::App;
+
+use sdl2::pixels::Color;
+use sdl2::rect::Rect;
+
+impl App {
+ pub fn draw_video(&mut self, video: &[u8], agb_palette: &[u16]) {
+ // TO-DO: Honour video mode.
+
+ let mut palette: [Color; 0x100] = [Color::RGB(0x00, 0x00, 0x00); 0x100];
+
+ for (index, element) in palette.iter_mut().enumerate() {
+ let value = unsafe { *agb_palette.get_unchecked(index) };
+
+ let colour = decode_colour(value);
+ *element = colour;
+ }
+
+ for pixel_y in 0x0..SCREEN_SIZE.1 {
+ for pixel_x in 0x0..SCREEN_SIZE.0 {
+ let pixel = pixel_y as usize * SCREEN_SIZE.0 as usize + pixel_x as usize;
+
+ let value = video[pixel];
+ let colour = palette[value as usize];
+ self.canvas.set_draw_color(colour);
+
+ let square = Rect::new(
+ (pixel_x as u32 * self.scale) as i32,
+ (pixel_y as u32 * self.scale) as i32,
+ self.scale,
+ self.scale,
+ );
+ self.canvas.fill_rect(square).unwrap();
+ }
+ }
+
+ self.canvas.present();
+ }
+}
+
+fn decode_colour(colour: u16) -> Color {
+ let red = ((colour & 0b0000000000011111) as f32 / 31.0 * 255.0) as u8;
+ let green = ((colour & 0b0000001111100000) as f32 / 992.0 * 255.0) as u8;
+ let blue = ((colour & 0b0111110000000000) as f32 / 31744.0 * 255.0) as u8;
+
+ return Color::RGB(red, green, blue);
+}
diff --git a/src/app/init.rs b/src/app/init.rs
new file mode 100644
index 0000000..c7a8653
--- /dev/null
+++ b/src/app/init.rs
@@ -0,0 +1,88 @@
+/*
+ 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 .
+*/
+
+use crate::{SCREEN_SIZE, VERSION};
+use crate::app::App;
+use crate::configuration::Configuration;
+
+use sdl2::pixels::Color;
+use sdl2::render::BlendMode;
+use std::sync::Arc;
+use std::sync::atomic::{AtomicBool, Ordering};
+
+impl App {
+ pub fn init(configuration: Configuration) -> Result {
+ let got_terminate = Arc::new(AtomicBool::new(false));
+
+ match ctrlc::set_handler({
+ let got_terminate = got_terminate.clone();
+ move || got_terminate.store(true, Ordering::Relaxed)
+ }) {
+ Err(..) => return Err("unable to set signal handler".to_string()),
+ _ => {},
+ };
+
+ let sdl = match sdl2::init() {
+ Ok( sdl) => sdl,
+ Err(..) => return Err("unable to initialise sdl2".to_string()),
+ };
+
+ let sdl_video = match sdl.video() {
+ Ok( video) => video,
+ Err(..) => return Err("unable to initialise video".to_string()),
+ };
+
+ let window_title = format!("Luma {:X}.{:X}", VERSION.0, VERSION.1);
+
+ let mut window_builder = sdl_video.window(&window_title, SCREEN_SIZE.0 as u32 * configuration.scale, SCREEN_SIZE.1 as u32 * configuration.scale);
+ window_builder.position_centered();
+
+ let window = match window_builder.build() {
+ Ok( window) => window,
+ Err(..) => return Err("unable to open window".to_string()),
+ };
+
+ let mut canvas = match window.into_canvas().build() {
+ Ok( canvas) => canvas,
+ Err(..) => return Err("unable to build canvas".to_string()),
+ };
+
+ canvas.set_blend_mode(BlendMode::Blend);
+
+ let clear_colour = Color::RGB(0x00, 0x00, 0x00);
+ canvas.set_draw_color(clear_colour);
+ canvas.clear();
+ canvas.present();
+
+ return Ok(App {
+ bootloader: configuration.bootloader,
+ image: configuration.image,
+
+ scale: configuration.scale,
+
+ got_terminate: got_terminate,
+
+ sdl: sdl,
+ canvas: canvas,
+ });
+ }
+}
diff --git a/src/app/load.rs b/src/app/load.rs
new file mode 100644
index 0000000..24a2385
--- /dev/null
+++ b/src/app/load.rs
@@ -0,0 +1,96 @@
+/*
+ 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 .
+*/
+
+use crate::VERSION;
+use crate::app::App;
+use crate::state::State;
+
+use std::fs::File;
+use std::io::Read;
+
+impl App {
+ pub fn load(&mut self, state: &mut State) -> Result<(), String> {
+ eprintln!("loading booatloader \"{}\"", self.bootloader);
+
+ let mut bootloader = match File::open(&self.bootloader) {
+ Ok(file) => file,
+ _ => return Err("unable to open bootloader".to_string()),
+ };
+
+ if let Err(..) = bootloader.read(state.bootloader_buffer()) { return Err("unable to read bootloader".to_string()) };
+
+ eprintln!("loading image \"{}\"", self.image);
+
+ let mut image = match File::open(&self.image) {
+ Ok(file) => file,
+ _ => return Err("unable to open image".to_string()),
+ };
+
+ match image.read(state.image_buffer()) {
+ Err(..) => return Err("unable to read image".to_string()),
+ _ => {},
+ };
+
+ let title = get_title(&state.image_buffer()[0xA0..0xAC]);
+ let id = get_id(&state.image_buffer()[0xAC..0xB0]);
+ let version = state.image_buffer()[0xBC];
+
+ eprintln!("loaded image \"{title}\" ({id}) v.{version}");
+
+ self.canvas.window_mut().set_title(&format!("Luma {:X}.{:X} - {title}", VERSION.0, VERSION.1)).unwrap();
+
+ return Ok(());
+ }
+}
+
+fn get_title(data: &[u8]) -> String {
+ let mut title = String::with_capacity(0xC);
+
+ for raw in data {
+ let character = match char::from_u32(*raw as u32) {
+ Some('\u{0000}') => break,
+ Some(character) => character,
+ None => '?',
+ };
+
+ title.push(character);
+ }
+
+ return title;
+}
+
+fn get_id(data: &[u8]) -> String {
+ let mut id = String::with_capacity(0xC);
+
+ for raw in data {
+ let character = match char::from_u32(*raw as u32) {
+ Some('\u{0000}') => break,
+ Some(character) => character,
+ None => '?',
+ };
+
+ id.push(character);
+ }
+
+ return id;
+}
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 .
+*/
+
+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/app/run.rs b/src/app/run.rs
new file mode 100644
index 0000000..5bc8dc7
--- /dev/null
+++ b/src/app/run.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 .
+*/
+
+use crate::{PALETTE_LENGTH, VIDEO_LENGTH};
+use crate::app::App;
+use crate::cpu::Cpu;
+use crate::state::State;
+
+use std::time::Instant;
+
+impl App {
+ pub fn run(mut self) -> Result<(), String> {
+ let mut state = State::new();
+
+ self.load(&mut state)?;
+
+ let cpu = Cpu::new(state);
+
+ let mut cpu = cpu.boot();
+
+ let mut video_buffer: Vec:: = vec![0x0; VIDEO_LENGTH as usize];
+ let mut palette_buffer: Vec:: = vec![0x0; (PALETTE_LENGTH / 0x2) as usize];
+
+ 'main_loop: loop {
+ let frame_start = Instant::now();
+
+ if self.check_events()? { break 'main_loop };
+
+ cpu.dump_video( &mut video_buffer[..]);
+ cpu.dump_palette(&mut palette_buffer[..]);
+ self.draw_video(&video_buffer[..], &palette_buffer[..]);
+
+ self.sync_video(frame_start);
+ }
+
+ return Ok(());
+ }
+}
diff --git a/src/app/sync_video.rs b/src/app/sync_video.rs
new file mode 100644
index 0000000..bb863d4
--- /dev/null
+++ b/src/app/sync_video.rs
@@ -0,0 +1,44 @@
+/*
+ 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 .
+*/
+
+use crate::app::App;
+
+use std::thread::sleep;
+use std::time::{Duration, Instant};
+
+impl App {
+ pub fn sync_video(&self, frame_start: Instant) {
+ // Courtesy of TASVideos:
+ // 59.7275005696058 Hz
+
+ const FRAME_DURATION: u64 = 0xFF7932;
+ let frame_duration = Duration::from_nanos(FRAME_DURATION);
+
+ let remaining = match frame_duration.checked_sub(frame_start.elapsed()) {
+ Some(value) => value,
+ None => Duration::from_secs(0x0),
+ };
+
+ sleep(remaining);
+ }
+}
diff --git a/src/configuration.rs b/src/configuration.rs
new file mode 100644
index 0000000..183901a
--- /dev/null
+++ b/src/configuration.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 .
+*/
+
+mod load;
+mod validate;
+
+pub struct Configuration {
+ pub bootloader: String,
+ pub image: String,
+
+ pub scale: u32,
+}
+
+impl Configuration {
+ pub const VERSION: u32 = 0x0;
+}
diff --git a/src/configuration/load.rs b/src/configuration/load.rs
new file mode 100644
index 0000000..a0065fc
--- /dev/null
+++ b/src/configuration/load.rs
@@ -0,0 +1,100 @@
+/*
+ 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 .
+*/
+
+use crate::configuration::Configuration;
+
+extern crate toml;
+
+use std::fs::read_to_string;
+use std::str::FromStr;
+use toml::{Table, Value};
+
+impl Configuration {
+ pub fn load(path: &str) -> Result {
+ eprintln!("loading configuration at \"{path}\"");
+
+ let configuration_text = match read_to_string(path) {
+ Ok( content) => content,
+ _ => return Err("unable to read file".to_string()),
+ };
+
+ let base_table = match Table::from_str(&configuration_text) {
+ Ok( table) => table,
+ _ => return Err("unable to parse configuration".to_string()),
+ };
+
+ let luma_table = get_table(&base_table, "luma")?;
+ let device_table = get_table(&base_table, "device")?;
+ let video_table = get_table(&base_table, "video")?;
+
+ let version = get_integer(&luma_table, "version")?;
+
+ if version < Self::VERSION { return Err(format!("ancient version - got {}, expected {}", version, Self::VERSION)) }
+ if version > Self::VERSION { return Err(format!("out-of-date version - got {}, expected {}", version, Self::VERSION)) }
+
+ let bootloader = get_string(&device_table, "bootloader")?;
+ let image = get_string(&device_table, "image")?;
+
+ let scale = get_integer(&video_table, "scale")?;
+
+ let configuration = Configuration {
+ bootloader: bootloader.clone(),
+ image: image.clone(),
+
+ scale: scale,
+ };
+
+ configuration.validate()?;
+ return Ok(configuration);
+ }
+}
+
+fn get_value<'a>(table: &'a Table, name: &str) -> Option<&'a Value> {
+ if !table.contains_key(name) { return None };
+
+ return Some(&table[name]);
+}
+
+fn get_table<'a>(table: &'a Table, name: &str) -> Result<&'a Table, String> {
+ return match get_value(table, name) {
+ Some(Value::Table(table)) => Ok(table),
+ Some(_) => Err(format!("\"{name}\" should be a section")),
+ _ => Err("section \"{name}\" is required".to_string()),
+ };
+}
+
+fn get_integer(table: &Table, name: &str) -> Result {
+ return match get_value(table, name) {
+ Some(Value::Integer(value)) => Ok(*value as u32),
+ Some(_) => Err(format!("\"{name}\" should be an integer")),
+ _ => Err("missing integer \"{name}\"".to_string()),
+ };
+}
+
+fn get_string<'a>(table: &'a Table, name: &str) -> Result<&'a String, String> {
+ return match get_value(table, name) {
+ Some(Value::String(string)) => Ok(string),
+ Some(_) => Err(format!("\"{name}\" should be a string")),
+ _ => Err("missing string \"{name}\"".to_string()),
+ };
+}
diff --git a/src/configuration/validate.rs b/src/configuration/validate.rs
new file mode 100644
index 0000000..26aee61
--- /dev/null
+++ b/src/configuration/validate.rs
@@ -0,0 +1,32 @@
+/*
+ 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 .
+*/
+
+use crate::configuration::Configuration;
+
+impl Configuration {
+ pub(super) fn validate(&self) -> Result<(), String> {
+ if self.scale < 0x1 { return Err("scale must be at least 1".to_string()) };
+
+ return Ok(());
+ }
+}
diff --git a/src/cpu.rs b/src/cpu.rs
new file mode 100644
index 0000000..8b31d59
--- /dev/null
+++ b/src/cpu.rs
@@ -0,0 +1,104 @@
+/*
+ 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 .
+*/
+
+use crate::instruction::Instruction;
+use crate::predicate::Predicate;
+use crate::state::State;
+
+use std::sync::{Arc, Mutex};
+use std::sync::atomic::AtomicBool;
+
+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 take_state;
+
+//
+
+#[allow(unused_imports)]
+pub use alu_exit_exception::*;
+
+#[allow(unused_imports)]
+pub use exchange::*;
+
+#[allow(unused_imports)]
+pub use take_state::*;
+
+pub type Fetcher = fn(&mut Cpu) -> (Instruction, Predicate, u32);
+
+pub struct Cpu {
+ state: Arc>,
+ cycle: u64,
+ dead: Arc,
+
+ instruction_size: u32,
+ fetcher: Fetcher,
+}
+
+impl Cpu {
+ pub fn new(state: State) -> Self {
+ return Self {
+ state: Arc::new(Mutex::new(state)),
+ cycle: 0x0,
+ dead: Arc::new(AtomicBool::new(false)),
+
+ instruction_size: 0x4,
+ fetcher: Self::fetch_arm,
+ };
+ }
+
+ #[inline(always)]
+ #[must_use]
+ 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 .
+*/
+
+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 .
+*/
+
+use crate::{log, log_status};
+use crate::cpu::Cpu;
+use crate::cpu_handle::CpuHandle;
+
+use std::sync::atomic::Ordering;
+use std::thread::{sleep, spawn};
+use std::time::{Duration, Instant};
+
+impl Cpu {
+ pub fn boot(self) -> CpuHandle {
+ eprintln!("starting emulation at {:#010X}", self.state.lock().unwrap().read_register(0xF).wrapping_sub(0x8));
+
+ let state = self.state.clone();
+ let dead = self.dead.clone();
+
+ let handle = spawn(move || { self.run() });
+
+ return CpuHandle::new(
+ state,
+ dead,
+
+ handle,
+ );
+ }
+
+ fn run(mut self) {
+ let run_timer = Instant::now();
+
+ 'main_loop: loop {
+ if self.dead.load(Ordering::Relaxed) { break 'main_loop };
+
+ let (instruction, predicate, cpsr) = self.fetch();
+ match predicate.test(cpsr) {
+ false => log_status!("skipping due to predicate ({})", predicate.code()),
+ true => self.execute(instruction),
+ };
+ self.r#continue();
+
+ if cfg!(debug_assertions) { sleep(Duration::from_millis(125)) };
+
+ self.cycle += 0x1;
+ }
+
+ let frequency = self.cycle as f64 / run_timer.elapsed().as_micros() as f64;
+ eprintln!("emulated {} cycle(s) ({frequency:.9} MHz)", self.cycle);
+ }
+}
diff --git a/src/cpu/continue.rs b/src/cpu/continue.rs
new file mode 100644
index 0000000..7b96713
--- /dev/null
+++ b/src/cpu/continue.rs
@@ -0,0 +1,33 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see .
+*/
+
+use crate::cpu::{Cpu, take_state_mut};
+
+impl Cpu {
+ pub(super) fn r#continue(&mut self) {
+ take_state_mut!(state, self);
+
+ let pc = state.read_register(0xF).wrapping_add(self.instruction_size);
+ state.write_register(0xF, pc);
+ }
+}
diff --git a/src/cpu/enter_exception.rs b/src/cpu/enter_exception.rs
new file mode 100644
index 0000000..193b8cf
--- /dev/null
+++ b/src/cpu/enter_exception.rs
@@ -0,0 +1,59 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see .
+*/
+
+use crate::cpu::{Cpu, exchange, take_state_mut};
+use crate::exception::Exception;
+
+impl Cpu {
+ pub(super) fn enter_exception(&mut self, exception: Exception) {
+ take_state_mut!(state, self);
+
+ let mode = exception.mode();
+
+ state.bank(mode);
+
+ let lr = state.read_register(0xF);
+ state.write_register(0xE, lr);
+
+ let mut cpsr = state.read_cpsr();
+ state.write_spsr(mode, cpsr);
+
+ cpsr &= 0b11111111111111111111111101000000;
+ cpsr |= 0b00000000000000000000000010000000;
+ cpsr |= mode as u32;
+
+ cpsr &= match exception {
+ | Exception::FastInterruptRequest
+ | Exception::Reset => 0b11111111111111111111111110111111,
+
+ _ => 0b11111111111111111111111111111111,
+ };
+
+ state.write_cpsr(cpsr);
+
+ exchange!(self, false);
+
+ let pc = exception.vector_address().wrapping_add(0x4);
+ state.write_register(0xF, pc);
+ }
+}
diff --git a/src/cpu/exchange.rs b/src/cpu/exchange.rs
new file mode 100644
index 0000000..1e74a75
--- /dev/null
+++ b/src/cpu/exchange.rs
@@ -0,0 +1,45 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see .
+*/
+
+macro_rules! exchange {
+ ($cpu: ident, $t: expr) => {{
+ use crate::log_status;
+ use crate::cpu::Fetcher;
+
+ log_status!("exchanging to {}", match $t {
+ false => "ARM",
+ true => "Thumb",
+ });
+
+ const DATA: [(u32, Fetcher); 0x2] = [
+ (0x4, Cpu::fetch_arm),
+ (0x2, Cpu::fetch_thumb),
+ ];
+
+ let index = $t as usize & 0b1;
+
+ $cpu.instruction_size = unsafe { DATA.get_unchecked(index).0 };
+ $cpu.fetcher = unsafe { DATA.get_unchecked(index).1 };
+ }};
+}
+pub(crate) use exchange;
diff --git a/src/cpu/execute.rs b/src/cpu/execute.rs
new file mode 100644
index 0000000..7987f92
--- /dev/null
+++ b/src/cpu/execute.rs
@@ -0,0 +1,69 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+use crate::log;
+use crate::cpu::{alu_exit_exception, Cpu, take_state_mut};
+use crate::shifter::Shifter;
+
+impl Cpu {
+ pub(super) fn isa_eor(&mut self, rd: u8, rn: u8, shifter: Shifter, s: bool) {
+ log!("{} r{rd}, r{rn}, {shifter}", match s {
+ false => "eor",
+ true => "eors",
+ });
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let (mask, c) = state.shifter_value(shifter);
+
+ let result = rn_value ^ mask;
+ state.write_register(rd, result);
+
+ if s {
+ alu_exit_exception!(self, state, rd, {
+ let mut cpsr = state.read_cpsr() & 0b00011111111111111111111111111111;
+ cpsr |= (c as u32) << 0x1D;
+ cpsr |= ((result == 0x0) as u32) << 0x1E;
+ cpsr |= result & 0b10000000000000000000000000000000;
+ state.write_cpsr(cpsr);
+ });
+ }
+ }
+}
diff --git a/src/cpu/isa_memory.rs b/src/cpu/isa_memory.rs
new file mode 100644
index 0000000..f4abecb
--- /dev/null
+++ b/src/cpu/isa_memory.rs
@@ -0,0 +1,122 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see .
+*/
+
+use crate::log;
+use crate::cpu::{Cpu, take_state_mut};
+
+impl Cpu {
+ pub(super) fn isa_load_halfword(&mut self, rd: u8, rn: u8, imm: i16) {
+ take_state_mut!(state, self);
+
+ log!("ldrh r{rd}, [r{rn}, #{imm:#X}]");
+
+ let rn_value = state.read_register(rn);
+
+ let target = rn_value.wrapping_add_signed(imm as i32);
+
+ let result = state.read_halfword(target) as u32;
+ state.write_register(rd, result);
+ }
+
+ pub(super) fn isa_load_immediate_offset(&mut self, rd: u8, rn: u8, imm: i16) {
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+
+ let target = rn_value.wrapping_add_signed(imm as i32);
+
+ log!("ldr r{rd}, [r{rn}, #{imm:#X}] @ {target:#010X}");
+
+ let result = state.read_word(target);
+ state.write_register(rd, result);
+ }
+
+ pub(super) fn isa_load_pc(&mut self, rd: u8, imm: u16) {
+ // Slightly different from load_immediate_offset
+ // due to the target being forced word-aligned.
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(0xF) & 0b11111111111111111111111111111100;
+
+ let target = rn_value.wrapping_add(imm as u32);
+
+ log!("ldr r{rd}, [pc, #{imm:#X}] @ {target:#010X}");
+
+ let result = state.read_word(target);
+ state.write_register(rd, result);
+ }
+
+ pub(super) fn isa_store_byte_immediate_offset(&mut self, rd: u8, rn: u8, imm: i16) {
+ log!("strb r{rd}, [r{rn}, #{imm:#X}]");
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+
+ let target = rn_value.wrapping_add_signed(imm as i32);
+
+ let result = state.read_register(rd) as u8;
+ state.write_byte(target, result);
+ }
+
+ pub(super) fn isa_store_byte_register_offset(&mut self, rd: u8, rn: u8, rm: u8) {
+ log!("strb r{rd}, [r{rn}, r{rm}]");
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+ let rm_value = state.read_register(rm);
+
+ let target = rn_value.wrapping_add(rm_value);
+
+ let result = state.read_register(rd) as u8;
+ state.write_byte(target, result);
+ }
+
+ pub(super) fn isa_store_halfword(&mut self, rd: u8, rn: u8, imm: i16) {
+ log!("strh r{rd}, [r{rn}, #{imm:#X}]");
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+
+ let target = rn_value.wrapping_add_signed(imm as i32);
+
+ let result = state.read_register(rd) as u16;
+ state.write_halfword(target, result);
+ }
+
+ pub(super) fn isa_store_immediate_offset(&mut self, rd: u8, rn: u8, imm: i16) {
+ log!("str r{rd}, [r{rn}, #{imm:#X}]");
+
+ take_state_mut!(state, self);
+
+ let rn_value = state.read_register(rn);
+
+ let target = rn_value.wrapping_add_signed(imm as i32);
+
+ let result = state.read_register(rd);
+ state.write_word(target, result);
+ }
+}
diff --git a/src/cpu/isa_mov.rs b/src/cpu/isa_mov.rs
new file mode 100644
index 0000000..302128e
--- /dev/null
+++ b/src/cpu/isa_mov.rs
@@ -0,0 +1,53 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+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/cpu_handle.rs b/src/cpu_handle.rs
new file mode 100644
index 0000000..c4c1513
--- /dev/null
+++ b/src/cpu_handle.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 .
+*/
+
+use crate::state::State;
+
+use std::sync::{Arc, Mutex};
+use std::sync::atomic::AtomicBool;
+use std::thread::JoinHandle;
+
+mod drop;
+mod dump_video;
+mod dump_palette;
+
+pub struct CpuHandle {
+ state: Arc>,
+ dead: Arc,
+
+ handle: Option>,
+}
+
+impl CpuHandle {
+ pub fn new(
+ state: Arc>,
+ dead: Arc,
+
+ handle: JoinHandle<()>,
+ ) -> Self {
+ return Self {
+ state: state,
+ dead: dead,
+
+ handle: Some(handle),
+ };
+ }
+}
diff --git a/src/cpu_handle/drop.rs b/src/cpu_handle/drop.rs
new file mode 100644
index 0000000..a2a4853
--- /dev/null
+++ b/src/cpu_handle/drop.rs
@@ -0,0 +1,37 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Luma is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Luma.
+ If not, see .
+*/
+
+use crate::cpu_handle::CpuHandle;
+
+use std::sync::atomic::Ordering;
+
+impl Drop for CpuHandle {
+ fn drop(&mut self) {
+ eprintln!("got kill order");
+
+ let handle = self.handle.take().unwrap();
+
+ self.dead.store(true, Ordering::Relaxed);
+ handle.join().unwrap();
+ }
+}
diff --git a/src/cpu_handle/dump_palette.rs b/src/cpu_handle/dump_palette.rs
new file mode 100644
index 0000000..826c96b
--- /dev/null
+++ b/src/cpu_handle/dump_palette.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 .
+*/
+
+use crate::PALETTE_LENGTH;
+use crate::cpu_handle::CpuHandle;
+
+use std::ptr::copy_nonoverlapping;
+
+impl CpuHandle {
+ pub fn dump_palette(&mut self, buffer: &mut [u16]) {
+ assert_eq!(buffer.len(), PALETTE_LENGTH as usize >> 0x1);
+
+ let state = self.state.lock().unwrap();
+ unsafe { copy_nonoverlapping(state.palette().as_ptr(), buffer.as_mut_ptr(), buffer.len()) };
+ }
+}
diff --git a/src/cpu_handle/dump_video.rs b/src/cpu_handle/dump_video.rs
new file mode 100644
index 0000000..6f7eefb
--- /dev/null
+++ b/src/cpu_handle/dump_video.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 .
+*/
+
+use crate::VIDEO_LENGTH;
+use crate::cpu_handle::CpuHandle;
+
+impl CpuHandle {
+ pub fn dump_video(&mut self, buffer: &mut [u8]) {
+ assert_eq!(buffer.len(), VIDEO_LENGTH as usize);
+
+ let state = self.state.lock().unwrap();
+ buffer.copy_from_slice(state.video8());
+ }
+}
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 .
+*/
+
+#[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 .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+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 .
*/
+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/agb.rs b/src/luma/agb.rs
deleted file mode 100644
index 01a77b8..0000000
--- a/src/luma/agb.rs
+++ /dev/null
@@ -1,25 +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 .
-*/
-
-pub mod arm;
-pub mod thumb;
diff --git a/src/luma/app.rs b/src/luma/app.rs
deleted file mode 100644
index c8e0134..0000000
--- a/src/luma/app.rs
+++ /dev/null
@@ -1,46 +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 .
-*/
-
-use sdl2::Sdl;
-use sdl2::render::WindowCanvas;
-use std::sync::Arc;
-use std::sync::atomic::AtomicBool;
-
-pub mod check_events;
-pub mod draw_video;
-pub mod init;
-pub mod load;
-pub mod run;
-pub mod sync_video;
-
-pub struct App {
- bootloader: String,
- image: String,
-
- scale: u32,
-
- got_terminate: Arc::,
-
- sdl: Sdl,
- canvas: WindowCanvas,
-}
diff --git a/src/luma/app/check_events.rs b/src/luma/app/check_events.rs
deleted file mode 100644
index 288a093..0000000
--- a/src/luma/app/check_events.rs
+++ /dev/null
@@ -1,52 +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 .
-*/
-
-use crate::luma::app::App;
-
-use sdl2::event::Event;
-use std::sync::atomic::Ordering;
-
-impl App {
- pub fn check_events(&mut self) -> Result {
- // Return true if we should quit.
-
- let mut event_pump = match self.sdl.event_pump() {
- Ok(pump) => pump,
- _ => return Err("unable to get event pump".to_string()),
- };
-
- if self.got_terminate.load(Ordering::Relaxed) {
- eprintln!("got terminate");
- return Ok(true)
- };
-
- for event in event_pump.poll_iter() {
- match event {
- Event::Quit {..} => return Ok(true),
- _ => {},
- };
- }
-
- return Ok(false);
- }
-}
diff --git a/src/luma/app/draw_video.rs b/src/luma/app/draw_video.rs
deleted file mode 100644
index 4acb012..0000000
--- a/src/luma/app/draw_video.rs
+++ /dev/null
@@ -1,71 +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 .
-*/
-
-use crate::luma::SCREEN_SIZE;
-use crate::luma::app::App;
-
-use sdl2::pixels::Color;
-use sdl2::rect::Rect;
-
-impl App {
- pub fn draw_video(&mut self, video: &[u8], agb_palette: &[u16]) {
- // TO-DO: Honour video mode.
-
- let mut palette: [Color; 0x100] = [Color::RGB(0x00, 0x00, 0x00); 0x100];
-
- for (index, element) in palette.iter_mut().enumerate() {
- let value = unsafe { *agb_palette.get_unchecked(index) };
-
- let colour = decode_colour(value);
- *element = colour;
- }
-
- for pixel_y in 0x0..SCREEN_SIZE.1 {
- for pixel_x in 0x0..SCREEN_SIZE.0 {
- let pixel = pixel_y as usize * SCREEN_SIZE.0 as usize + pixel_x as usize;
-
- let value = video[pixel];
- let colour = palette[value as usize];
- self.canvas.set_draw_color(colour);
-
- let square = Rect::new(
- (pixel_x as u32 * self.scale) as i32,
- (pixel_y as u32 * self.scale) as i32,
- self.scale,
- self.scale,
- );
- self.canvas.fill_rect(square).unwrap();
- }
- }
-
- self.canvas.present();
- }
-}
-
-fn decode_colour(colour: u16) -> Color {
- let red = ((colour & 0b0000000000011111) as f32 / 31.0 * 255.0) as u8;
- let green = ((colour & 0b0000001111100000) as f32 / 992.0 * 255.0) as u8;
- let blue = ((colour & 0b0111110000000000) as f32 / 31744.0 * 255.0) as u8;
-
- return Color::RGB(red, green, blue);
-}
diff --git a/src/luma/app/init.rs b/src/luma/app/init.rs
deleted file mode 100644
index 2d826aa..0000000
--- a/src/luma/app/init.rs
+++ /dev/null
@@ -1,88 +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 .
-*/
-
-use crate::luma::{SCREEN_SIZE, VERSION};
-use crate::luma::app::App;
-use crate::luma::configuration::Configuration;
-
-use sdl2::pixels::Color;
-use sdl2::render::BlendMode;
-use std::sync::Arc;
-use std::sync::atomic::{AtomicBool, Ordering};
-
-impl App {
- pub fn init(configuration: Configuration) -> Result {
- let got_terminate = Arc::new(AtomicBool::new(false));
-
- match ctrlc::set_handler({
- let got_terminate = got_terminate.clone();
- move || got_terminate.store(true, Ordering::Relaxed)
- }) {
- Err(..) => return Err("unable to set signal handler".to_string()),
- _ => {},
- };
-
- let sdl = match sdl2::init() {
- Ok( sdl) => sdl,
- Err(..) => return Err("unable to initialise sdl2".to_string()),
- };
-
- let sdl_video = match sdl.video() {
- Ok( video) => video,
- Err(..) => return Err("unable to initialise video".to_string()),
- };
-
- let window_title = format!("Luma {:X}.{:X}", VERSION.0, VERSION.1);
-
- let mut window_builder = sdl_video.window(&window_title, SCREEN_SIZE.0 as u32 * configuration.scale, SCREEN_SIZE.1 as u32 * configuration.scale);
- window_builder.position_centered();
-
- let window = match window_builder.build() {
- Ok( window) => window,
- Err(..) => return Err("unable to open window".to_string()),
- };
-
- let mut canvas = match window.into_canvas().build() {
- Ok( canvas) => canvas,
- Err(..) => return Err("unable to build canvas".to_string()),
- };
-
- canvas.set_blend_mode(BlendMode::Blend);
-
- let clear_colour = Color::RGB(0x00, 0x00, 0x00);
- canvas.set_draw_color(clear_colour);
- canvas.clear();
- canvas.present();
-
- return Ok(App {
- bootloader: configuration.bootloader,
- image: configuration.image,
-
- scale: configuration.scale,
-
- got_terminate: got_terminate,
-
- sdl: sdl,
- canvas: canvas,
- });
- }
-}
diff --git a/src/luma/app/load.rs b/src/luma/app/load.rs
deleted file mode 100644
index 5c67daf..0000000
--- a/src/luma/app/load.rs
+++ /dev/null
@@ -1,96 +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 .
-*/
-
-use crate::luma::VERSION;
-use crate::luma::app::App;
-use crate::luma::state::State;
-
-use std::fs::File;
-use std::io::Read;
-
-impl App {
- pub fn load(&mut self, state: &mut State) -> Result<(), String> {
- eprintln!("loading booatloader \"{}\"", self.bootloader);
-
- let mut bootloader = match File::open(&self.bootloader) {
- Ok(file) => file,
- _ => return Err("unable to open bootloader".to_string()),
- };
-
- if let Err(..) = bootloader.read(state.bootloader_buffer()) { return Err("unable to read bootloader".to_string()) };
-
- eprintln!("loading image \"{}\"", self.image);
-
- let mut image = match File::open(&self.image) {
- Ok(file) => file,
- _ => return Err("unable to open image".to_string()),
- };
-
- match image.read(state.image_buffer()) {
- Err(..) => return Err("unable to read image".to_string()),
- _ => {},
- };
-
- let title = get_title(&state.image_buffer()[0xA0..0xAC]);
- let id = get_id(&state.image_buffer()[0xAC..0xB0]);
- let version = state.image_buffer()[0xBC];
-
- eprintln!("loaded image \"{title}\" ({id}) v.{version}");
-
- self.canvas.window_mut().set_title(&format!("Luma {:X}.{:X} - {title}", VERSION.0, VERSION.1)).unwrap();
-
- return Ok(());
- }
-}
-
-fn get_title(data: &[u8]) -> String {
- let mut title = String::with_capacity(0xC);
-
- for raw in data {
- let character = match char::from_u32(*raw as u32) {
- Some('\u{0000}') => break,
- Some(character) => character,
- None => '?',
- };
-
- title.push(character);
- }
-
- return title;
-}
-
-fn get_id(data: &[u8]) -> String {
- let mut id = String::with_capacity(0xC);
-
- for raw in data {
- let character = match char::from_u32(*raw as u32) {
- Some('\u{0000}') => break,
- Some(character) => character,
- None => '?',
- };
-
- id.push(character);
- }
-
- return id;
-}
diff --git a/src/luma/app/run.rs b/src/luma/app/run.rs
deleted file mode 100644
index 6c00153..0000000
--- a/src/luma/app/run.rs
+++ /dev/null
@@ -1,58 +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 .
-*/
-
-use crate::luma::{PALETTE_LENGTH, VIDEO_LENGTH};
-use crate::luma::app::App;
-use crate::luma::cpu::Cpu;
-use crate::luma::state::State;
-
-use std::time::Instant;
-
-impl App {
- pub fn run(mut self) -> Result<(), String> {
- let mut state = State::new();
-
- self.load(&mut state)?;
-
- let cpu = Cpu::new(state);
-
- let mut cpu = cpu.boot();
-
- let mut video_buffer: Vec:: = vec![0x0; VIDEO_LENGTH as usize];
- let mut palette_buffer: Vec:: = vec![0x0; (PALETTE_LENGTH / 0x2) as usize];
-
- 'main_loop: loop {
- let frame_start = Instant::now();
-
- if self.check_events()? { break 'main_loop };
-
- cpu.dump_video( &mut video_buffer[..]);
- cpu.dump_palette(&mut palette_buffer[..]);
- self.draw_video(&video_buffer[..], &palette_buffer[..]);
-
- self.sync_video(frame_start);
- }
-
- return Ok(());
- }
-}
diff --git a/src/luma/app/sync_video.rs b/src/luma/app/sync_video.rs
deleted file mode 100644
index 10c6abc..0000000
--- a/src/luma/app/sync_video.rs
+++ /dev/null
@@ -1,44 +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 .
-*/
-
-use crate::luma::app::App;
-
-use std::thread::sleep;
-use std::time::{Duration, Instant};
-
-impl App {
- pub fn sync_video(&self, frame_start: Instant) {
- // Courtesy of TASVideos:
- // 59.7275005696058 Hz
-
- const FRAME_DURATION: u64 = 0xFF7932;
- let frame_duration = Duration::from_nanos(FRAME_DURATION);
-
- let remaining = match frame_duration.checked_sub(frame_start.elapsed()) {
- Some(value) => value,
- None => Duration::from_secs(0x0),
- };
-
- sleep(remaining);
- }
-}
diff --git a/src/luma/configuration.rs b/src/luma/configuration.rs
deleted file mode 100644
index facbc69..0000000
--- a/src/luma/configuration.rs
+++ /dev/null
@@ -1,36 +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 .
-*/
-
-pub mod load;
-pub mod validate;
-
-pub struct Configuration {
- pub bootloader: String,
- pub image: String,
-
- pub scale: u32,
-}
-
-impl Configuration {
- pub const VERSION: u32 = 0x0;
-}
diff --git a/src/luma/configuration/load.rs b/src/luma/configuration/load.rs
deleted file mode 100644
index bfd91b0..0000000
--- a/src/luma/configuration/load.rs
+++ /dev/null
@@ -1,100 +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 .
-*/
-
-use crate::luma::configuration::Configuration;
-
-extern crate toml;
-
-use std::fs::read_to_string;
-use std::str::FromStr;
-use toml::{Table, Value};
-
-impl Configuration {
- pub fn load(path: &str) -> Result {
- eprintln!("loading configuration at \"{path}\"");
-
- let configuration_text = match read_to_string(path) {
- Ok( content) => content,
- _ => return Err("unable to read file".to_string()),
- };
-
- let base_table = match Table::from_str(&configuration_text) {
- Ok( table) => table,
- _ => return Err("unable to parse configuration".to_string()),
- };
-
- let luma_table = get_table(&base_table, "luma")?;
- let device_table = get_table(&base_table, "device")?;
- let video_table = get_table(&base_table, "video")?;
-
- let version = get_integer(&luma_table, "version")?;
-
- if version < Self::VERSION { return Err(format!("ancient version - got {}, expected {}", version, Self::VERSION)) }
- if version > Self::VERSION { return Err(format!("out-of-date version - got {}, expected {}", version, Self::VERSION)) }
-
- let bootloader = get_string(&device_table, "bootloader")?;
- let image = get_string(&device_table, "image")?;
-
- let scale = get_integer(&video_table, "scale")?;
-
- let configuration = Configuration {
- bootloader: bootloader.clone(),
- image: image.clone(),
-
- scale: scale,
- };
-
- configuration.validate()?;
- return Ok(configuration);
- }
-}
-
-fn get_value<'a>(table: &'a Table, name: &str) -> Option<&'a Value> {
- if !table.contains_key(name) { return None };
-
- return Some(&table[name]);
-}
-
-fn get_table<'a>(table: &'a Table, name: &str) -> Result<&'a Table, String> {
- return match get_value(table, name) {
- Some(Value::Table(table)) => Ok(table),
- Some(_) => Err(format!("\"{name}\" should be a section")),
- _ => Err("section \"{name}\" is required".to_string()),
- };
-}
-
-fn get_integer(table: &Table, name: &str) -> Result {
- return match get_value(table, name) {
- Some(Value::Integer(value)) => Ok(*value as u32),
- Some(_) => Err(format!("\"{name}\" should be an integer")),
- _ => Err("missing integer \"{name}\"".to_string()),
- };
-}
-
-fn get_string<'a>(table: &'a Table, name: &str) -> Result<&'a String, String> {
- return match get_value(table, name) {
- Some(Value::String(string)) => Ok(string),
- Some(_) => Err(format!("\"{name}\" should be a string")),
- _ => Err("missing string \"{name}\"".to_string()),
- };
-}
diff --git a/src/luma/configuration/validate.rs b/src/luma/configuration/validate.rs
deleted file mode 100644
index 9cdd135..0000000
--- a/src/luma/configuration/validate.rs
+++ /dev/null
@@ -1,32 +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 .
-*/
-
-use crate::luma::configuration::Configuration;
-
-impl Configuration {
- pub(super) fn validate(&self) -> Result<(), String> {
- if self.scale < 0x1 { return Err("scale must be at least 1".to_string()) };
-
- return Ok(());
- }
-}
diff --git a/src/luma/cpu.rs b/src/luma/cpu.rs
deleted file mode 100644
index 28f6844..0000000
--- a/src/luma/cpu.rs
+++ /dev/null
@@ -1,78 +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 .
-*/
-
-use crate::luma::instruction::Instruction;
-use crate::luma::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 exchange;
-mod test_predicate;
-
-// https://github.com/rust-lang/rust/issues/115966
-
-#[allow(unused_imports)]
-pub use exchange::*;
-
-#[allow(unused_imports)]
-pub use test_predicate::*;
-
-pub type Decoder = fn(&mut Cpu) -> Instruction;
-
-pub struct Cpu {
- state: Arc>,
- cycle: u64,
- dead: Arc,
-
- instruction_size: u32,
- decoder: Decoder,
-}
-
-impl Cpu {
- pub fn new(state: State) -> Self {
- return Self {
- state: Arc::new(Mutex::new(state)),
- cycle: 0x0,
- dead: Arc::new(AtomicBool::new(false)),
-
- instruction_size: 0x4,
- decoder: Self::decode_arm,
- };
- }
-
- #[inline(always)]
- #[must_use]
- fn decode(&mut self) -> Instruction { (self.decoder)(self) }
-}
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 .
-*/
-
-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/continue.rs b/src/luma/cpu/continue.rs
deleted file mode 100644
index 6d10bc3..0000000
--- a/src/luma/cpu/continue.rs
+++ /dev/null
@@ -1,33 +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 .
-*/
-
-use crate::luma::cpu::Cpu;
-
-impl Cpu {
- pub(super) fn r#continue(&mut self) {
- let mut state = self.state.lock().unwrap();
-
- let pc = state.read_register(0xF).wrapping_add(self.instruction_size);
- state.write_register(0xF, pc);
- }
-}
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 .
-*/
-
-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 .
-*/
-
-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/exchange.rs b/src/luma/cpu/exchange.rs
deleted file mode 100644
index 2414831..0000000
--- a/src/luma/cpu/exchange.rs
+++ /dev/null
@@ -1,39 +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 .
-*/
-
-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),
- ];
-
- let index = $t as usize & 0b1;
-
- $cpu.instruction_size = unsafe { DATA.get_unchecked(index).0 };
- $cpu.decoder = unsafe { DATA.get_unchecked(index).1 };
- }};
-}
-pub(crate) use exchange;
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 .
-*/
-
-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 .
-*/
-
-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 .
-*/
-
-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 .
-*/
-
-use crate::luma::log;
-use crate::luma::cpu::Cpu;
-
-impl Cpu {
- pub(super) fn isa_compare_immediate(&mut self, rn: u8, imm: u32) {
- log!("cmp r{rn}, {imm:#X}");
-
- let mut state = self.state.lock().unwrap();
-
- let rn_value = state.read_register(rn);
-
- let (temporary, c) = rn_value.overflowing_sub(imm);
-
- let v = false; // ???
- let z = temporary == 0x0;
- let n = temporary & 0b10000000000000000000000000000000 != 0x0;
-
- let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
- cpsr |= (v as u32) << 0x1C;
- cpsr |= (c as u32) << 0x1D;
- cpsr |= (z as u32) << 0x1E;
- cpsr |= (n as u32) << 0x1F;
- state.write_cpsr(cpsr);
- }
-
- pub(super) fn isa_compare_negative_register(&mut self, rn: u8, rm: u8) {
- log!("cmn r{rn}, r{rm}");
-
- let mut state = self.state.lock().unwrap();
-
- let rn_value = state.read_register(rn);
- let rm_value = state.read_register(rm);
-
- let (temporary, c) = rn_value.overflowing_add(rm_value);
-
- let v = false; // ???
- let z = temporary == 0x0;
- let n = temporary & 0b10000000000000000000000000000000 != 0x0;
-
- let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
- cpsr |= (v as u32) << 0x1C;
- cpsr |= (c as u32) << 0x1D;
- cpsr |= (z as u32) << 0x1E;
- cpsr |= (n as u32) << 0x1F;
- state.write_cpsr(cpsr);
- }
-
- pub(super) fn isa_compare_register(&mut self, rn: u8, rm: u8) {
- log!("cmp r{rn}, r{rm}");
-
- let mut state = self.state.lock().unwrap();
-
- let rn_value = state.read_register(rn);
- let rm_value = state.read_register(rm);
-
- let (temporary, c) = rn_value.overflowing_sub(rm_value);
-
- let v = false; // ???
- let z = temporary == 0x0;
- let n = temporary & 0b10000000000000000000000000000000 != 0x0;
-
- let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
- cpsr |= (v as u32) << 0x1C;
- cpsr |= (c as u32) << 0x1D;
- cpsr |= (z as u32) << 0x1E;
- cpsr |= (n as u32) << 0x1F;
- state.write_cpsr(cpsr);
- }
-
- pub(super) fn isa_test_register(&mut self, rn: u8, rm: u8) {
- log!("tst r{rn}, r{rm}");
-
- let mut state = self.state.lock().unwrap();
-
- let rn_value = state.read_register(rn);
- let rm_value = state.read_register(rm);
-
- let temporary = rn_value & !rm_value;
-
- let z = temporary == 0x0;
- let n = temporary & 0b10000000000000000000000000000000 != 0x0;
-
- let mut cpsr = state.read_cpsr() & 0b00001111111111111111111111111111;
- cpsr |= (z as u32) << 0x1E;
- cpsr |= (n as u32) << 0x1F;
- state.write_cpsr(cpsr);
- }
-}
diff --git a/src/luma/cpu/isa_memory.rs b/src/luma/cpu/isa_memory.rs
deleted file mode 100644
index 8b953c0..0000000
--- a/src/luma/cpu/isa_memory.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 .
-*/
-
-use crate::luma::log;
-use crate::luma::cpu::Cpu;
-
-impl Cpu {
- pub(super) fn isa_load_halfword(&mut self, rd: u8, rn: u8, imm: i16) {
- let mut state = self.state.lock().unwrap();
-
- log!("ldrh r{rd}, [r{rn}, {imm:#X}]");
-
- let rn_value = state.read_register(rn);
-
- let target = rn_value.wrapping_add_signed(imm as i32);
-
- let result = state.read_halfword(target) as u32;
- state.write_register(rd, result);
- }
-
- pub(super) fn isa_load_immediate_offset(&mut self, rd: u8, rn: u8, imm: i16) {
- let mut state = self.state.lock().unwrap();
-
- log!("ldr r{rd}, [r{rn}, {imm:#X}]");
-
- let rn_value = state.read_register(rn);
-
- let target = rn_value.wrapping_add_signed(imm as i32);
-
- let result = state.read_word(target);
- state.write_register(rd, result);
- }
-
- pub(super) fn isa_load_pc(&mut self, rd: u8, imm: i16) {
- // Slightly different from load_immediate_offset
- // due to the target being forced word-aligned.
-
- let mut state = self.state.lock().unwrap();
-
- log!("ldr r{rd}, [pc, {imm:#X}]");
-
- let rn_value = state.read_register(0xF) & 0b11111111111111111111111111111100;
-
- let target = rn_value.wrapping_add_signed(imm as i32);
-
- let result = state.read_word(target);
- state.write_register(rd, result);
- }
-
- pub(super) fn isa_store_byte_immediate_offset(&mut self, rd: u8, rn: u8, imm: i16) {
- log!("strb r{rd}, [r{rn}, {imm:#X}]");
-
- let mut state = self.state.lock().unwrap();
-
- let rn_value = state.read_register(rn);
-
- let target = rn_value.wrapping_add_signed(imm as i32);
-
- let result = state.read_register(rd) as u8;
- state.write_byte(target, result);
- }
-
- pub(super) fn isa_store_byte_register_offset(&mut self, rd: u8, rn: u8, rm: u8) {
- log!("strb r{rd}, [r{rn}, r{rm}]");
-
- let mut state = self.state.lock().unwrap();
-
- let rn_value = state.read_register(rn);
- let rm_value = state.read_register(rm);
-
- let target = rn_value.wrapping_add(rm_value);
-
- let result = state.read_register(rd) as u8;
- state.write_byte(target, result);
- }
-
- pub(super) fn isa_store_halfword(&mut self, rd: u8, rn: u8, imm: i16) {
- log!("strh r{rd}, [r{rn}, {imm:#X}]");
-
- let mut state = self.state.lock().unwrap();
-
- let rn_value = state.read_register(rn);
-
- let target = rn_value.wrapping_add_signed(imm as i32);
-
- let result = state.read_register(rd) as u16;
- state.write_halfword(target, result);
- }
-
- pub(super) fn isa_store_immediate_offset(&mut self, rd: u8, rn: u8, imm: i16) {
- log!("str r{rd}, [r{rn}, {imm:#X}]");
-
- let mut state = self.state.lock().unwrap();
-
- let rn_value = state.read_register(rn);
-
- let target = rn_value.wrapping_add_signed(imm as i32);
-
- let result = state.read_register(rd);
- state.write_word(target, result);
- }
-}
diff --git a/src/luma/cpu/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 .
-*/
-
-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 .
-*/
-
-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/cpu_handle.rs b/src/luma/cpu_handle.rs
deleted file mode 100644
index ceacd03..0000000
--- a/src/luma/cpu_handle.rs
+++ /dev/null
@@ -1,55 +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 .
-*/
-
-use crate::luma::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;
-
-pub struct CpuHandle {
- state: Arc>,
- dead: Arc,
-
- handle: Option>,
-}
-
-impl CpuHandle {
- pub fn new(
- state: Arc>,
- dead: Arc,
-
- handle: JoinHandle<()>,
- ) -> Self {
- return Self {
- state: state,
- dead: dead,
-
- handle: Some(handle),
- };
- }
-}
diff --git a/src/luma/cpu_handle/drop.rs b/src/luma/cpu_handle/drop.rs
deleted file mode 100644
index 52c2866..0000000
--- a/src/luma/cpu_handle/drop.rs
+++ /dev/null
@@ -1,37 +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 .
-*/
-
-use crate::luma::cpu_handle::CpuHandle;
-
-use std::sync::atomic::Ordering;
-
-impl Drop for CpuHandle {
- fn drop(&mut self) {
- eprintln!("got kill order");
-
- let handle = self.handle.take().unwrap();
-
- self.dead.store(true, Ordering::Relaxed);
- handle.join().unwrap();
- }
-}
diff --git a/src/luma/cpu_handle/dump_palette.rs b/src/luma/cpu_handle/dump_palette.rs
deleted file mode 100644
index c59a132..0000000
--- a/src/luma/cpu_handle/dump_palette.rs
+++ /dev/null
@@ -1,36 +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 .
-*/
-
-use crate::luma::PALETTE_LENGTH;
-use crate::luma::cpu_handle::CpuHandle;
-
-use std::ptr::copy_nonoverlapping;
-
-impl CpuHandle {
- pub fn dump_palette(&mut self, buffer: &mut [u16]) {
- assert_eq!(buffer.len(), PALETTE_LENGTH as usize >> 0x1);
-
- let state = self.state.lock().unwrap();
- unsafe { copy_nonoverlapping(state.palette().as_ptr(), buffer.as_mut_ptr(), buffer.len()) };
- }
-}
diff --git a/src/luma/cpu_handle/dump_video.rs b/src/luma/cpu_handle/dump_video.rs
deleted file mode 100644
index c59c467..0000000
--- a/src/luma/cpu_handle/dump_video.rs
+++ /dev/null
@@ -1,34 +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 .
-*/
-
-use crate::luma::VIDEO_LENGTH;
-use crate::luma::cpu_handle::CpuHandle;
-
-impl CpuHandle {
- pub fn dump_video(&mut self, buffer: &mut [u8]) {
- assert_eq!(buffer.len(), VIDEO_LENGTH as usize);
-
- let state = self.state.lock().unwrap();
- buffer.copy_from_slice(state.video8());
- }
-}
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 .
-*/
-
-#[derive(Clone, Copy)]
-pub enum Instruction {
- AddCarryRegister( u8, u8, u8),
- AddImmediate( u8, u8, u32),
- AddRegister( u8, u8, u8),
- AndRegister( u8, u8, u8),
- BitClearRegister( u8, u8, u8),
- Branch( i32),
- BranchExchange( u8),
- BranchLink( i32),
- BranchLinkPrefix( i32),
- BranchLinkSuffix( i32),
- CompareImmediate( u8, u32),
- CompareNegativeRegister( u8, u8),
- CompareRegister( u8, u8),
- ExclusiveOrRegister( u8, u8, u8),
- LoadHalfword( u8, u8, i16),
- LoadImmediateOffset( u8, u8, i16),
- LoadPc( u8, i16),
- LogicalOrRegister( u8, u8, u8),
- MoveImmediate( u8, u32),
- MoveImmediateArithmeticShiftRight( u8, u8, u8),
- MoveImmediateLogicalShiftLeftImmediate( u8, u8, u8),
- MoveImmediateLogicalShiftRightImmediate(u8, u8, u8),
- MoveNotRegister( u8, u8),
- MoveRegister( u8, u8),
- MoveRegisterArithmeticShiftRight( u8, u8, u8),
- MoveRegisterLogicalShiftLeftImmediate( u8, u8, u8),
- MoveRegisterLogicalShiftRightImmediate( u8, u8, u8),
- MoveRegisterRotateRight( u8, u8, u8),
- MultiplyRegister( u8, u8, u8),
- ReverseSubtractImmediate( u8, u8, u32),
- StoreByteImmediateOffset( u8, u8, i16),
- StoreByteRegisterOffset( u8, u8, u8),
- StoreHalfword( u8, u8, i16),
- StoreImmediateOffset( u8, u8, i16),
- SubtractCarryRegister( u8, u8, u8),
- SubtractImmediate( u8, u8, u32),
- SubtractRegister( u8, u8, u8),
- TestRegister( u8, u8),
-
- Undefined,
-}
diff --git a/src/luma/state.rs b/src/luma/state.rs
deleted file mode 100644
index 1b293ef..0000000
--- a/src/luma/state.rs
+++ /dev/null
@@ -1,103 +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 .
-*/
-
-use crate::luma::{BOOTLOADER_LENGTH, IMAGE_LENGTH, PALETTE_LENGTH, VIDEO_LENGTH};
-
-use std::slice::{from_raw_parts, from_raw_parts_mut};
-
-pub mod new;
-pub mod read;
-pub mod write;
-
-pub struct State {
- registers: [u32; 0x10],
- cpsr: u32,
-
- memory: Vec::,
-}
-
-macro_rules! address_unused {
- ($address: expr) => {{
- match $address {
- 0x02040000..=0x02FFFFFF => true,
- 0x03008000..=0x03FFFFFF => true,
- 0x04000400..=0x04FFFFFF => true,
- 0x05000400..=0x05FFFFFF => true,
- 0x06018000..=0x06FFFFFF => true,
- 0x07000400..=0x07FFFFFF => true,
-
- _ => false,
- }
- }};
-}
-pub(crate) use address_unused;
-
-impl State {
- #[must_use]
- pub fn video8<'a>(&'a self) -> &'a [u8] {
- let slice = unsafe {
- let pointer = (self.memory.as_ptr() as *const u8).add(0x06000000);
- let slice = from_raw_parts(pointer, VIDEO_LENGTH as usize);
-
- slice
- };
-
- return slice;
- }
-
- #[must_use]
- pub fn palette<'a>(&'a self) -> &'a [u16] {
- let slice = unsafe {
- let pointer = (self.memory.as_ptr() as *const u8).add(0x05000000) as *const u16;
- let slice = from_raw_parts(pointer, PALETTE_LENGTH as usize);
-
- slice
- };
-
- return slice;
- }
-
- #[must_use]
- pub fn bootloader_buffer<'a>(&'a mut self) -> &'a mut [u8] {
- let slice = unsafe {
- let pointer = (self.memory.as_mut_ptr() as *mut u8).add(0x00000000);
- let slice = from_raw_parts_mut(pointer, BOOTLOADER_LENGTH as usize);
-
- slice
- };
-
- return slice;
- }
-
- #[must_use]
- pub fn image_buffer<'a>(&'a mut self) -> &'a mut [u8] {
- let slice = unsafe {
- let pointer = (self.memory.as_mut_ptr() as *mut u8).add(0x08000000);
- let slice = from_raw_parts_mut(pointer, IMAGE_LENGTH as usize);
-
- slice
- };
-
- return slice;
- }
-}
diff --git a/src/luma/state/new.rs b/src/luma/state/new.rs
deleted file mode 100644
index 39caaf6..0000000
--- a/src/luma/state/new.rs
+++ /dev/null
@@ -1,60 +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 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 .
-*/
-
-use crate::luma::MEMORY_LENGTH;
-use crate::luma::state::State;
-
-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;
-
- let memory: Vec:: = vec![0x0; MEMORY_LENGTH as usize / 0x4];
-
- return Self {
- registers: registers,
- cpsr: cpsr,
-
- memory: memory,
- };
- }
-}
diff --git a/src/luma/state/read.rs b/src/luma/state/read.rs
deleted file mode 100644
index f22dc86..0000000
--- a/src/luma/state/read.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 .
-*/
-
-use crate::luma::{Error, MEMORY_LENGTH};
-use crate::luma::state::State;
-
-impl State {
- #[inline(always)]
- #[must_use]
- pub fn read_register(&self, register: u8) -> u32 {
- // Limit to 0..=15.
- let index = (register & 0b00001111) as usize;
-
- return unsafe { *self.registers.get_unchecked(index) };
- }
-
- #[must_use]
- pub fn read_word(&self, address: u32) -> u32 {
- if address > MEMORY_LENGTH - 0x4 { Error::OutOfBounds( address).trap(); return 0x00000000; }
- if address % 0x4 != 0x0 { Error::BadAlignment(address, 0x4).trap(); return 0x00000000; }
-
- unsafe {
- let pointer = (self.memory.as_ptr() as *const u8).add(address as usize) as *const u32;
- return *pointer;
- }
- }
-
- #[must_use]
- pub fn read_halfword(&self, address: u32) -> u16 {
- if address > MEMORY_LENGTH - 0x2 { Error::OutOfBounds( address).trap(); return 0x0000; }
- if address % 0x2 != 0x0 { Error::BadAlignment(address, 0x2).trap(); return 0x0000; }
-
- unsafe {
- let pointer = (self.memory.as_ptr() as *const u8).add(address as usize) as *const u16;
- return *pointer;
- }
- }
-
- #[must_use]
- pub fn read_byte(&self, address: u32) -> u8 {
- if address > MEMORY_LENGTH - 0x1 { Error::OutOfBounds(address).trap(); return 0x00; }
-
- unsafe {
- let pointer = (self.memory.as_ptr() as *const u8).add(address as usize);
- return *pointer;
- }
- }
-
- #[inline(always)]
- #[must_use]
- pub fn read_cpsr(&self) -> u32 {
- return self.cpsr;
- }
-}
diff --git a/src/luma/state/write.rs b/src/luma/state/write.rs
deleted file mode 100644
index bd2b031..0000000
--- a/src/luma/state/write.rs
+++ /dev/null
@@ -1,116 +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 .
-*/
-
-use crate::luma::{Error, log_assignment, MEMORY_LENGTH};
-use crate::luma::state::{address_unused, State};
-
-macro_rules! read_only {
- ($address: expr) => {{
- match $address {
- 0x00000000..=0x00003FFF => true,
- 0x04000130..=0x04000131 => true, // KEYINPUT
- 0x08000000..=0x0DFFFFFF => true,
-
- _ => false,
- }
- }};
-}
-
-impl State {
- #[inline(always)]
- pub fn write_register(&mut self, register: u8, value: u32) {
- log_assignment!("r{register}", "{value:#010X}");
-
- let index = (register & 0b00001111) as usize;
-
- unsafe { *self.registers.get_unchecked_mut(index) = value };
- }
-
- pub fn write_word(&mut self, address: u32, value: u32) {
- log_assignment!("{address:#010X}", "{value:#010X}");
-
- if address > MEMORY_LENGTH - 0x4 { Error::OutOfBounds( address).trap(); return; }
- if address % 0x4 != 0x0 { Error::BadAlignment(address, 0x4).trap(); return; }
-
- if read_only!(address) || address_unused!(address) { return };
-
- unsafe {
- let pointer = (self.memory.as_mut_ptr() as *mut u8).add(address as usize) as *mut u32;
- *pointer = value;
- }
- }
-
- pub fn write_halfword(&mut self, address: u32, value: u16) {
- log_assignment!("{address:#010X}", "{value:#06X}");
-
- if address > MEMORY_LENGTH - 0x2 { Error::OutOfBounds( address).trap(); return; }
- if address % 0x2 != 0x0 { Error::BadAlignment(address, 0x2).trap(); return; }
-
- if read_only!(address) || address_unused!(address) { return };
-
- unsafe {
- let pointer = (self.memory.as_mut_ptr() as *mut u8).add(address as usize) as *mut u16;
- *pointer = value;
- }
- }
-
- pub fn write_byte(&mut self, address: u32, value: u8) {
- log_assignment!("{address:#010X}", "{value:#04X}");
-
- if address > MEMORY_LENGTH - 0x1 { Error::OutOfBounds(address).trap(); return; }
-
- if read_only!(address) || address_unused!(address) { return };
-
- let memory = self.memory.as_mut_ptr() as *mut u8;
-
- match address {
- // Extend to halfwords:
- | 0x05000000..=0x050003FF
- | 0x06000000..=0x06017FFF
- | 0x07000000..=0x070003FF => {
- // Align to halfwords.
- let address = address & 0b11111111111111111111111111111110;
-
- // Repeat value.
- let value = value as u16 | (value as u16) << 0x8;
-
- unsafe {
- let pointer = memory.add(address as usize) as *mut u16;
- *pointer = value;
- }
- },
-
- // Bytes are allowed:
- _ => unsafe {
- let pointer = memory.add(address as usize);
- *pointer = value;
- },
- };
- }
-
- pub fn write_cpsr(&mut self, value: u32) {
- log_assignment!("cpsr", "{value:#034b}");
-
- self.cpsr = value;
- }
-}
diff --git a/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 .
-*/
-
-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 .
+*/
+
+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 .
+*/
+
+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 .
+*/
+
+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/state.rs b/src/state.rs
new file mode 100644
index 0000000..6697059
--- /dev/null
+++ b/src/state.rs
@@ -0,0 +1,126 @@
+/*
+ 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 .
+*/
+
+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: [*mut u32; 0x10],
+ banks: Box<[[u32; 0x10]; 0x6]>,
+
+ cpsr: u32,
+ spsr: [u32; 0x6],
+
+ memory: Vec::,
+}
+
+macro_rules! address_unused {
+ ($address: expr) => {{
+ match $address {
+ 0x02040000..=0x02FFFFFF => true,
+ 0x03008000..=0x03FFFFFF => true,
+ 0x04000400..=0x04FFFFFF => true,
+ 0x05000400..=0x05FFFFFF => true,
+ 0x06018000..=0x06FFFFFF => true,
+ 0x07000400..=0x07FFFFFF => true,
+
+ _ => false,
+ }
+ }};
+}
+pub(crate) use address_unused;
+
+impl State {
+ #[must_use]
+ pub fn video8<'a>(&'a self) -> &'a [u8] {
+ let slice = unsafe {
+ let pointer = (self.memory.as_ptr() as *const u8).add(0x06000000);
+ let slice = from_raw_parts(pointer, VIDEO_LENGTH as usize);
+
+ slice
+ };
+
+ return slice;
+ }
+
+ #[must_use]
+ pub fn palette<'a>(&'a self) -> &'a [u16] {
+ let slice = unsafe {
+ let pointer = (self.memory.as_ptr() as *const u8).add(0x05000000) as *const u16;
+ let slice = from_raw_parts(pointer, PALETTE_LENGTH as usize);
+
+ slice
+ };
+
+ return slice;
+ }
+
+ #[must_use]
+ pub fn bootloader_buffer<'a>(&'a mut self) -> &'a mut [u8] {
+ let slice = unsafe {
+ let pointer = (self.memory.as_mut_ptr() as *mut u8).add(0x00000000);
+ let slice = from_raw_parts_mut(pointer, BOOTLOADER_LENGTH as usize);
+
+ slice
+ };
+
+ return slice;
+ }
+
+ #[must_use]
+ pub fn image_buffer<'a>(&'a mut self) -> &'a mut [u8] {
+ let slice = unsafe {
+ let pointer = (self.memory.as_mut_ptr() as *mut u8).add(0x08000000);
+ let slice = from_raw_parts_mut(pointer, IMAGE_LENGTH as usize);
+
+ slice
+ };
+
+ 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 .
+*/
+
+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/state/new.rs b/src/state/new.rs
new file mode 100644
index 0000000..bb6f87e
--- /dev/null
+++ b/src/state/new.rs
@@ -0,0 +1,75 @@
+/*
+ Copyright 2021-2023 Gabriel Jensen.
+
+ This file is part of Luma.
+
+ Luma is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero 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 .
+*/
+
+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 {
+ log!("initialising new state");
+
+ let banks = Box::new([DEFAULT_REGISTER_VALUES; 0x6]);
+
+ let cpsr = 0b00000000000000000000000000011111;
+
+ let memory: Vec:: = vec![0x0; MEMORY_LENGTH as usize / 0x4];
+
+ 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/state/read.rs b/src/state/read.rs
new file mode 100644
index 0000000..927d528
--- /dev/null
+++ b/src/state/read.rs
@@ -0,0 +1,85 @@
+/*
+ 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 .
+*/
+
+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)]
+ #[must_use]
+ pub fn read_register(&self, register: u8) -> u32 {
+ // Limit to 0..=15.
+ let index = (register & 0b00001111) as usize;
+
+ return unsafe { **self.registers.get_unchecked(index) };
+ }
+
+ #[must_use]
+ pub fn read_word(&self, address: u32) -> u32 {
+ if address > 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;
+ return *pointer;
+ }
+ }
+
+ #[must_use]
+ pub fn read_halfword(&self, address: u32) -> u16 {
+ 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;
+ return *pointer;
+ }
+ }
+
+ #[must_use]
+ pub fn read_byte(&self, address: u32) -> u8 {
+ 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);
+ return *pointer;
+ }
+ }
+
+ #[inline(always)]
+ #[must_use]
+ 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 .
+*/
+
+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/state/write.rs b/src/state/write.rs
new file mode 100644
index 0000000..10955e2
--- /dev/null
+++ b/src/state/write.rs
@@ -0,0 +1,125 @@
+/*
+ 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 .
+*/
+
+use crate::{Error, log_assignment, MEMORY_LENGTH};
+use crate::cpu_mode::CpuMode;
+use crate::state::{address_unused, State};
+
+macro_rules! read_only {
+ ($address: expr) => {{
+ match $address {
+ 0x00000000..=0x00003FFF => true,
+ 0x04000130..=0x04000131 => true, // KEYINPUT
+ 0x08000000..=0x0DFFFFFF => true,
+
+ _ => false,
+ }
+ }};
+}
+
+impl State {
+ #[inline(always)]
+ pub fn write_register(&mut self, register: u8, value: u32) {
+ log_assignment!(format!("r{register}"), format!("{value:#010X}"));
+
+ let index = (register & 0b00001111) as usize;
+
+ unsafe { **self.registers.get_unchecked_mut(index) = value };
+ }
+
+ pub fn write_word(&mut self, address: u32, value: u32) {
+ log_assignment!(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; }
+
+ if read_only!(address) || address_unused!(address) { return };
+
+ unsafe {
+ let pointer = (self.memory.as_mut_ptr() as *mut u8).add(address as usize) as *mut u32;
+ *pointer = value;
+ }
+ }
+
+ pub fn write_halfword(&mut self, address: u32, value: u16) {
+ log_assignment!(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; }
+
+ if read_only!(address) || address_unused!(address) { return };
+
+ unsafe {
+ let pointer = (self.memory.as_mut_ptr() as *mut u8).add(address as usize) as *mut u16;
+ *pointer = value;
+ }
+ }
+
+ pub fn write_byte(&mut self, address: u32, value: u8) {
+ log_assignment!(format!("{address:#010X}"), format!("{value:#04X}"));
+
+ if address > MEMORY_LENGTH - 0x1 { Error::OutOfBounds(address).trap(); return; }
+
+ if read_only!(address) || address_unused!(address) { return };
+
+ let memory = self.memory.as_mut_ptr() as *mut u8;
+
+ match address {
+ // Extend to halfwords:
+ | 0x05000000..=0x050003FF
+ | 0x06000000..=0x06017FFF
+ | 0x07000000..=0x070003FF => {
+ // Align to halfwords.
+ let address = address & 0b11111111111111111111111111111110;
+
+ // Repeat value.
+ let value = value as u16 | (value as u16) << 0x8;
+
+ unsafe {
+ let pointer = memory.add(address as usize) as *mut u16;
+ *pointer = value;
+ }
+ },
+
+ // Bytes are allowed:
+ _ => unsafe {
+ let pointer = memory.add(address as usize);
+ *pointer = value;
+ },
+ };
+ }
+
+ #[inline(always)]
+ pub fn write_cpsr(&mut self, value: u32) {
+ 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
--
cgit v1.2.3