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