diff options
-rw-r--r-- | CHANGELOG.txt | 56 | ||||
-rw-r--r-- | Cargo.toml | 7 | ||||
-rw-r--r-- | README.html | 16 | ||||
-rw-r--r-- | README.txt | 33 | ||||
-rw-r--r-- | src/luma.rs | 16 | ||||
-rw-r--r-- | src/luma/application.rs | 24 | ||||
-rw-r--r-- | src/luma/application/decode.rs | 11 | ||||
-rw-r--r-- | src/luma/application/initialise.rs | 22 | ||||
-rw-r--r-- | src/luma/application/load.rs | 8 | ||||
-rw-r--r-- | src/luma/application/parse_parameters.rs | 26 | ||||
-rw-r--r-- | src/luma/application/read.rs | 4 | ||||
-rw-r--r-- | src/luma/application/run.rs | 12 | ||||
-rw-r--r-- | src/luma/application/trap.rs | 6 | ||||
-rw-r--r-- | src/luma/configuration.rs | 14 | ||||
-rw-r--r-- | src/luma/configuration/create.rs | 28 | ||||
-rw-r--r-- | src/luma/configuration/load.rs | 80 | ||||
-rw-r--r-- | src/luma/configuration/new.rs | 18 | ||||
-rw-r--r-- | src/luma/configuration/overwrite.rs | 20 | ||||
-rw-r--r-- | src/luma/configuration/path.rs | 14 | ||||
-rw-r--r-- | src/main.rs | 5 |
20 files changed, 306 insertions, 114 deletions
diff --git a/CHANGELOG.txt b/CHANGELOG.txt index cc25adf..734db7b 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,11 +1,19 @@ -# 0.29 +# 0.22 + +* Survive traps; +* Use hexadecimal version numbers; +* Rewrite readme into ASCII-text; +* Don't default image; +* Add configuration file; + +# 0.21 * Update version constant to include minor versions; * Use SDL2 for windowing; * Combine Application and Emulator structures; * Rename opcode method to decode; -# 0.28 +# 0.20 * Support bl; * Update register format; @@ -13,14 +21,14 @@ * Add memory read helper functions; * Update naming convention; -# 0.27 +# 0.1F * Update trap function; * Add emulator helper structure; * Support conditional instructions; * Set signal handlers; -# 0.26 +# 0.1E * Repurpose project for emulating the AGB; * Make changelog plain-text (rename to CHANGELOG.txt); @@ -29,11 +37,11 @@ * Use Git tagging; * Update versioning: major.minor; -# 25 +# 1D * Fix logs being forced disabled. -# 24 +# 1C * Depend on SDL2. * Remove include directory path. @@ -47,7 +55,7 @@ * Improve some loggers. * Create new test program. -# 23 +# 1B * Implement more instructions. * Update project description. @@ -56,11 +64,11 @@ * Add more instructions. * Remove sound buffer. -# 22 +# 1A * Fix version number being out of date. -# 21 +# 19 * Remove old readme. * Update memory model. @@ -74,37 +82,37 @@ * Writes in ROM no longer succeed. * Create SIGINT handler. -# 20 +# 18 * Move all UTF-8 related code into a seperate project, *u8c*. * Rewrite project. * Require C99 instead of C17. * Reformat the readme into HTML. -# 1↋ +# 17 * Create *bin* folder in destination directory when installing. -# 1↊ +# 16 * Remove memory leaks. * Require C17 instead of C2x. * Create license notices in source files. * Create install and uninstall targets in Makefile. -# 19 +# 15 * Improve Makefile. * Improve UTF-8 encoder. * Complete UTF-8 decoder. * Create basic print function. -# 18 +# 14 * Complete UTF-8 encoder. * Fix #1. -# 17 +# 13 * Reformat changelog to Markdown. * Completely rework codebase (multiple times, in C, C++, Objective-C and Rust). Finally decide on C. @@ -115,20 +123,20 @@ * Create functions for decoding and encoding UTF-8. * Don't include entire changelog in commit message. -# 16 +# 12 * Remove build artifacts. -# 15 +# 11 * Compile "luma" instead of "luma.bin". * Get input file via arguments passed to executable. -# 14 +# 10 * Reformat README.html to Markdown. -# 13 +# F * Add "changelog.html" to keep track of changes. * Remove deprecated gfx library files. @@ -139,7 +147,7 @@ * Begin creation Luma stdlib API. * Build "luma.bin" file instead of "luma.elf". -# 12 +# E * revert .gitignore styling * reorganize source code structure in filesystem @@ -150,14 +158,14 @@ * unite core functions in class with app data (replaces luma::dat) for easier access (no friends needed, "this->" instead of "luma::dat.") * reformat README into HTML (temporary change, will be reformated again in later commit) -# 11 +# D * create the arch_t and kernel_t types * use char const * instead of std::string * use custom function instead of std::cerr and std::cout * replace as many stdlib function with custom-made ones -# 10 +# C * redo .gitignore ifle * clean up Makefile @@ -167,12 +175,12 @@ * remove C relics * create semi-working Vulkan test -# ↋ +# B * readd x support but only for non-linux systems (may change in the future) * fix makefile cxxflags -# ↊ +# A * drop x support * move codebase to c++ @@ -1,6 +1,6 @@ [package] name = "luma" -version = "0.33.0" +version = "0.34.0" edition = "2021" [[bin]] @@ -14,5 +14,6 @@ overflow-checks = false lto = true [dependencies] -libc = "0.2.146" -sdl2 = "0.35.2" +configparser = "3.0.2" +libc = "0.2.146" +sdl2 = "0.35.2" diff --git a/README.html b/README.html deleted file mode 100644 index 71d88b3..0000000 --- a/README.html +++ /dev/null @@ -1,16 +0,0 @@ -<!DOCTYPE html> -<html> - <body> - <h1>luma</h1> - <p><a href="https://mandelbrot.dk/luma">luma</a> is a free, open-source, and portable application for the luma platform.</p> - <p>This project serves as the reference implementation for the luma platform.</p> - <h2 id="copyright-license">Copyright & License</h2> - <p>Copyright 2021, 2022 Gabriel Jensen.</p> - <p>All rights reserved.</p> - <p>This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.</p> - <p>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.</p> - <p>See the GNU Affero General Public License for more details.</p> - <p>You should have received a copy of the GNU Affero General Public License along with this program.</p> - <p>If not, see <a href="https://www.gnu.org/licenses"><https://www.gnu.org/licenses></a>.</p> - </body> -</html> diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..b07663c --- /dev/null +++ b/README.txt @@ -0,0 +1,33 @@ +LUMA + +Copyright 2021-2023 Gabriel Jensen. + +luma is an emulator for the AGB - Game Boy Advance - platform. + +USAGE + +luma [image] [bootloader] + +Invoke the emulator via the 'luma' command. + +CONFIGURATION + +The emulator tries to read the configuration file at '${HOME}/.luma.ini'. If +this file is found, the following fields are read (if present): + +luma: + - version: (Uint) 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) + +video: + - scale: (Uint) The scale modifier applied to the screen (min 1; max (2^32-1)) + +These settings are overwritten by terminal parameters (see USAGE). + +COMPATIBILITY + +Currently, the emulator only supports the b{cond}{l}.w instruction. Improved +support is, of course, planned. diff --git a/src/luma.rs b/src/luma.rs index af45480..4be467a 100644 --- a/src/luma.rs +++ b/src/luma.rs @@ -1,6 +1,7 @@ // Copyright 2021-2023 Gabriel Jensen. pub mod application; +pub mod configuration; pub struct VersionType<T> { major: T, @@ -9,10 +10,21 @@ pub struct VersionType<T> { pub const VERSION: VersionType::<u32> = VersionType::<u32> { major: 0x0, - minor: 0x21, + minor: 0x22, }; +pub enum TrapKind { + BadAlignment, + InvalidOpcode, + OutOfBounds, +} + +pub const CONFIGURATION_VERSION: u32 = 0x0; + pub const MEMORY_SIZE: usize = 0x0E010000; pub const BOOTLOADER_SIZE: usize = 0x00004000; -pub const IMAGE_SIZE: usize = 0x02000000; +pub const IMAGE_SIZE: usize = 0x02000000; + +pub const SCREEN_WIDTH: u8 = 0xF0; +pub const SCREEN_HEIGHT: u8 = 0xA0; diff --git a/src/luma/application.rs b/src/luma/application.rs index 63f7d92..efb4beb 100644 --- a/src/luma/application.rs +++ b/src/luma/application.rs @@ -1,5 +1,7 @@ // Copyright 2021-2023 Gabriel Jensen. +use crate::luma::configuration::Configuration; + extern crate sdl2; use sdl2::{Sdl, VideoSubsystem}; @@ -13,26 +15,18 @@ pub mod end; pub mod image; pub mod initialise; pub mod load; -pub mod parse_parameters; pub mod read; pub mod run; pub mod trap; -pub enum TrapKind { - BadAlignment, - InvalidOpcode, - OutOfBounds, -} - pub struct Application { - bootloader: String, - image: String, - sdl: Sdl, - sdl_video: VideoSubsystem, - window: Window, - memory: *mut u8, - registers: [u32; 0x10], - psr: u32, + configuration: Configuration, + sdl: Sdl, + sdl_video: VideoSubsystem, + window: Window, + memory: *mut u8, + registers: [u32; 0x10], + psr: u32, } pub static mut GOT_SIGNAL: AtomicBool = AtomicBool::new(false); diff --git a/src/luma/application/decode.rs b/src/luma/application/decode.rs index feca794..2c18ab1 100644 --- a/src/luma/application/decode.rs +++ b/src/luma/application/decode.rs @@ -1,6 +1,7 @@ // Copyright 2021-2023 Gabriel Jensen. -use crate::luma::application::{Application, TrapKind}; +use crate::luma::application::Application; +use crate::luma::TrapKind; impl Application { pub fn decode(&mut self, opcode: u32) { @@ -20,7 +21,7 @@ impl Application { 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, - _ => { self.trap(TrapKind::InvalidOpcode, Some(self.registers[0xF] - 0x8), Some(opcode), None); unreachable!(); }, + _ => { self.trap(TrapKind::InvalidOpcode, Some(self.registers[0xF] - 0x8), Some(opcode), None); false }, }; if !condition { return }; @@ -28,7 +29,10 @@ impl Application { let off = opcode & 0b00000000111111111111111111111111; // Offset from pc. let inv = !(opcode - 0x1) & 0b00000000111111111111111111111111; // Inverted offset. - if opcode & 0b00000001000000000000000000000000 != 0x0 { self.registers[0xE] = self.registers[0xF] - 0x4; } + if opcode & 0b00000001000000000000000000000000 != 0x0 { + self.registers[0xE] = self.registers[0xF] - 0x4; + eprintln!("link: lr => {}", self.registers[0xE]); + } self.registers[0xF] = match (off & 0b00000000100000000000000000000000) != 0x0 { // If negative... false => self.registers[0xF] + off * 0x4 + 0x8, @@ -40,6 +44,5 @@ impl Application { } self.trap(TrapKind::InvalidOpcode, Some(self.registers[0xF] - 0x8), Some(opcode), None); - unreachable!(); } } diff --git a/src/luma/application/initialise.rs b/src/luma/application/initialise.rs index e9bf0ef..67b0133 100644 --- a/src/luma/application/initialise.rs +++ b/src/luma/application/initialise.rs @@ -1,7 +1,8 @@ // Copyright 2021-2023 Gabriel Jensen. +use crate::luma::{MEMORY_SIZE, SCREEN_HEIGHT, SCREEN_WIDTH}; use crate::luma::application::{Application, GOT_SIGNAL}; -use crate::luma::MEMORY_SIZE; +use crate::luma::configuration::Configuration; extern crate libc; extern crate sdl2; @@ -20,7 +21,7 @@ fn signal_handler(sig: c_int) { } impl Application { - pub fn initialise() -> Application { + pub fn initialise(configuration: &Configuration) -> Application { eprintln!("initialising"); unsafe { @@ -31,7 +32,7 @@ impl Application { let sdl = sdl2::init().expect("unable to initialise sdl2"); let sdl_video = sdl.video().expect("unable to initialise sdl2"); - let window = sdl_video.window("luma", 0xF0, 0xA0).position_centered().build().unwrap(); + 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") } @@ -39,13 +40,12 @@ impl Application { eprintln!("allocated memory buffer at 0x{:0X}", memory as usize); return Application { - bootloader: "bootloader.bin".to_string(), - image: "image.agb".to_string(), - sdl: sdl, - sdl_video: sdl_video, - window: window, - memory: memory, - registers: [ + configuration: configuration.clone(), + sdl: sdl, + sdl_video: sdl_video, + window: window, + memory: memory, + registers: [ 0x00000000, 0x00000000, 0x00000000, @@ -63,7 +63,7 @@ impl Application { 0x00000000, 0x08000008, ], - psr: 0b00000000000000000000000000001111, + psr: 0b00000000000000000000000000001111, }; } } diff --git a/src/luma/application/load.rs b/src/luma/application/load.rs index 58491d7..bc56922 100644 --- a/src/luma/application/load.rs +++ b/src/luma/application/load.rs @@ -7,18 +7,18 @@ use std::io::Read; impl Application { pub fn load(&mut self) { - eprintln!("loading booatloader \"{}\"", self.bootloader); + eprintln!("loading booatloader \"{}\"", self.configuration.bootloader); // Open bootloader: - let mut bootloader = File::open(self.bootloader.clone()).expect("unable to open bootloader"); + 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"); - eprintln!("loading image \"{}\"", self.image); + eprintln!("loading image \"{}\"", self.configuration.image); // Open image: - let mut image = File::open(self.image.clone()).expect("unable to open image"); + 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"); diff --git a/src/luma/application/parse_parameters.rs b/src/luma/application/parse_parameters.rs deleted file mode 100644 index 46e619d..0000000 --- a/src/luma/application/parse_parameters.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2021-2023 Gabriel Jensen. - -use crate::luma::application::Application; - -use std::env::args; - -impl Application { - pub fn parse_parameters(&mut self) { - eprintln!("parsing parameters"); - - let parameters: Vec<String> = args().collect(); - let number = parameters.len(); - - if number >= 0x2 { - self.image = parameters[0x1].clone(); - } - - if number >= 0x3 { - self.bootloader = parameters[0x2].clone(); - } - - if number > 0x3 { - self.end(0x1, Some(format!("invalid number of parameters ({number})").as_str())); - } - } -} diff --git a/src/luma/application/read.rs b/src/luma/application/read.rs index 40604a9..ac1b4aa 100644 --- a/src/luma/application/read.rs +++ b/src/luma/application/read.rs @@ -1,7 +1,7 @@ // Copyright 2021-2023 Gabriel Jensen. -use crate::luma::application::{Application, TrapKind}; -use crate::luma::MEMORY_SIZE; +use crate::luma::application::Application; +use crate::luma::{MEMORY_SIZE, TrapKind}; impl Application { #[allow(dead_code)] diff --git a/src/luma/application/run.rs b/src/luma/application/run.rs index a0a3f83..59f2144 100644 --- a/src/luma/application/run.rs +++ b/src/luma/application/run.rs @@ -1,16 +1,20 @@ // Copyright 2021-2023 Gabriel Jensen. -use crate::luma::application::{Application, GOT_SIGNAL}; use crate::luma::VERSION; +use crate::luma::application::{Application, GOT_SIGNAL}; use sdl2::event::Event; use std::sync::atomic::Ordering; +use std::thread::sleep; +use std::time::Duration; impl Application { pub fn run(&mut self) { + eprintln!(); eprintln!("luma {}.{}", VERSION.major, VERSION.minor); + eprintln!("Copyright 2021-2023 Gabriel Jensen."); + eprintln!(); - self.parse_parameters(); self.load(); let mut event_pump = self.sdl.event_pump().expect("unable to get event pump"); @@ -24,6 +28,7 @@ impl Application { break; } + // Iterate over events: for event in event_pump.poll_iter() { match event { Event::Quit {..} => break 'main_loop, @@ -37,6 +42,9 @@ impl Application { // Continue: self.registers[0xF] += 0x4; + eprintln!("continue: pc => {:08X}", self.registers[0xF]); + + sleep(Duration::from_secs(0x1)); } } } diff --git a/src/luma/application/trap.rs b/src/luma/application/trap.rs index 7f484ba..7b7adbf 100644 --- a/src/luma/application/trap.rs +++ b/src/luma/application/trap.rs @@ -1,7 +1,7 @@ // Copyright 2021-2023 Gabriel Jensen. -use crate::luma::application::{Application, TrapKind}; -use crate::luma::MEMORY_SIZE; +use crate::luma::{MEMORY_SIZE, TrapKind}; +use crate::luma::application::Application; impl Application { pub fn trap(&mut self, kind: TrapKind, address: Option<u32>, opcode: Option<u32>, alignment: Option<u32>) { @@ -30,7 +30,5 @@ impl Application { eprintln!("\tlr: {:08X}", self.registers[0xE]); eprintln!("\tpc: {:08X}", self.registers[0xF]); eprintln!("\tcpsr: {:032b}", self.psr); - - panic!("{message}"); } } diff --git a/src/luma/configuration.rs b/src/luma/configuration.rs new file mode 100644 index 0000000..045e897 --- /dev/null +++ b/src/luma/configuration.rs @@ -0,0 +1,14 @@ +// Copyright 2021-2023 Gabriel Jensen. + +pub mod create; +pub mod load; +pub mod new; +pub mod overwrite; +pub mod path; + +#[derive(Clone)] +pub struct Configuration { + pub bootloader: String, + pub image: String, + pub scale: u32, +} diff --git a/src/luma/configuration/create.rs b/src/luma/configuration/create.rs new file mode 100644 index 0000000..b5f2312 --- /dev/null +++ b/src/luma/configuration/create.rs @@ -0,0 +1,28 @@ +// 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!( + "[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 new file mode 100644 index 0000000..a3fe455 --- /dev/null +++ b/src/luma/configuration/load.rs @@ -0,0 +1,80 @@ +// Copyright 2021-2023 Gabriel Jensen. + +use crate::luma::CONFIGURATION_VERSION; +use crate::luma::configuration::Configuration; + +use configparser::ini::Ini; +use std::env::var; + +impl Configuration { + pub(super) fn load(&mut self) { + let configuration_path = Configuration::path(); + + eprintln!("loading configuration \"{configuration_path}\""); + + let mut configuration = Ini::new(); + + match configuration.load(configuration_path) { + Ok( _) => {}, + Err(_) => { + eprintln!("unable to read configuration"); + + return self.create(); + }, + } + + let get_path = |configuration: &mut Ini, section: &str, entry: &str| -> Option<String> { + match configuration.get(section, entry) { + Some(path) => { + if path.chars().nth(0x0).unwrap() != '/' { + let home_directory = match var("HOME") { + Ok( path) => path, + Err(_) => { eprintln!("unable to get home directory"); "".to_string() }, + }; + + return Some(home_directory + "/" + &path) + } + + return Some(path); + }, + None => None, + } + }; + + let get_unsigned = |configuration: &mut Ini, section: &str, entry: &str| -> Option<u64> { + match configuration.getuint(section, entry) { + Ok( optional) => optional, + Err(_) => panic!("invalid format of '{entry}' in configuration under '{section}"), + } + }; + + let version = get_unsigned(&mut configuration, "luma", "version").expect("'version' field not defined in configuration under 'luma'") as u32; + + if version < CONFIGURATION_VERSION { panic!("out-of-date configuration - please upgrade") }; + if version > CONFIGURATION_VERSION { panic!("future configuration - please downgrade") }; + + match get_path(&mut configuration, "device", "bootloader") { + Some(path) => { + self.bootloader = path; + }, + None => {}, + } + + match get_path(&mut configuration, "device", "image") { + Some(path) => { + self.image = path; + }, + None => {}, + } + + match get_unsigned(&mut configuration, "video", "scale") { + Some(value) => { + assert!(value >= 0x1); + assert!(value <= 0xFFFFFFFF); + + self.scale = value as u32; + }, + None => {}, + } + } +} diff --git a/src/luma/configuration/new.rs b/src/luma/configuration/new.rs new file mode 100644 index 0000000..5742dd2 --- /dev/null +++ b/src/luma/configuration/new.rs @@ -0,0 +1,18 @@ +// Copyright 2021-2023 Gabriel Jensen. + +use crate::luma::configuration::Configuration; + +impl Configuration { + pub fn new() -> Configuration { + let mut configuration = Configuration { + bootloader: "bootloader.bin".to_string(), + image: "image.agb".to_string(), + scale: 0x1, + }; + + configuration.load(); + configuration.overwrite(); + + return configuration; + } +} diff --git a/src/luma/configuration/overwrite.rs b/src/luma/configuration/overwrite.rs new file mode 100644 index 0000000..ab55ff3 --- /dev/null +++ b/src/luma/configuration/overwrite.rs @@ -0,0 +1,20 @@ +// Copyright 2021-2023 Gabriel Jensen. + +use crate::luma::configuration::Configuration; + +use std::env::args; + +impl Configuration { + pub(super) fn overwrite(&mut self) { + eprintln!("overwritting settings"); + + let parameters: Vec<String> = args().collect(); + let number = parameters.len(); + + if number >= 0x2 { self.image = parameters[0x1].clone() } + + if number >= 0x3 { self.bootloader = parameters[0x2].clone() } + + if number > 0x3 { panic!("invalid number of parameters ({number})") } + } +} diff --git a/src/luma/configuration/path.rs b/src/luma/configuration/path.rs new file mode 100644 index 0000000..0db3d98 --- /dev/null +++ b/src/luma/configuration/path.rs @@ -0,0 +1,14 @@ +// Copyright 2021-2023 Gabriel Jensen. + +use crate::luma::configuration::Configuration; + +use std::env::var; + +impl Configuration { + pub(super) fn path() -> String { + return match var("HOME") { + Ok( path) => path, + Err(_) => panic!("unable to get home directory"), + } + "/.luma.ini"; + } +} diff --git a/src/main.rs b/src/main.rs index 92e5dcc..4ad9cf9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,8 +3,11 @@ mod luma; use crate::luma::application::Application; +use crate::luma::configuration::Configuration; fn main() { - let mut application = Application::initialise(); + let configuration = Configuration::new(); + + let mut application = Application::initialise(&configuration); application.run(); } |