diff options
37 files changed, 1102 insertions, 266 deletions
diff --git a/CHANGELOG.txt b/CHANGELOG.txt index ba07ef5..17b0e91 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,20 @@ +# 0.25 + +* Support load and store instructions; +* Update messages; +* Implement some move instructions; +* Update log function; +* Add equivalent write functions; +* Fix conditional execution; +* Fix branch; +* Add device helper structure; +* Update trap print; +* Reenable overflow checks; +* License under AGPL3; +* Fix default configuration not being made; +* Update readme; +* Add spsr registers; + # 0.24 * Bump dependency versions; @@ -1,6 +1,6 @@ [package] name = "luma" -version = "0.36.0" +version = "0.37.0" authors = ["Gabriel Jensen"] edition = "2021" description = "AGB emulator." @@ -13,9 +13,6 @@ categories = ["emulators"] name = "luma" path = "src/main.rs" -[profile.dev] -overflow-checks = false - [profile.release] lto = true @@ -1,33 +1,61 @@ -LUMA +- LUMA Copyright 2021-2023 Gabriel Jensen. +This program is free software: you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see <https://www.gnu.org/licenses/>. + +- ABOUT + luma is an emulator for the AGB - Game Boy Advance - platform. -USAGE +- USAGE luma [image] [bootloader] Invoke the emulator via the 'luma' command. -CONFIGURATION +- CONFIGURATION The emulator tries to read the configuration file at '${HOME}/.luma.toml'. If this file is found, the following fields are read (if present): luma: - - version: (Integer) The version of the configuration file (0) + * version: (Integer) The version of the configuration file (0) device: - - bootloader: (String) The path to the bootloader file (home-relative) - - image: (String) The path to the image file (home-relative) + * bootloader: (String) The path to the bootloader file (home-relative) + * image: (String) The path to the image file (home-relative) video: - - scale: (Integer) The scale modifier applied to the screen (min 1; max (2^32-1)) + * scale: (Integer) The scale modifier applied to the screen (min 1; max (2^32-1)) These settings are overwritten by terminal parameters (see USAGE). -COMPATIBILITY +- COMPATIBILITY + +Currently, the emulator supports the following ARM instructions only. Others +will be skipped. + + * b{cond}{l} + * ldr{cond} Rn, +/-offset + * mov{cond} Rd, Rn + * mov{cons}s r15, Rn + * str{cond} Rn, +/-offset + +When the virtual processor boots, the default mode is the sys mode. As no +supported instruction can change this mode, this is also the only mode for now. + +The entire memory space (0x00000000 to 0x0E010000, inclusive) is available, +however, no I/O-mapped addresses are currently functional. -Currently, the emulator only supports the b{cond}{l}.w instruction. Improved -support is, of course, planned. +Improved support is, of course, planned. diff --git a/src/luma.rs b/src/luma.rs index 222e97e..a13c330 100644 --- a/src/luma.rs +++ b/src/luma.rs @@ -1,7 +1,29 @@ -// Copyright 2021-2023 Gabriel Jensen. +/* + 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/>. +*/ pub mod application; pub mod configuration; +pub mod device; pub struct VersionType<T> { major: T, @@ -10,27 +32,9 @@ pub struct VersionType<T> { pub const VERSION: VersionType::<u32> = VersionType::<u32> { major: 0x0, - minor: 0x24, + minor: 0x25, }; -#[allow(dead_code)] -pub enum LogType { - Branch( i32, u32), - Continue( u32), - Link( u32), - Load( u8, u32), - MoveRegister( u8, u8), - MoveImmediate(u8, u32), - Store( u32, u8), -} - -#[allow(dead_code)] -pub enum TrapKind { - BadAlignment( u32, u32), - InvalidOpcode(u32, u32), - OutOfBounds( u32), -} - pub const CONFIGURATION_VERSION: u32 = 0x0; pub const MEMORY_SIZE: usize = 0x0E010000; diff --git a/src/luma/application.rs b/src/luma/application.rs index 4c34c9d..e41f75f 100644 --- a/src/luma/application.rs +++ b/src/luma/application.rs @@ -1,6 +1,28 @@ -// Copyright 2021-2023 Gabriel Jensen. +/* + 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::configuration::Configuration; +use crate::luma::device::Device; extern crate sdl2; @@ -8,25 +30,17 @@ use sdl2::{Sdl, VideoSubsystem}; use sdl2::video::Window; use std::sync::atomic::AtomicBool; -pub mod bootloader; -pub mod decode; pub mod drop; -pub mod image; pub mod initialise; pub mod load; -pub mod log; -pub mod read; pub mod run; -pub mod trap; pub struct Application { configuration: Configuration, sdl: Sdl, sdl_video: VideoSubsystem, window: Window, - memory: *mut u8, - registers: [u32; 0x10], - psr: u32, + device: Device, } pub static mut GOT_SIGNAL: AtomicBool = AtomicBool::new(false); diff --git a/src/luma/application/bootloader.rs b/src/luma/application/bootloader.rs deleted file mode 100644 index 2a69447..0000000 --- a/src/luma/application/bootloader.rs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2021-2023 Gabriel Jensen. - -use crate::luma::application::Application; -use crate::luma::BOOTLOADER_SIZE; - -use std::slice; - -impl Application { - pub fn bootloader<'a>(&mut self) -> &'a mut [u8] { - return unsafe { slice::from_raw_parts_mut(self.memory.offset(0x00000000), BOOTLOADER_SIZE) }; - } -} diff --git a/src/luma/application/decode.rs b/src/luma/application/decode.rs deleted file mode 100644 index bfb7765..0000000 --- a/src/luma/application/decode.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2021-2023 Gabriel Jensen. - -use crate::luma::application::Application; -use crate::luma::{LogType, TrapKind}; - -impl Application { - pub fn decode(&mut self, opcode: u32) { - let condition = match opcode & 0b11110000000000000000000000000 { - 0b00000000000000000000000000000 => self.psr & 0b01000000000000000000000000000000 != 0x00, - 0b00010000000000000000000000000 => self.psr & 0b01000000000000000000000000000000 == 0x00, - 0b00100000000000000000000000000 => self.psr & 0b00100000000000000000000000000000 != 0x00, - 0b00110000000000000000000000000 => self.psr & 0b00100000000000000000000000000000 == 0x00, - 0b01000000000000000000000000000 => self.psr & 0b10000000000000000000000000000000 != 0x00, - 0b01010000000000000000000000000 => self.psr & 0b10000000000000000000000000000000 == 0x00, - 0b01100000000000000000000000000 => self.psr & 0b00010000000000000000000000000000 != 0x00, - 0b01110000000000000000000000000 => self.psr & 0b00010000000000000000000000000000 == 0x00, - 0b10000000000000000000000000000 => self.psr & 0b00100000000000000000000000000000 != 0x00 && self.psr & 0b01000000000000000000000000000000 == 0x00, - 0b10010000000000000000000000000 => self.psr & 0b00100000000000000000000000000000 == 0x00 && self.psr & 0b01000000000000000000000000000000 != 0x00, - 0b10100000000000000000000000000 => self.psr & 0b00010000000000000000000000000000 >> 0x1C == self.psr & 0b10000000000000000000000000000000 >> 0x1F, - 0b10110000000000000000000000000 => self.psr & 0b00010000000000000000000000000000 >> 0x1C != self.psr & 0b10000000000000000000000000000000 >> 0x1F, - 0b11000000000000000000000000000 => self.psr & 0b01000000000000000000000000000000 == 0x00 && self.psr & 0b00010000000000000000000000000000 >> 0x1C == self.psr & 0b10000000000000000000000000000000 >> 0x1F, - 0b11010000000000000000000000000 => self.psr & 0b01000000000000000000000000000000 != 0x00 || self.psr & 0b00010000000000000000000000000000 >> 0x1C != self.psr & 0b10000000000000000000000000000000 >> 0x1F, - 0b11100000000000000000000000000 => true, - _ => return self.trap(TrapKind::InvalidOpcode(self.registers[0xF] - 0x8, opcode)), - }; - if !condition { return } - - if opcode & 0b00001110000000000000000000000000 == 0b00001010000000000000000000000000 { - if opcode & 0b00000001000000000000000000000000 != 0x0 { // Check the L flag. - self.registers[0xE] = self.registers[0xF] - 0x4; - - self.log(LogType::Link(self.registers[0xE])); - } - - let offset = match opcode & 0b00000000100000000000000000000000 != 0x0 { - false => (opcode & 0b00000000111111111111111111111111) as i32, - true => 0x0 - (!(opcode - 0x1) & 0b00000000111111111111111111111111) as i32, // Inverted (absolute) offset. - }; - - (self.registers[0xF], _) = self.registers[0xF].overflowing_add_signed(offset * 0x4 + 0x8); - - self.log(LogType::Branch(offset, self.registers[0xF] - 0x8)); - return; - } - - self.trap(TrapKind::InvalidOpcode(self.registers[0xF] - 0x8, opcode)); - } -} diff --git a/src/luma/application/drop.rs b/src/luma/application/drop.rs index 5652610..9bef2c4 100644 --- a/src/luma/application/drop.rs +++ b/src/luma/application/drop.rs @@ -1,15 +1,30 @@ -// Copyright 2021-2023 Gabriel Jensen. +/* + Copyright 2021-2023 Gabriel Jensen. -use crate::luma::application::Application; -use crate::luma::MEMORY_SIZE; + 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. -use std::alloc::{dealloc, Layout}; -use std::mem::size_of; + 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::application::Application; impl Drop for Application { fn drop(&mut self) { eprintln!("ending"); - - unsafe { dealloc(self.memory, Layout::new::<[u32; MEMORY_SIZE / size_of::<u32>()]>()) }; } } diff --git a/src/luma/application/image.rs b/src/luma/application/image.rs deleted file mode 100644 index 9f2aeae..0000000 --- a/src/luma/application/image.rs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2021-2023 Gabriel Jensen. - -use crate::luma::application::Application; -use crate::luma::IMAGE_SIZE; - -use std::slice; - -impl Application { - pub fn image<'a>(&mut self) -> &'a mut [u8] { - return unsafe { slice::from_raw_parts_mut(self.memory.offset(0x08000000), IMAGE_SIZE) }; - } -}
\ No newline at end of file diff --git a/src/luma/application/initialise.rs b/src/luma/application/initialise.rs index 4777e51..8bf40b8 100644 --- a/src/luma/application/initialise.rs +++ b/src/luma/application/initialise.rs @@ -1,15 +1,36 @@ -// Copyright 2021-2023 Gabriel Jensen. +/* + Copyright 2021-2023 Gabriel Jensen. -use crate::luma::{MEMORY_SIZE, SCREEN_HEIGHT, SCREEN_WIDTH}; + 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::{SCREEN_HEIGHT, SCREEN_WIDTH}; use crate::luma::application::{Application, GOT_SIGNAL}; use crate::luma::configuration::Configuration; +use crate::luma::device::Device; extern crate libc; extern crate sdl2; use libc::{c_int, sighandler_t, SIGINT, signal, SIGTERM}; -use std::alloc::{alloc_zeroed, Layout}; -use std::mem::{size_of, transmute}; +use std::mem::transmute; use std::sync::atomic::Ordering; fn signal_handler(sig: c_int) { @@ -34,36 +55,12 @@ impl Application { let window = sdl_video.window("luma", SCREEN_WIDTH as u32 * configuration.scale, SCREEN_HEIGHT as u32 * configuration.scale).position_centered().build().unwrap(); - let memory = unsafe { alloc_zeroed(Layout::new::<[u32; MEMORY_SIZE / size_of::<u32>()]>()) }; - if memory.is_null() { panic!("unable to allocate memory buffer") } - - eprintln!("allocated memory buffer at {:#0X}", memory as usize); - return Application { configuration: configuration.clone(), sdl: sdl, sdl_video: sdl_video, window: window, - memory: memory, - registers: [ - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x08000008, - ], - psr: 0b00000000000000000000000000001111, + device: Device::new(), }; } } diff --git a/src/luma/application/load.rs b/src/luma/application/load.rs index bc56922..c3000ea 100644 --- a/src/luma/application/load.rs +++ b/src/luma/application/load.rs @@ -1,4 +1,25 @@ -// Copyright 2021-2023 Gabriel Jensen. +/* + 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::application::Application; @@ -13,7 +34,7 @@ impl Application { let mut bootloader = File::open(self.configuration.bootloader.clone()).expect("unable to open bootloader"); // Read bootloader: - bootloader.read(self.bootloader()).expect("unable to read bootloader"); + bootloader.read(self.device.bootloader()).expect("unable to read bootloader"); eprintln!("loading image \"{}\"", self.configuration.image); @@ -21,6 +42,6 @@ impl Application { let mut image = File::open(self.configuration.image.clone()).expect("unable to open image"); // Read image: - image.read(self.image()).expect("unable to read image"); + image.read(self.device.image()).expect("unable to read image"); } } diff --git a/src/luma/application/log.rs b/src/luma/application/log.rs deleted file mode 100644 index 4e0eb88..0000000 --- a/src/luma/application/log.rs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2021-2023 Gabriel Jensen. - -use crate::luma::LogType; -use crate::luma::application::Application; - -impl Application { - pub fn log(&mut self, log_type: LogType) { - let type_string = match log_type { - LogType::Branch( _, _) => "branch ", - LogType::Continue( _) => "continue", - LogType::Link( _) => "link ", - LogType::Load( _, _) => "load ", - LogType::MoveRegister( _, _) => "move ", - LogType::MoveImmediate(_, _) => "move ", - LogType::Store( _, _) => "store ", - }; - - let message = match log_type { - LogType::Branch( offset, address) => format!("pc{offset:+} => {address:#010X}"), - LogType::Continue( address) => format!("pc => {address:#010X}"), - LogType::Link( address) => format!("lr => {address:#010X}"), - LogType::Load( register, address) => format!("r{register} => {address:#010X}"), - LogType::MoveRegister( destination, source) => format!("r{destination} => r{source}"), - LogType::MoveImmediate(register, immediate) => format!("r{register} => {immediate:#X}"), - LogType::Store( address, register) => format!("{address:#010X} => r{register}"), - }; - - eprintln!("{type_string} : {message}"); - } -} diff --git a/src/luma/application/read.rs b/src/luma/application/read.rs deleted file mode 100644 index 06003b7..0000000 --- a/src/luma/application/read.rs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2021-2023 Gabriel Jensen. - -use crate::luma::application::Application; -use crate::luma::{MEMORY_SIZE, TrapKind}; - -impl Application { - #[allow(dead_code)] - pub fn read_byte(&mut self, address: u32) -> u8 { - if address >= MEMORY_SIZE as u32 { self.trap(TrapKind::OutOfBounds(address)) }; - - return unsafe { *(self.memory.offset(address as isize) as *mut u8) }; - } - - #[allow(dead_code)] - pub fn read_halfword(&mut self, address: u32) -> u16 { - if address >= MEMORY_SIZE as u32 { self.trap(TrapKind::OutOfBounds(address)) }; - if address % 0x2 != 0x0 { self.trap(TrapKind::BadAlignment(address, 0x2)) }; - - return unsafe { *(self.memory.offset(address as isize) as *mut u16) }; - } - - #[allow(dead_code)] - pub fn read_word(&mut self, address: u32) -> u32 { - if address >= MEMORY_SIZE as u32 { self.trap(TrapKind::OutOfBounds(address)) }; - if address % 0x4 != 0x0 { self.trap(TrapKind::BadAlignment(address, 0x4)) }; - - return unsafe { *(self.memory.offset(address as isize) as *mut u32) }; - } -} diff --git a/src/luma/application/run.rs b/src/luma/application/run.rs index 8f7368a..14ab560 100644 --- a/src/luma/application/run.rs +++ b/src/luma/application/run.rs @@ -1,6 +1,27 @@ -// Copyright 2021-2023 Gabriel Jensen. +/* + Copyright 2021-2023 Gabriel Jensen. -use crate::luma::{LogType, VERSION}; + 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::VERSION; use crate::luma::application::{Application, GOT_SIGNAL}; use sdl2::event::Event; @@ -19,9 +40,7 @@ impl Application { let mut event_pump = self.sdl.event_pump().expect("unable to get event pump"); - eprintln!("starting emulation at {:#010X}",self.registers[0xF] - 0x8); - - 'main_loop: loop { + 'main_loop: for cycle in 0x0..u64::MAX { // Check if we have recieved a signal: if unsafe { GOT_SIGNAL.load(Ordering::Relaxed) } { eprintln!("got interrupt"); @@ -36,13 +55,8 @@ impl Application { } } - // Decode opcode: - let opcode = self.read_word(self.registers[0xF] - 0x8); - self.decode(opcode); - - // Continue: - self.registers[0xF] += 0x4; - self.log(LogType::Continue(self.registers[0xF])); + eprintln!("({cycle})"); + self.device.decode(); sleep(Duration::from_secs(0x1)); } diff --git a/src/luma/application/trap.rs b/src/luma/application/trap.rs deleted file mode 100644 index f96ccbc..0000000 --- a/src/luma/application/trap.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021-2023 Gabriel Jensen. - -use crate::luma::{MEMORY_SIZE, TrapKind}; -use crate::luma::application::Application; - -impl Application { - pub fn trap(&mut self, kind: TrapKind) { - let message = match kind { - TrapKind::BadAlignment( address, alignment) => format!("bad alignment of address {address:#010X} (should be {alignment}-byte aligned)"), - TrapKind::InvalidOpcode(address, opcode) => format!("invalid opcode {opcode:#010X} at {address:#010X}"), - TrapKind::OutOfBounds( address) => format!("out-of-bounds address {address:#010X} (limit is {MEMORY_SIZE:#010X})"), - }; - - eprintln!("trap - {message}"); - eprintln!("\tr0: {:#010X}", self.registers[0x0]); - eprintln!("\tr1: {:#010X}", self.registers[0x1]); - eprintln!("\tr2: {:#010X}", self.registers[0x2]); - eprintln!("\tr3: {:#010X}", self.registers[0x3]); - eprintln!("\tr4: {:#010X}", self.registers[0x4]); - eprintln!("\tr5: {:#010X}", self.registers[0x5]); - eprintln!("\tr6: {:#010X}", self.registers[0x6]); - eprintln!("\tr7: {:#010X}", self.registers[0x7]); - eprintln!("\tr8: {:#010X}", self.registers[0x8]); - eprintln!("\tr9: {:#010X}", self.registers[0x9]); - eprintln!("\tr10: {:#010X}", self.registers[0xA]); - eprintln!("\tr11: {:#010X}", self.registers[0xB]); - eprintln!("\tr12: {:#010X}", self.registers[0xC]); - eprintln!("\tsp: {:#010X}", self.registers[0xD]); - eprintln!("\tlr: {:#010X}", self.registers[0xE]); - eprintln!("\tpc: {:#010X}", self.registers[0xF]); - eprintln!("\tcpsr: {:#034b}", self.psr); - } -} diff --git a/src/luma/configuration.rs b/src/luma/configuration.rs index efad017..20a858f 100644 --- a/src/luma/configuration.rs +++ b/src/luma/configuration.rs @@ -1,5 +1,27 @@ -// Copyright 2021-2023 Gabriel Jensen. +/* + 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/>. +*/ + +pub mod create; pub mod load; pub mod new; pub mod overwrite; diff --git a/src/luma/configuration/create.rs b/src/luma/configuration/create.rs new file mode 100644 index 0000000..96fb25d --- /dev/null +++ b/src/luma/configuration/create.rs @@ -0,0 +1,31 @@ +// Copyright 2021-2023 Gabriel Jensen. + +use crate::luma::CONFIGURATION_VERSION; +use crate::luma::configuration::Configuration; + +use std::fs::write; + +impl Configuration { + pub(super) fn create(&mut self) { + let configuration_path = Configuration::path(); + + eprintln!("creating configuration at {configuration_path}"); + + let default_configuration = format!( + "# This is the default configuration for the\n\ + # Luma emulator.\n\ + \n\ + [luma]\n\ + version = {CONFIGURATION_VERSION}\n\ + \n\ + [device]\n\ + #bootloader = \"\"\n\ + #image = \"\"\n\ + \n\ + [video]\n\ + scale = 1\n" + ); + + write(configuration_path, default_configuration).unwrap(); + } +} diff --git a/src/luma/configuration/load.rs b/src/luma/configuration/load.rs index fd425d4..dac8570 100644 --- a/src/luma/configuration/load.rs +++ b/src/luma/configuration/load.rs @@ -1,4 +1,25 @@ -// Copyright 2021-2023 Gabriel Jensen. +/* + 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::CONFIGURATION_VERSION; use crate::luma::configuration::Configuration; @@ -39,7 +60,13 @@ impl Configuration { eprintln!("loading configuration \"{configuration_path}\""); - let contents = read_to_string(configuration_path).expect("unable to read configuration file"); + let contents = match read_to_string(configuration_path) { + Ok( contents) => contents, + Err(_) => { + eprintln!("unable to read configuration file"); + return self.create(); + }, + }; let configuration: Container = toml::from_str(contents.as_str()).expect("unable to parse configuration file"); diff --git a/src/luma/configuration/new.rs b/src/luma/configuration/new.rs index 5742dd2..eea03a9 100644 --- a/src/luma/configuration/new.rs +++ b/src/luma/configuration/new.rs @@ -1,4 +1,25 @@ -// Copyright 2021-2023 Gabriel Jensen. +/* + 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::configuration::Configuration; diff --git a/src/luma/configuration/overwrite.rs b/src/luma/configuration/overwrite.rs index ab55ff3..3c06259 100644 --- a/src/luma/configuration/overwrite.rs +++ b/src/luma/configuration/overwrite.rs @@ -1,4 +1,25 @@ -// Copyright 2021-2023 Gabriel Jensen. +/* + 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::configuration::Configuration; diff --git a/src/luma/configuration/path.rs b/src/luma/configuration/path.rs index a1ff3fd..63b3ccd 100644 --- a/src/luma/configuration/path.rs +++ b/src/luma/configuration/path.rs @@ -1,4 +1,25 @@ -// Copyright 2021-2023 Gabriel Jensen. +/* + 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::configuration::Configuration; diff --git a/src/luma/device.rs b/src/luma/device.rs new file mode 100644 index 0000000..2a0e503 --- /dev/null +++ b/src/luma/device.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/>. +*/ + +pub mod bootloader; +pub mod branch; +pub mod r#continue; +pub mod decode; +pub mod drop; +pub mod image; +pub mod store; +pub mod log; +pub mod memory; +pub mod r#move; +pub mod new; +pub mod read; +pub mod trap; +pub mod write; + +#[allow(dead_code)] +pub enum Log { + Branch( i32, u32), + Continue( u32), + Link( u32), + Load( u8, u32, u8, i32, u32), + MoveRegister( u8, u8, u32), + MoveImmediate(u8, u32), + Store( u32, u8, u8, i32, u32), +} + +#[allow(dead_code)] +pub enum Trap { + BadAlignment( u32, u32), + InvalidOpcode(u32, u32), + OutOfBounds( u32), +} + +pub struct Device { + memory: *mut u8, + registers: [u32; 0x10], + cpsr: u32, + spsr: [u32; 0x10], // We don't actually use all sixteen, we just have this many to enable us to directly use the mode number as the offset. +} diff --git a/src/luma/device/bootloader.rs b/src/luma/device/bootloader.rs new file mode 100644 index 0000000..4af66fd --- /dev/null +++ b/src/luma/device/bootloader.rs @@ -0,0 +1,33 @@ +/* + Copyright 2021-2023 Gabriel Jensen. + + This file is part of Luma. + + Luma is free software: you can redistribute it + and/or modify it under the terms of the GNU + Affero General Public License as published by + the Free Software Foundation, either version 3 + of the License, or (at your option) any later + version. + + Luma is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU + Affero General Public License along with Luma. If not, + see <https://www.gnu.org/licenses/>. +*/ + +use crate::luma::device::Device; +use crate::luma::BOOTLOADER_SIZE; + +use std::slice; + +impl Device { + pub fn bootloader<'a>(&mut self) -> &'a mut [u8] { + return unsafe { slice::from_raw_parts_mut(self.memory.offset(0x00000000), BOOTLOADER_SIZE) }; + } +} diff --git a/src/luma/device/branch.rs b/src/luma/device/branch.rs new file mode 100644 index 0000000..5654763 --- /dev/null +++ b/src/luma/device/branch.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::luma::device::{Device, Log}; + +impl Device { + pub fn branch(&mut self, offset: i32, l: bool) { + if l { // Check the l flag. + self.registers[0xE] = self.registers[0xF] - 0x4; + + self.log(Log::Link(self.registers[0xE])); + } + + (self.registers[0xF], _) = self.registers[0xF].overflowing_add_signed(offset + 0x8); // Add extra eight to move to the new fetch instruction. + + self.log(Log::Branch(offset, self.registers[0xF] - 0x8)); + } +} diff --git a/src/luma/device/continue.rs b/src/luma/device/continue.rs new file mode 100644 index 0000000..5aabbb8 --- /dev/null +++ b/src/luma/device/continue.rs @@ -0,0 +1,32 @@ +/* + Copyright 2021-2023 Gabriel Jensen. + + This file is part of Luma. + + Luma is free software: you can redistribute it + and/or modify it under the terms of the GNU + Affero General Public License as published by + the Free Software Foundation, either version 3 + of the License, or (at your option) any later + version. + + Luma is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU + Affero General Public License along with Luma. If not, + see <https://www.gnu.org/licenses/>. +*/ + +use crate::luma::device::{Device, Log}; + +impl Device { + pub fn r#continue(&mut self) { + self.registers[0xF] += 0x4; + + self.log(Log::Continue(self.registers[0xF])); + } +} diff --git a/src/luma/device/decode.rs b/src/luma/device/decode.rs new file mode 100644 index 0000000..bb31d62 --- /dev/null +++ b/src/luma/device/decode.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 crate::luma::device::{Device, Trap}; + +impl Device { + pub fn decode(&mut self) { + let opcode = self.read_word(self.registers[0xF] - 0x8); + + let condition = match opcode & 0b11110000000000000000000000000000 { + 0b00000000000000000000000000000000 => self.cpsr & 0b01000000000000000000000000000000 != 0x00, + 0b00010000000000000000000000000000 => self.cpsr & 0b01000000000000000000000000000000 == 0x00, + 0b00100000000000000000000000000000 => self.cpsr & 0b00100000000000000000000000000000 != 0x00, + 0b00110000000000000000000000000000 => self.cpsr & 0b00100000000000000000000000000000 == 0x00, + 0b01000000000000000000000000000000 => self.cpsr & 0b10000000000000000000000000000000 != 0x00, + 0b01010000000000000000000000000000 => self.cpsr & 0b10000000000000000000000000000000 == 0x00, + 0b01100000000000000000000000000000 => self.cpsr & 0b00010000000000000000000000000000 != 0x00, + 0b01110000000000000000000000000000 => self.cpsr & 0b00010000000000000000000000000000 == 0x00, + 0b10000000000000000000000000000000 => self.cpsr & 0b00100000000000000000000000000000 != 0x00 && self.cpsr & 0b01000000000000000000000000000000 == 0x00, + 0b10010000000000000000000000000000 => self.cpsr & 0b00100000000000000000000000000000 == 0x00 && self.cpsr & 0b01000000000000000000000000000000 != 0x00, + 0b10100000000000000000000000000000 => self.cpsr & 0b00010000000000000000000000000000 >> 0x1C == self.cpsr & 0b10000000000000000000000000000000 >> 0x1F, + 0b10110000000000000000000000000000 => self.cpsr & 0b00010000000000000000000000000000 >> 0x1C != self.cpsr & 0b10000000000000000000000000000000 >> 0x1F, + 0b11000000000000000000000000000000 => self.cpsr & 0b01000000000000000000000000000000 == 0x00 && self.cpsr & 0b00010000000000000000000000000000 >> 0x1C == self.cpsr & 0b10000000000000000000000000000000 >> 0x1F, + 0b11010000000000000000000000000000 => self.cpsr & 0b01000000000000000000000000000000 != 0x00 || self.cpsr & 0b00010000000000000000000000000000 >> 0x1C != self.cpsr & 0b10000000000000000000000000000000 >> 0x1F, + 0b11100000000000000000000000000000 => true, + _ => return self.trap(Trap::InvalidOpcode(self.registers[0xF] - 0x8, opcode)), + }; + if !condition { return self.r#continue() } + + // load/store + if opcode & 0b00001111001000000000000000000000 == 0b00000101000000000000000000000000 { + let register = ((opcode & 0b00000000000000001111000000000000) >> 0xC) as u8; + + let base = ((opcode & 0b00000000000011110000000000000000) >> 0x10) as u8; + + let immediate = (opcode & 0b00000000000000000000111111111111) as u16; + + let u = 0b00000000100000000000000000000000 != 0x0; + let b = 0b00000000010000000000000000000000 != 0x0; + let l = 0b00000000000100000000000000000000 != 0x0; + + self.store(register, base, immediate, u, b, l); + return self.r#continue(); + } + + // move + if opcode & 0b00001101111111100000111111110000 == 0b00000001101000000000000000000000 { + let destination = ((opcode & 0b00000000000000001111000000000000) >> 0xC) as u8; + let source = (opcode & 0b00000000000000000000000000001111) as u8; + + let value = self.registers[source as usize]; + self.registers[destination as usize] = value; + + let s = opcode & 0b00000000000100000000000000000000 != 0x0; + + self.r#move(destination, source, s); + return self.r#continue(); + } + + // branch + if opcode & 0b00001110000000000000000000000000 == 0b00001010000000000000000000000000 { + let link = opcode & 0b00000001000000000000000000000000 != 0x0; + + let offset = { + let mut offset = opcode & 0b00000000111111111111111111111111; + + if offset & 0b00000000100000000000000000000000 != 0x0 { offset |= 0b00111111000000000000000000000000 } // Sign-extend. + + offset <<= 0x2; + + offset as i32 + }; + + return self.branch(offset, link); + } + + self.trap(Trap::InvalidOpcode(self.registers[0xF] - 0x8, opcode)); + + self.r#continue(); + } +} diff --git a/src/luma/device/drop.rs b/src/luma/device/drop.rs new file mode 100644 index 0000000..6bb694b --- /dev/null +++ b/src/luma/device/drop.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::luma::device::Device; +use crate::luma::MEMORY_SIZE; + +use std::alloc::{dealloc, Layout}; +use std::mem::size_of; + +impl Drop for Device { + fn drop(&mut self) { + unsafe { dealloc(self.memory, Layout::new::<[u32; MEMORY_SIZE / size_of::<u32>()]>()) }; + } +} diff --git a/src/luma/device/image.rs b/src/luma/device/image.rs new file mode 100644 index 0000000..5fbb0be --- /dev/null +++ b/src/luma/device/image.rs @@ -0,0 +1,33 @@ +/* + Copyright 2021-2023 Gabriel Jensen. + + This file is part of Luma. + + Luma is free software: you can redistribute it + and/or modify it under the terms of the GNU + Affero General Public License as published by + the Free Software Foundation, either version 3 + of the License, or (at your option) any later + version. + + Luma is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU + Affero General Public License along with Luma. If not, + see <https://www.gnu.org/licenses/>. +*/ + +use crate::luma::device::Device; +use crate::luma::IMAGE_SIZE; + +use std::slice; + +impl Device { + pub fn image<'a>(&mut self) -> &'a mut [u8] { + return unsafe { slice::from_raw_parts_mut(self.memory.offset(0x08000000), IMAGE_SIZE) }; + } +}
\ No newline at end of file diff --git a/src/luma/device/log.rs b/src/luma/device/log.rs new file mode 100644 index 0000000..72b7982 --- /dev/null +++ b/src/luma/device/log.rs @@ -0,0 +1,50 @@ +/* + Copyright 2021-2023 Gabriel Jensen. + + This file is part of Luma. + + Luma is free software: you can redistribute it + and/or modify it under the terms of the GNU + Affero General Public License as published by + the Free Software Foundation, either version 3 + of the License, or (at your option) any later + version. + + Luma is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU + Affero General Public License along with Luma. If not, + see <https://www.gnu.org/licenses/>. +*/ + +use crate::luma::device::{Device, Log}; + +impl Device { + pub fn log(&mut self, kind: Log) { + let kind_string = match kind { + Log::Branch( ..) => "branch ", + Log::Continue( ..) => "continue", + Log::Link( ..) => "link ", + Log::Load( ..) => "load ", + Log::MoveRegister( ..) => "move ", + Log::MoveImmediate(..) => "move ", + Log::Store( ..) => "store ", + }; + + let message = match kind { + Log::Branch( offset, address) => format!("r15{offset:+} => {address:#010X}"), + Log::Continue( address) => format!("r15 => {address:#010X}"), + Log::Link( address) => format!("r14 => {address:#010X}"), + Log::Load( register, address, base, offset, value) => format!("r{register} => r{base}{offset:+}={address:#010X} ({value:#010X})"), + Log::MoveRegister( destination, source, value) => format!("r{destination} => r{source} ({value:#010X})"), + Log::MoveImmediate(register, immediate) => format!("r{register} => {immediate:#X}"), + Log::Store( address, register, base, offset, value) => format!("r{base}{offset:+}={address:#010X} => r{register} ({value:#010X})"), + }; + + eprintln!("{kind_string} : {message}"); + } +} diff --git a/src/luma/device/memory.rs b/src/luma/device/memory.rs new file mode 100644 index 0000000..f13e56b --- /dev/null +++ b/src/luma/device/memory.rs @@ -0,0 +1,30 @@ +/* + Copyright 2021-2023 Gabriel Jensen. + + This file is part of Luma. + + Luma is free software: you can redistribute it + and/or modify it under the terms of the GNU + Affero General Public License as published by + the Free Software Foundation, either version 3 + of the License, or (at your option) any later + version. + + Luma is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU + Affero General Public License along with Luma. If not, + see <https://www.gnu.org/licenses/>. +*/ + +use crate::luma::device::Device; + +impl Device { + pub fn memory(&mut self) -> *const u8{ + return self.memory as *const u8; + } +}
\ No newline at end of file diff --git a/src/luma/device/move.rs b/src/luma/device/move.rs new file mode 100644 index 0000000..ba67146 --- /dev/null +++ b/src/luma/device/move.rs @@ -0,0 +1,41 @@ +/* + Copyright 2021-2023 Gabriel Jensen. + + This file is part of Luma. + + Luma is free software: you can redistribute it + and/or modify it under the terms of the GNU + Affero General Public License as published by + the Free Software Foundation, either version 3 + of the License, or (at your option) any later + version. + + Luma is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU + Affero General Public License along with Luma. If not, + see <https://www.gnu.org/licenses/>. +*/ + +use crate::luma::device::{Device, Log}; + +impl Device { + pub fn r#move(&mut self, destination: u8, source: u8, s: bool) { + let value = self.registers[source as usize]; + self.registers[destination as usize] = value; + + if s { // Check the s flag. + if destination == 0xF { + self.cpsr = self.spsr[(self.cpsr & 0b00000000000000000000000000001111) as usize]; // We ignore the fifth bit, as this is always set. + } else { + // TO-DO + } + } + + self.log(Log::MoveRegister(destination, source, value)); + } +}
\ No newline at end of file diff --git a/src/luma/device/new.rs b/src/luma/device/new.rs new file mode 100644 index 0000000..59a3380 --- /dev/null +++ b/src/luma/device/new.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/>. +*/ + +use crate::luma::MEMORY_SIZE; +use crate::luma::device::Device; + +use std::alloc::{alloc_zeroed, Layout}; +use std::mem::size_of; + +impl Device { + pub fn new() -> Device { + eprintln!("creating new device"); + + let memory = unsafe { alloc_zeroed(Layout::new::<[u32; MEMORY_SIZE / size_of::<u32>()]>()) }; + if memory.is_null() { panic!("unable to allocate memory buffer") } + + eprintln!("allocated memory buffer at {:#0X}", memory as usize); + + let start = 0x08000008; + eprintln!("starting emulation at {start:#08X}"); + + return Device { + memory: memory, + registers: [ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + start, + ], + cpsr: 0b00000000000000000000000000001111, + spsr: [0b00000000000000000000000000000000; 0x10], + }; + } +} diff --git a/src/luma/device/read.rs b/src/luma/device/read.rs new file mode 100644 index 0000000..0724075 --- /dev/null +++ b/src/luma/device/read.rs @@ -0,0 +1,50 @@ +/* + Copyright 2021-2023 Gabriel Jensen. + + This file is part of Luma. + + Luma is free software: you can redistribute it + and/or modify it under the terms of the GNU + Affero General Public License as published by + the Free Software Foundation, either version 3 + of the License, or (at your option) any later + version. + + Luma is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU + Affero General Public License along with Luma. If not, + see <https://www.gnu.org/licenses/>. +*/ + +use crate::luma::device::{Device, Trap}; +use crate::luma::MEMORY_SIZE; + +impl Device { + #[allow(dead_code)] + pub fn read_byte(&mut self, address: u32) -> u8 { + if address >= MEMORY_SIZE as u32 { self.trap(Trap::OutOfBounds(address)) }; + + return unsafe { *(self.memory.offset(address as isize) as *mut u8) }; + } + + #[allow(dead_code)] + pub fn read_halfword(&mut self, address: u32) -> u16 { + if address >= MEMORY_SIZE as u32 { self.trap(Trap::OutOfBounds(address)) }; + if address % 0x2 != 0x0 { self.trap(Trap::BadAlignment(address, 0x2)) }; + + return unsafe { *(self.memory.offset(address as isize) as *mut u16) }; + } + + #[allow(dead_code)] + pub fn read_word(&mut self, address: u32) -> u32 { + if address >= MEMORY_SIZE as u32 { self.trap(Trap::OutOfBounds(address)) }; + if address % 0x4 != 0x0 { self.trap(Trap::BadAlignment(address, 0x4)) }; + + return unsafe { *(self.memory.offset(address as isize) as *mut u32) }; + } +} diff --git a/src/luma/device/store.rs b/src/luma/device/store.rs new file mode 100644 index 0000000..21fb3a7 --- /dev/null +++ b/src/luma/device/store.rs @@ -0,0 +1,50 @@ +/* + Copyright 2021-2023 Gabriel Jensen. + + This file is part of Luma. + + Luma is free software: you can redistribute it + and/or modify it under the terms of the GNU + Affero General Public License as published by + the Free Software Foundation, either version 3 + of the License, or (at your option) any later + version. + + Luma is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU + Affero General Public License along with Luma. If not, + see <https://www.gnu.org/licenses/>. +*/ + +use crate::luma::device::{Device, Log}; + +impl Device { + pub fn store(&mut self, register: u8, base: u8, immediate: u16, u: bool, _b: bool, l: bool) { + // TO-DO: Byte loads/stores. + + let offset = if u { + 0x0 + immediate as i32 + } else { + 0x0 - immediate as i32 + }; + + let (address, _) = self.registers[base as usize].overflowing_add_signed(offset); + + if l { // Check the l flag. + let value = self.read_word(address); + self.registers[register as usize] = value; + + self.log(Log::Load(register, address, base, offset, value)); + } else { + let value = self.registers[register as usize]; + self.write_word(address, value); + + self.log(Log::Store(address, register, base, offset, value)); + } + } +} diff --git a/src/luma/device/trap.rs b/src/luma/device/trap.rs new file mode 100644 index 0000000..8af3622 --- /dev/null +++ b/src/luma/device/trap.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::luma::MEMORY_SIZE; +use crate::luma::device::{Device, Trap}; + +impl Device { + pub fn trap(&mut self, kind: Trap) { + let message = match kind { + Trap::BadAlignment( address, alignment) => format!("bad alignment of address {address:#010X} (should be {alignment}-byte aligned)"), + Trap::InvalidOpcode(address, opcode) => format!("invalid opcode {opcode:#034b} at {address:#010X}"), + Trap::OutOfBounds( address) => format!("out-of-bounds address {address:#010X} (limit is {MEMORY_SIZE:#010X})"), + }; + + eprintln!("{message}"); + eprintln!(" r0: {:#010X}", self.registers[0x0]); + eprintln!(" r1: {:#010X}", self.registers[0x1]); + eprintln!(" r2: {:#010X}", self.registers[0x2]); + eprintln!(" r3: {:#010X}", self.registers[0x3]); + eprintln!(" r4: {:#010X}", self.registers[0x4]); + eprintln!(" r5: {:#010X}", self.registers[0x5]); + eprintln!(" r6: {:#010X}", self.registers[0x6]); + eprintln!(" r7: {:#010X}", self.registers[0x7]); + eprintln!(" r8: {:#010X}", self.registers[0x8]); + eprintln!(" r9: {:#010X}", self.registers[0x9]); + eprintln!(" r10: {:#010X}", self.registers[0xA]); + eprintln!(" r11: {:#010X}", self.registers[0xB]); + eprintln!(" r12: {:#010X}", self.registers[0xC]); + eprintln!(" r13: {:#010X}", self.registers[0xD]); + eprintln!(" r14: {:#010X}", self.registers[0xE]); + eprintln!(" r15: {:#010X}", self.registers[0xF]); + eprintln!(" cpsr: {:#034b}", self.cpsr); + eprintln!(" spsr_fiq: {:#034b}", self.spsr[0x1]); + eprintln!(" spsr_irq: {:#034b}", self.spsr[0x2]); + eprintln!(" spsr_svc: {:#034b}", self.spsr[0x3]); + eprintln!(" spsr_abt: {:#034b}", self.spsr[0x7]); + eprintln!(" spsr_und: {:#034b}", self.spsr[0xB]); + } +} diff --git a/src/luma/device/write.rs b/src/luma/device/write.rs new file mode 100644 index 0000000..e234bef --- /dev/null +++ b/src/luma/device/write.rs @@ -0,0 +1,50 @@ +/* + Copyright 2021-2023 Gabriel Jensen. + + This file is part of Luma. + + Luma is free software: you can redistribute it + and/or modify it under the terms of the GNU + Affero General Public License as published by + the Free Software Foundation, either version 3 + of the License, or (at your option) any later + version. + + Luma is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU + Affero General Public License along with Luma. If not, + see <https://www.gnu.org/licenses/>. +*/ + +use crate::luma::device::{Device, Trap}; +use crate::luma::MEMORY_SIZE; + +impl Device { + #[allow(dead_code)] + pub fn write_byte(&mut self, address: u32, value: u8) { + if address >= MEMORY_SIZE as u32 { self.trap(Trap::OutOfBounds(address)) }; + + return unsafe { *(self.memory.offset(address as isize) as *mut u8) = value }; + } + + #[allow(dead_code)] + pub fn write_halfword(&mut self, address: u32, value: u16) { + if address >= MEMORY_SIZE as u32 { self.trap(Trap::OutOfBounds(address)) }; + if address % 0x2 != 0x0 { self.trap(Trap::BadAlignment(address, 0x2)) }; + + return unsafe { *(self.memory.offset(address as isize) as *mut u16) = value }; + } + + #[allow(dead_code)] + pub fn write_word(&mut self, address: u32, value: u32) { + if address >= MEMORY_SIZE as u32 { self.trap(Trap::OutOfBounds(address)) }; + if address % 0x4 != 0x0 { self.trap(Trap::BadAlignment(address, 0x4)) }; + + return unsafe { *(self.memory.offset(address as isize) as *mut u32) = value }; + } +} diff --git a/src/main.rs b/src/main.rs index 4ad9cf9..1d45ca3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,25 @@ -// Copyright 2021-2023 Gabriel Jensen. +/* + 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/>. +*/ mod luma; |