aboutsummaryrefslogtreecommitdiff
path: root/lib/xixanta
diff options
context:
space:
mode:
Diffstat (limited to 'lib/xixanta')
-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"]