aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorMiquel Sabaté Solà <mikisabate@gmail.com>2024-12-22 22:40:11 +0100
committerMiquel Sabaté Solà <mikisabate@gmail.com>2024-12-22 23:05:18 +0100
commit4628873cfc53d3bdc7a0834b3edbb02b0b9cf4a7 (patch)
tree59d4fefeac7ce0d095f6e86712d5c70816a2e428 /lib
parent0bc97c99c644955096b3e13a2a7df6b281314e25 (diff)
downloadtools.nes-4628873cfc53d3bdc7a0834b3edbb02b0b9cf4a7.tar.gz
tools.nes-4628873cfc53d3bdc7a0834b3edbb02b0b9cf4a7.zip
Transform mapping configurations into toml files
This will allow the creation of configuration files that can live outside of this tree, so developers can fine tune configuration files of their own without having to pick up whatever is currently available. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/xixanta/Cargo.toml1
-rw-r--r--lib/xixanta/fuzz/Cargo.lock137
-rw-r--r--lib/xixanta/fuzz/fuzz_targets/fuzz_target_assembler.rs5
-rw-r--r--lib/xixanta/src/assembler.rs58
-rw-r--r--lib/xixanta/src/mapping.rs530
-rw-r--r--lib/xixanta/src/mappings/empty.toml12
-rw-r--r--lib/xixanta/src/mappings/nrom.toml27
-rw-r--r--lib/xixanta/src/mappings/nrom65.toml27
-rw-r--r--lib/xixanta/src/mappings/unrom.toml69
9 files changed, 489 insertions, 377 deletions
diff --git a/lib/xixanta/Cargo.toml b/lib/xixanta/Cargo.toml
index d14bb60..66f0a76 100644
--- a/lib/xixanta/Cargo.toml
+++ b/lib/xixanta/Cargo.toml
@@ -12,3 +12,4 @@ rust-version.workspace = true
[dependencies]
lazy_static = "1.5.0"
+toml = { version = "0.8", features = ["preserve_order"] }
diff --git a/lib/xixanta/fuzz/Cargo.lock b/lib/xixanta/fuzz/Cargo.lock
index d06a484..eb897dd 100644
--- a/lib/xixanta/fuzz/Cargo.lock
+++ b/lib/xixanta/fuzz/Cargo.lock
@@ -20,6 +20,28 @@ dependencies = [
]
[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "hashbrown"
+version = "0.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
+
+[[package]]
+name = "indexmap"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
name = "jobserver"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -52,22 +74,137 @@ dependencies = [
]
[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
name = "once_cell"
version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
+name = "proc-macro2"
+version = "1.0.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.216"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.216"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
+dependencies = [
+ "serde",
+]
+
+[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
+name = "syn"
+version = "2.0.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "toml"
+version = "0.8.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.22.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
+
+[[package]]
+name = "winnow"
+version = "0.6.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
name = "xixanta"
version = "0.1.0"
dependencies = [
"lazy_static",
+ "toml",
]
[[package]]
diff --git a/lib/xixanta/fuzz/fuzz_targets/fuzz_target_assembler.rs b/lib/xixanta/fuzz/fuzz_targets/fuzz_target_assembler.rs
index db33241..93a7651 100644
--- a/lib/xixanta/fuzz/fuzz_targets/fuzz_target_assembler.rs
+++ b/lib/xixanta/fuzz/fuzz_targets/fuzz_target_assembler.rs
@@ -2,9 +2,10 @@
use libfuzzer_sys::fuzz_target;
use xixanta::assembler::Assembler;
-use xixanta::mapping::EMPTY;
+use xixanta::mapping::get_mapping_configuration;
fuzz_target!(|data: &[u8]| {
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let config = get_mapping_configuration("empty").unwrap();
+ let mut asm = Assembler::new(config);
let _ = asm.assemble(std::env::current_dir().unwrap().to_path_buf(), data);
});
diff --git a/lib/xixanta/src/assembler.rs b/lib/xixanta/src/assembler.rs
index e6f3396..d24a555 100644
--- a/lib/xixanta/src/assembler.rs
+++ b/lib/xixanta/src/assembler.rs
@@ -82,8 +82,6 @@ pub struct Assembler {
impl Assembler {
pub fn new(mappings: Vec<Mapping>) -> Self {
- crate::mapping::assert(&mappings);
-
Self {
context: Context::new(),
literal_mode: None,
@@ -1557,7 +1555,7 @@ impl Assembler {
#[cfg(test)]
mod tests {
use super::*;
- use crate::mapping::{SectionType, Segment, EMPTY};
+ use crate::mapping::{get_mapping_configuration, SectionType, Segment};
fn one_two() -> Vec<Mapping> {
vec![
@@ -1600,6 +1598,10 @@ mod tests {
]
}
+ fn empty() -> Vec<Mapping> {
+ get_mapping_configuration("empty").unwrap()
+ }
+
fn minimal_header() -> Vec<Bundle> {
vec![
Bundle::fill(0x4E), // N
@@ -1615,7 +1617,7 @@ mod tests {
// Set up the empty mapper, but we have to push a minimal header
// (otherwise an early assertion will fail), and we need to point to the
// "CODE" segment (which is in the mapping indexed by 1).
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let mut asm = Assembler::new(empty());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
asm.current_mapping = 1;
@@ -1636,7 +1638,7 @@ mod tests {
}
fn assert_error(line: &str, line_num: usize, global: bool, message: &str) {
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let mut asm = Assembler::new(empty());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
asm.current_mapping = 1;
@@ -1668,7 +1670,7 @@ mod tests {
#[test]
fn empty_line() {
for line in vec!["", " ", ";; Comment", " ;; Comment"].into_iter() {
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let mut asm = Assembler::new(empty());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
asm.current_mapping = 1;
@@ -1771,7 +1773,7 @@ adc $Four
#[test]
fn scoped_variable() {
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let mut asm = Assembler::new(empty());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
asm.current_mapping = 1;
@@ -1811,7 +1813,7 @@ adc #Another::Variable
#[test]
fn bare_variables() {
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let mut asm = Assembler::new(empty());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
asm.current_mapping = 1;
@@ -1836,7 +1838,7 @@ adc Variable
#[test]
fn reference_outer_variables() {
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let mut asm = Assembler::new(empty());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
asm.current_mapping = 1;
@@ -1930,7 +1932,7 @@ lda #Scope::Variable
#[test]
fn operations_with_variables() {
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let mut asm = Assembler::new(empty());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
asm.current_mapping = 1;
@@ -1970,7 +1972,7 @@ ldx #~Value
#[test]
fn signed_with_variables() {
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let mut asm = Assembler::new(empty());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
asm.current_mapping = 1;
@@ -2003,7 +2005,7 @@ ldx #+Value
#[test]
fn divide_by_zero() {
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let mut asm = Assembler::new(empty());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
asm.current_mapping = 1;
@@ -2025,7 +2027,7 @@ ldx #(2 / Value)
#[test]
fn bad_shift() {
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let mut asm = Assembler::new(empty());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
asm.current_mapping = 1;
@@ -2343,7 +2345,7 @@ ldx #(2 << Value)
#[test]
fn same_segment_labels() {
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let mut asm = Assembler::new(empty());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
asm.current_mapping = 1;
@@ -2379,7 +2381,7 @@ nop
#[test]
fn anonymous_relative_jumps() {
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let mut asm = Assembler::new(empty());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
asm.current_mapping = 1;
@@ -2448,7 +2450,7 @@ nop
#[test]
fn anonymous_relative_branches() {
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let mut asm = Assembler::new(empty());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
asm.current_mapping = 1;
@@ -2513,7 +2515,7 @@ nop
#[test]
fn conditional_branch_to_labels() {
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let mut asm = Assembler::new(empty());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
asm.current_mapping = 1;
@@ -2547,7 +2549,7 @@ nop
#[test]
fn jsr_to_proc() {
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let mut asm = Assembler::new(empty());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
asm.current_mapping = 1;
@@ -2584,7 +2586,7 @@ nop
#[test]
fn label_in_instruction_addressing() {
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let mut asm = Assembler::new(empty());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
asm.current_mapping = 1;
@@ -2624,7 +2626,7 @@ palettes:
#[test]
fn byte_literals() {
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let mut asm = Assembler::new(empty());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
asm.current_mapping = 1;
@@ -2663,7 +2665,7 @@ palettes:
#[test]
fn hi_lo_byte() {
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let mut asm = Assembler::new(empty());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
asm.current_mapping = 1;
@@ -2695,7 +2697,7 @@ lda #>Var
#[test]
fn macro_no_arguments() {
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let mut asm = Assembler::new(empty());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
asm.current_mapping = 1;
@@ -2728,7 +2730,7 @@ MACRO
#[test]
fn macro_not_enough_arguments() {
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let mut asm = Assembler::new(empty());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
asm.current_mapping = 1;
@@ -2757,7 +2759,7 @@ MACRO
#[test]
fn macro_too_many_arguments() {
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let mut asm = Assembler::new(empty());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
asm.current_mapping = 1;
@@ -2786,7 +2788,7 @@ MACRO(1, 2)
#[test]
fn macro_with_one_argument() {
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let mut asm = Assembler::new(empty());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
asm.current_mapping = 1;
@@ -2819,7 +2821,7 @@ MACRO(2)
#[test]
fn macro_unknown_arguments() {
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let mut asm = Assembler::new(empty());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
asm.current_mapping = 1;
@@ -2849,7 +2851,7 @@ MACRO(1)
#[test]
fn macro_multiple_arguments() {
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let mut asm = Assembler::new(empty());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
asm.current_mapping = 1;
@@ -3132,7 +3134,7 @@ code:
#[test]
fn cannot_switch_to_segment_inside_of_scope() {
- let mut asm = Assembler::new(EMPTY.to_vec());
+ let mut asm = Assembler::new(empty());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
asm.current_mapping = 1;
diff --git a/lib/xixanta/src/mapping.rs b/lib/xixanta/src/mapping.rs
index 8cf752d..a196c8e 100644
--- a/lib/xixanta/src/mapping.rs
+++ b/lib/xixanta/src/mapping.rs
@@ -1,327 +1,11 @@
use crate::errors::EvalError;
use crate::object::Bundle;
+use toml::{Table, Value};
-lazy_static! {
- /// An empty mapper used for testing purposes.
- pub static ref EMPTY: Vec<Mapping> = vec![
- Mapping {
- name: String::from("HEADER"),
- start: 0x0000,
- size: 0x0010,
- offset: 0,
- fill: Some(0x00),
- section_type: SectionType::Header,
- segments: vec![Segment {
- name: String::from("HEADER"),
- len: 0,
- offset: 0,
- bundles: vec![],
- }]
- },
- Mapping {
- name: String::from("ROM0"),
- start: 0x8000,
- size: 0x8000,
- offset: 0,
- fill: None,
- section_type: SectionType::PrgRom,
- segments: vec![Segment {
- name: String::from("CODE"),
- len: 0,
- offset: 0,
- bundles: vec![],
- },]
- },
- ];
-
- // Mapper for a simple NROM setup (e.g. Super Mario Bros).
- pub static ref NROM: Vec<Mapping> = vec![
- Mapping {
- name: String::from("HEADER"),
- start: 0x0000,
- size: 0x0010,
- offset: 0,
- fill: Some(0x00),
- section_type: SectionType::Header,
- segments: vec![Segment {
- name: String::from("HEADER"),
- len: 0,
- offset: 0,
- bundles: vec![],
- }]
- },
- Mapping {
- name: String::from("ROM0"),
- start: 0x8000,
- size: 0x7FFA,
- offset: 0,
- fill: Some(0x00),
- section_type: SectionType::PrgRom,
- segments: vec![Segment {
- name: String::from("CODE"),
- len: 0,
- offset: 0,
- bundles: vec![],
- },]
- },
- Mapping {
- name: String::from("ROMV"),
- start: 0xFFFA,
- size: 0x0006,
- offset: 0,
- fill: Some(0x00),
- section_type: SectionType::PrgRom,
- segments: vec![Segment {
- name: String::from("VECTORS"),
- len: 0,
- offset: 0,
- bundles: vec![],
- },]
- },
- Mapping {
- name: String::from("ROM2"),
- start: 0x0000,
- size: 0x2000,
- offset: 0,
- fill: Some(0x00),
- section_type: SectionType::ChrRom,
- segments: vec![Segment {
- name: String::from("CHARS"),
- len: 0,
- offset: 0,
- bundles: vec![],
- },]
- },
- ];
-
- // Mapper for UxROM boards (UxROM (NES-UNROM, NES-UOROM, HVC-UN1ROM their
- // HVC counterparts, and clone boards).
- pub static ref UXROM: Vec<Mapping> = vec![
- Mapping {
- name: String::from("HEADER"),
- start: 0x0000,
- size: 0x0010,
- offset: 0,
- fill: Some(0x00),
- section_type: SectionType::Header,
- segments: vec![Segment {
- name: String::from("HEADER"),
- len: 0,
- offset: 0,
- bundles: vec![],
- }]
- },
- Mapping {
- name: String::from("PRG0"),
- start: 0x8000,
- size: 0x4000,
- offset: 0,
- fill: Some(0xF8),
- section_type: SectionType::PrgRom,
- segments: vec![
- Segment {
- name: String::from("BANK0"),
- len: 0,
- offset: 0,
- bundles: vec![],
- },
- ]
- },
- Mapping {
- name: String::from("PRG1"),
- start: 0x8000,
- size: 0x4000,
- offset: 0,
- fill: Some(0xF9),
- section_type: SectionType::PrgRom,
- segments: vec![
- Segment {
- name: String::from("BANK1"),
- len: 0,
- offset: 0,
- bundles: vec![],
- },
- ]
- },
- Mapping {
- name: String::from("PRG2"),
- start: 0x8000,
- size: 0x4000,
- offset: 0,
- fill: Some(0xFA),
- section_type: SectionType::PrgRom,
- segments: vec![
- Segment {
- name: String::from("BANK2"),
- len: 0,
- offset: 0,
- bundles: vec![],
- },
- ]
- },
- Mapping {
- name: String::from("PRG3"),
- start: 0x8000,
- size: 0x4000,
- offset: 0,
- fill: Some(0xFB),
- section_type: SectionType::PrgRom,
- segments: vec![
- Segment {
- name: String::from("BANK3"),
- len: 0,
- offset: 0,
- bundles: vec![],
- },
- ]
- },
- Mapping {
- name: String::from("PRG4"),
- start: 0x8000,
- size: 0x4000,
- offset: 0,
- fill: Some(0xFC),
- section_type: SectionType::PrgRom,
- segments: vec![
- Segment {
- name: String::from("BANK4"),
- len: 0,
- offset: 0,
- bundles: vec![],
- },
- ]
- },
- Mapping {
- name: String::from("PRG5"),
- start: 0x8000,
- size: 0x4000,
- offset: 0,
- fill: Some(0xFD),
- section_type: SectionType::PrgRom,
- segments: vec![
- Segment {
- name: String::from("BANK5"),
- len: 0,
- offset: 0,
- bundles: vec![],
- },
- ]
- },
- Mapping {
- name: String::from("PRG6"),
- start: 0x8000,
- size: 0x4000,
- offset: 0,
- fill: Some(0xFE),
- section_type: SectionType::PrgRom,
- segments: vec![
- Segment {
- name: String::from("BANK6"),
- len: 0,
- offset: 0,
- bundles: vec![],
- },
- ]
- },
- Mapping {
- name: String::from("PRG"),
- start: 0xC000,
- size: 0x3FFA,
- offset: 0,
- fill: Some(0xFF),
- section_type: SectionType::PrgRom,
- segments: vec![
- Segment {
- name: String::from("FIXED"),
- len: 0,
- offset: 0,
- bundles: vec![],
- },
- ]
- },
- Mapping {
- name: String::from("ROMV"),
- start: 0xFFFA,
- size: 0x0006,
- offset: 0,
- fill: Some(0x00),
- section_type: SectionType::PrgRom,
- segments: vec![Segment {
- name: String::from("VECTORS"),
- len: 0,
- offset: 0,
- bundles: vec![],
- },]
- },
- ];
-
- pub static ref NROM65: Vec<Mapping> = vec![
- Mapping {
- name: String::from("HEADER"),
- start: 0x0000,
- size: 0x0010,
- offset: 0,
- fill: Some(0x00),
- section_type: SectionType::Header,
- segments: vec![Segment {
- name: String::from("HEADER"),
- len: 0,
- offset: 0,
- bundles: vec![],
- }]
- },
- Mapping {
- name: String::from("ROM0"),
- start: 0x8000,
- size: 0x7FFA,
- offset: 0,
- fill: Some(0x00),
- section_type: SectionType::PrgRom,
- segments: vec![
- Segment {
- name: String::from("STARTUP"),
- len: 0,
- offset: 0,
- bundles: vec![],
- },
- Segment {
- name: String::from("CODE"),
- len: 0,
- offset: 0,
- bundles: vec![],
- },
- ]
- },
- Mapping {
- name: String::from("ROMV"),
- start: 0xFFFA,
- size: 0x0006,
- offset: 0,
- fill: Some(0x00),
- section_type: SectionType::PrgRom,
- segments: vec![Segment {
- name: String::from("VECTORS"),
- len: 0,
- offset: 0,
- bundles: vec![],
- },]
- },
- Mapping {
- name: String::from("ROM2"),
- start: 0x0000,
- size: 0x2000,
- offset: 0,
- fill: Some(0x00),
- section_type: SectionType::ChrRom,
- segments: vec![Segment {
- name: String::from("CHARS"),
- len: 0,
- offset: 0,
- bundles: vec![],
- },]
- },
- ];
-}
+const EMPTY_CONFIG: &str = include_str!("mappings/empty.toml");
+const NROM_CONFIG: &str = include_str!("mappings/nrom.toml");
+const NROM65_CONFIG: &str = include_str!("mappings/nrom65.toml");
+const UXROM_CONFIG: &str = include_str!("mappings/unrom.toml");
/// The type of section that a Mapping represents.
#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)]
@@ -353,6 +37,17 @@ pub struct Segment {
pub bundles: Vec<Bundle>,
}
+impl From<&str> for Segment {
+ fn from(name: &str) -> Self {
+ Segment {
+ name: name.to_string(),
+ offset: 0,
+ len: 0,
+ bundles: vec![],
+ }
+ }
+}
+
impl Segment {
/// Returns the length of the segment by counting the bundles that have been
// pushed so far into the segment.
@@ -401,39 +96,180 @@ pub struct Mapping {
pub section_type: SectionType,
}
-/// Assert that the given mappings conform to a minimum standard.
-pub fn assert(mappings: &[Mapping]) {
- assert!(
- !mappings.is_empty(),
- "We need at least one segment defined, the header"
- );
- assert!(
- !mappings.first().unwrap().segments.is_empty(),
- "We need at least one segment defined, the header"
- );
- assert_eq!(
- mappings.first().unwrap().section_type,
- SectionType::Header,
- "First mapping section must be the header"
- );
- assert_eq!(
- mappings.first().unwrap().size,
- 0x10,
- "The header must be exactly 16 bytes long"
- );
+/// Returns a vector corresponding to the configuration of mappings that is
+/// expected for the given `name`. Returns an error if the configuration cannot
+/// be parsed or there's something wrong about it.
+pub fn get_mapping_configuration(name: &str) -> Result<Vec<Mapping>, String> {
+ let text = match name.to_lowercase().as_str() {
+ "empty" => EMPTY_CONFIG,
+ "nrom" => NROM_CONFIG,
+ "nrom65" => NROM65_CONFIG,
+ "uxrom" | "unrom" => UXROM_CONFIG,
+ _ => return Err("mapper configuration is not known".to_string()),
+ };
+
+ let configuration = load_configuration_for(text)?;
+ validate_configuration(&configuration)?;
+
+ Ok(configuration)
+}
+
+// Returns the integer value for the mandatory integer contained in `value` that
+// is named `prop_name` under the `section_name` section. This integer has to be
+// lesser or equal to the `max` value.
+fn get_integer(
+ section_name: &String,
+ prop_name: &str,
+ value: Option<&Value>,
+ max: usize,
+) -> Result<usize, String> {
+ if value.is_none() {
+ return Err(format!(
+ "you have to define a value for '{}' in '{}'",
+ section_name, prop_name
+ ));
+ }
+ if !value.unwrap().is_integer() {
+ return Err(format!(
+ "value for '{}' in '{}' has to be an integer value",
+ section_name, prop_name
+ ));
+ }
+
+ let val = value.unwrap().as_integer().unwrap() as usize;
+ if val > max {
+ return Err(format!(
+ "value for '{}' in '{}' is too big",
+ prop_name, section_name
+ ));
+ }
+ Ok(val)
+}
+
+// Get a `SectionType` out of the given mandatory `value` which is under the
+// `section_name`.
+fn parse_section_type(section_name: &String, value: Option<&Value>) -> Result<SectionType, String> {
+ match value {
+ Some(v) => {
+ if !v.is_str() {
+ return Err(format!(
+ "'section_type' in '{}' has to be a string",
+ section_name
+ ));
+ }
+ match v.as_str().unwrap().to_lowercase().as_str() {
+ "header" => Ok(SectionType::Header),
+ "prgrom" => Ok(SectionType::PrgRom),
+ "chrrom" => Ok(SectionType::ChrRom),
+ _ => Err(format!(
+ "bad value for 'section_type' in '{}'",
+ section_name
+ )),
+ }
+ }
+ None => Err(format!(
+ "you have to define 'section_type' in '{}'",
+ section_name
+ )),
+ }
+}
+
+// Returns a vector of segments which are contained inside of the mandatory
+// `value`.
+fn get_segments(section_name: &String, value: Option<&Value>) -> Result<Vec<Segment>, String> {
+ if value.is_none() {
+ return Err(format!(
+ "you have to define a value for 'segments' in '{}'",
+ section_name
+ ));
+ }
+ if !value.unwrap().is_array() {
+ return Err(format!(
+ "value for 'segments' in '{}' has to be an array value",
+ section_name
+ ));
+ }
+
+ let mut res = vec![];
+ for item in value.unwrap().as_array().unwrap() {
+ if !item.is_str() {
+ return Err(format!(
+ "every item in 'segments' has to be a string ({})",
+ section_name
+ ));
+ }
+ res.push(Segment::from(item.as_str().unwrap()));
+ }
+
+ Ok(res)
+}
+
+// Returns a vector of mappings that is retrieved by parsing the given text.
+fn load_configuration_for(text: &str) -> Result<Vec<Mapping>, String> {
+ // Obtain the raw data by parsing the given text as a toml::Table.
+ let table = match text.parse::<Table>() {
+ Ok(t) => t,
+ Err(e) => return Err(format!("could not parse configuration file: {}", e)),
+ };
+
+ // Each section of the configuration file is a mapping, where the title is
+ // simply the name of it.
+ let mut mappings = vec![];
+ for (name, value) in table {
+ let start = get_integer(&name, "start", value.get("start"), u16::MAX as usize)? as u16;
+ let size = get_integer(&name, "size", value.get("size"), u16::MAX as usize)?;
+ let fill = match value.get("fill") {
+ Some(_) => Some(get_integer(&name, "fill", value.get("fill"), u8::MAX as usize)? as u8),
+ None => None,
+ };
+ let section_type = parse_section_type(&name, value.get("section_type"))?;
+ let segments = get_segments(&name, value.get("segments"))?;
+
+ mappings.push(Mapping {
+ name,
+ start,
+ size,
+ offset: 0,
+ fill,
+ section_type,
+ segments,
+ });
+ }
+
+ Ok(mappings)
+}
+
+// Ensure that the given mappings conform to a minimum standard.
+fn validate_configuration(mappings: &[Mapping]) -> Result<(), String> {
+ if mappings.is_empty() {
+ return Err("We need at least one segment defined, the header".to_string());
+ }
+ if mappings.first().unwrap().segments.is_empty() {
+ return Err("We need at least one segment defined, the header".to_string());
+ }
+ if mappings.first().unwrap().section_type != SectionType::Header {
+ return Err("First mapping section must be the header".to_string());
+ }
+ if mappings.first().unwrap().size != 0x10 {
+ return Err("The header must be exactly 16 bytes long".to_string());
+ }
let prg_rom_len = mappings
.iter()
.filter(|m| m.section_type == SectionType::PrgRom)
.fold(0, |acc, x| acc + x.size);
- assert!(prg_rom_len >= 0x4000, "PRG ROM must be at least 8KB long");
- assert!(
- prg_rom_len % 0x4000 == 0,
- "PRG ROM must be formed by banks of exactly 8KB"
- );
+
+ if prg_rom_len < 0x4000 {
+ return Err("PRG ROM must be at least 8KB long".to_string());
+ }
+ if prg_rom_len % 0x4000 != 0 {
+ return Err("PRG ROM must be formed by banks of exactly 8KB".to_string());
+ }
+
+ Ok(())
}
-/// Validate some sanity checks on the given `mappings`. Only call this function
+/// Perform some sanity checks on the given `mappings`. Only call this function
/// after all bundles have been produced.
pub fn validate(mappings: &[Mapping]) -> Result<(), EvalError> {
// Guaranteed by `crate::mapping::assert` to be the header.
diff --git a/lib/xixanta/src/mappings/empty.toml b/lib/xixanta/src/mappings/empty.toml
new file mode 100644
index 0000000..ae6312c
--- /dev/null
+++ b/lib/xixanta/src/mappings/empty.toml
@@ -0,0 +1,12 @@
+[HEADER]
+start = 0x0000
+size = 0x0010
+fill = 0x00
+section_type = "Header"
+segments = ["HEADER"]
+
+[ROM0]
+start = 0x8000
+size = 0x8000
+section_type = "PRGROM"
+segments = ["CODE"]
diff --git a/lib/xixanta/src/mappings/nrom.toml b/lib/xixanta/src/mappings/nrom.toml
new file mode 100644
index 0000000..18711a6
--- /dev/null
+++ b/lib/xixanta/src/mappings/nrom.toml
@@ -0,0 +1,27 @@
+[HEADER]
+start = 0x0000
+size = 0x0010
+fill = 0x00
+section_type = "Header"
+segments = ["HEADER"]
+
+[ROM0]
+start = 0x8000
+size = 0x7FFA
+fill = 0x00
+section_type = "PRGROM"
+segments = ["CODE"]
+
+[ROMV]
+start = 0xFFFA
+size = 0x0006
+fill = 0x00
+section_type = "PRGROM"
+segments = ["VECTORS"]
+
+[ROM2]
+start = 0x0000
+size = 0x2000
+fill = 0x00
+section_type = "CHRROM"
+segments = ["CHARS"]
diff --git a/lib/xixanta/src/mappings/nrom65.toml b/lib/xixanta/src/mappings/nrom65.toml
new file mode 100644
index 0000000..d4775e4
--- /dev/null
+++ b/lib/xixanta/src/mappings/nrom65.toml
@@ -0,0 +1,27 @@
+[HEADER]
+start = 0x0000
+size = 0x0010
+fill = 0x00
+section_type = "Header"
+segments = ["HEADER"]
+
+[ROM0]
+start = 0x8000
+size = 0x7FFA
+fill = 0x00
+section_type = "PRGROM"
+segments = ["STARTUP", "CODE"]
+
+[ROMV]
+start = 0xFFFA
+size = 0x0006
+fill = 0x00
+section_type = "PRGROM"
+segments = ["VECTORS"]
+
+[ROM2]
+start = 0x0000
+size = 0x2000
+fill = 0x00
+section_type = "CHRROM"
+segments = ["CHARS"]
diff --git a/lib/xixanta/src/mappings/unrom.toml b/lib/xixanta/src/mappings/unrom.toml
new file mode 100644
index 0000000..070cfd1
--- /dev/null
+++ b/lib/xixanta/src/mappings/unrom.toml
@@ -0,0 +1,69 @@
+[HEADER]
+start = 0x0000
+size = 0x0010
+fill = 0x00
+section_type = "Header"
+segments = ["HEADER"]
+
+[PRG0]
+start = 0x8000
+size = 0x4000
+fill = 0xF8
+section_type = "PRGROM"
+segments = ["BANK0"]
+
+[PRG1]
+start = 0x8000
+size = 0x4000
+fill = 0xF9
+section_type = "PRGROM"
+segments = ["BANK1"]
+
+[PRG2]
+start = 0x8000
+size = 0x4000
+fill = 0xFA
+section_type = "PRGROM"
+segments = ["BANK2"]
+
+[PRG3]
+start = 0x8000
+size = 0x4000
+fill = 0xFB
+section_type = "PRGROM"
+segments = ["BANK3"]
+
+[PRG4]
+start = 0x8000
+size = 0x4000
+fill = 0xFC
+section_type = "PRGROM"
+segments = ["BANK4"]
+
+[PRG5]
+start = 0x8000
+size = 0x4000
+fill = 0xFD
+section_type = "PRGROM"
+segments = ["BANK5"]
+
+[PRG6]
+start = 0x8000
+size = 0x4000
+fill = 0xFE
+section_type = "PRGROM"
+segments = ["BANK6"]
+
+[PRG]
+start = 0xC000
+size = 0x3FFA
+fill = 0xFF
+section_type = "PRGROM"
+segments = ["FIXED"]
+
+[ROMV]
+start = 0xFFFA
+size = 0x0006
+fill = 0x00
+section_type = "PRGROM"
+segments = ["VECTORS"]