aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiquel Sabaté Solà <mikisabate@gmail.com>2024-12-20 21:58:26 +0100
committerMiquel Sabaté Solà <mikisabate@gmail.com>2024-12-20 21:58:26 +0100
commit0bc97c99c644955096b3e13a2a7df6b281314e25 (patch)
tree1e09fa92a5ac71df1d097d8581e4538ce7182112
parent0087cc0631ae8e8b1892e674e34a9daa84ad0bb9 (diff)
downloadtools.nes-0bc97c99c644955096b3e13a2a7df6b281314e25.tar.gz
tools.nes-0bc97c99c644955096b3e13a2a7df6b281314e25.zip
Add support for UxROM chips
I have also added an end-to-end test for it. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
-rw-r--r--crates/nasm/src/main.rs5
-rw-r--r--lib/xixanta/src/mapping.rs163
-rwxr-xr-xscripts/test-e2e.sh3
-rw-r--r--tests/bin/chr-ram.nesbin0 -> 131088 bytes
-rw-r--r--tests/src/chr-ram.s279
5 files changed, 446 insertions, 4 deletions
diff --git a/crates/nasm/src/main.rs b/crates/nasm/src/main.rs
index fc5d1c0..0c754e3 100644
--- a/crates/nasm/src/main.rs
+++ b/crates/nasm/src/main.rs
@@ -4,7 +4,7 @@ use std::fs::File;
use std::io::{self, Read, Write};
use std::path::Path;
use xixanta::assembler::Assembler;
-use xixanta::mapping::{Mapping, EMPTY, NROM, NROM65};
+use xixanta::mapping::{Mapping, EMPTY, NROM, NROM65, UXROM};
/// Assembler for the 6502 microprocessor that targets the NES/Famicom.
#[derive(ClapParser, Debug)]
@@ -81,8 +81,9 @@ fn main() -> Result<()> {
"empty" => EMPTY.to_vec(),
"nrom" => NROM.to_vec(),
"nrom65" => NROM65.to_vec(),
+ "uxrom" | "unrom" => UXROM.to_vec(),
_ => {
- println!("Unnown linker configuration '{}'", c);
+ println!("Unknown linker configuration '{}'", c);
std::process::exit(1);
}
},
diff --git a/lib/xixanta/src/mapping.rs b/lib/xixanta/src/mapping.rs
index 2859bbf..8cf752d 100644
--- a/lib/xixanta/src/mapping.rs
+++ b/lib/xixanta/src/mapping.rs
@@ -94,8 +94,167 @@ lazy_static! {
},
];
- // The same mapper as NROM, but it adds a "STARTUP" segment into the "ROM0"
- // mapping so to behave the same as the default "cc65" configuration.
+ // 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"),
diff --git a/scripts/test-e2e.sh b/scripts/test-e2e.sh
index 32bd76f..89e787c 100755
--- a/scripts/test-e2e.sh
+++ b/scripts/test-e2e.sh
@@ -9,3 +9,6 @@ cargo build
./target/debug/nasm -c nrom -Werror -o tests/out/sprite.nes tests/src/sprite.s
diff tests/out/sprite.nes tests/bin/sprite.nes
+
+./target/debug/nasm -c unrom -o tests/out/chr-ram.nes tests/src/chr-ram.s
+diff tests/out/chr-ram.nes tests/bin/chr-ram.nes
diff --git a/tests/bin/chr-ram.nes b/tests/bin/chr-ram.nes
new file mode 100644
index 0000000..5e90cd6
--- /dev/null
+++ b/tests/bin/chr-ram.nes
Binary files differ
diff --git a/tests/src/chr-ram.s b/tests/src/chr-ram.s
new file mode 100644
index 0000000..828217f
--- /dev/null
+++ b/tests/src/chr-ram.s
@@ -0,0 +1,279 @@
+;;; From https://github.com/mssola/NES which will be open sourced soon (pinky
+;;; promess!).
+
+.segment "HEADER"
+ .byte 'N', 'E', 'S', $1A
+ .byte $08 ; 128KB of PRG-ROM (8 x 16KB)
+ .byte $00 ; No CHR-ROM.
+
+ .byte $20, $08 ; Mapper 2, horizontal mirroring, NES 2.0
+
+ .byte $00
+ .byte $00
+ .byte $00
+ .byte $07 ; 8192 (64 * 2^7) bytes CHR RAM, no battery
+
+.segment "VECTORS"
+ .addr nmi, reset, irq
+
+.segment "BANK0"
+
+chr: .incbin "../assets/basic.chr"
+
+;;;
+;; We use the fixed bank for the core functionality.
+
+.segment "FIXED"
+
+;;;
+;; Bank switching. Not actually used here :D
+
+banktable:
+ .byte $00, $01, $02, $03, $04, $05, $06
+
+m_current_bank = $00
+
+bankswitch:
+ sty m_current_bank
+bankswitch_nosave:
+ tya
+ sta banktable, y
+ rts
+
+;;;
+;; From here on the code is basically the same as `sprite.s`, but with a special
+;; twist that will be commented in.
+
+reset:
+ sei
+ cld
+
+ ldx #$40
+ stx $4017
+
+ ldx #$ff
+ txs
+ inx
+ stx $2000
+ stx $2001
+ stx $4010
+
+@vblankwait1:
+ bit $2002
+ bpl @vblankwait1
+
+ ldx #0
+ lda #0
+@ram_reset_loop:
+ sta $000, x
+ sta $100, x
+ sta $300, x
+ sta $400, x
+ sta $500, x
+ sta $600, x
+ sta $700, x
+ inx
+ bne @ram_reset_loop
+
+ lda #$ef
+@sprite_reset_loop:
+ sta $200, x
+ inx
+ bne @sprite_reset_loop
+
+ ;; NOTE: after sprites have been reset we can transfer data from PRG-ROM
+ ;; into RAM so the rest of the code can assume that the data is there. This
+ ;; is the main difference with the `sprite.s` example.
+ jsr transfer_to_chr_ram
+
+ lda #$00
+ sta $2003
+ lda #$02
+ sta $4014
+
+@vblankwait2:
+ bit $2002
+ bpl @vblankwait2
+
+ lda #$3F
+ sta $2006
+ lda #$00
+ sta $2006
+
+ lda #$0F
+ ldx #$20
+@palettes_reset_loop:
+ sta $2007
+ dex
+ bne @palettes_reset_loop
+
+ jmp main
+
+;;;
+;; Transfer the CHR data from PRG-ROM into RAM.
+.proc transfer_to_chr_ram
+ ;;;
+ ;; The code is pretty much taken from the NESDev wiki (see:
+ ;; https://www.nesdev.org/wiki/CHR_ROM_vs._CHR_RAM).
+
+ ;; First we set a 16-bit pointer that points `chr`, which contains the
+ ;; actual data. This pointer will be stored at $00-$01, which is probably
+ ;; not ideal, but this is just an example.
+ lda #<chr
+ sta $00
+ lda #>chr
+ sta $01
+
+ ;; Load the destination address into the PPU. We have to initialize both
+ ;; pattern tables, one starting at $0000, and the other at $1000. Hence we
+ ;; start by simply pointing at PPU address $0000, and the loop will simply
+ ;; fill both tables from here.
+ ldy #0
+ sty $2006
+ sty $2006
+
+ ;; - x contains the number of 256-byte pages to copy.
+ ;; - y will index within the page ($00-$FF).
+ ldx #32
+loop:
+ ;; First part of the loop: copy each byte of the current page.
+ lda ($00), y
+ sta $2007
+ iny
+ bne loop
+
+ ;; Go to the next page and repeat the first part of the loop.
+ inc $01
+ dex
+ bne loop
+
+ rts
+.endproc
+
+;;;
+;; And from here on it's pretty much copied from `sprite.s`.
+
+.proc main
+ jsr init_palettes
+ jsr init_nametable
+ jsr init_sprites
+
+ cli
+ lda #%10110000
+ sta $2000
+ lda #%00011110
+ sta $2001
+
+@main_game_loop:
+ lda #%10000000
+ ora $20
+ sta $20
+@wait_for_render:
+ bit $20
+ bmi @wait_for_render
+
+ jmp @main_game_loop
+.endproc
+
+.proc init_palettes
+ lda #$3F
+ sta $2006
+ lda #$00
+ sta $2006
+
+ ldx #0
+@load_palettes_loop:
+ lda palettes, x
+ sta $2007
+ inx
+ cpx #$20
+ bne @load_palettes_loop
+ rts
+palettes:
+ .byte $0F, $12, $22, $32
+ .byte $0F, $00, $28, $30
+ .byte $0F, $28, $16, $2D
+ .byte $0F, $28, $16, $2D
+
+ .byte $0F, $00, $05, $30
+ .byte $0F, $00, $00, $00
+ .byte $0F, $00, $00, $00
+ .byte $0F, $00, $00, $00
+.endproc
+
+.macro WRITE_PPU_DATA address, value
+ bit $2002
+ lda #.HIBYTE(address)
+ sta $2006
+ lda #.LOBYTE(address)
+ sta $2006
+ lda #value
+ sta $2007
+.endmacro
+
+.proc init_nametable
+ bit $2002
+
+ WRITE_PPU_DATA $20C8, $02
+ WRITE_PPU_DATA $20B9, $04
+ WRITE_PPU_DATA $21CE, $04
+ WRITE_PPU_DATA $21BA, $04
+ WRITE_PPU_DATA $22B8, $04
+ WRITE_PPU_DATA $22E7, $04
+ WRITE_PPU_DATA $227A, $02
+
+ WRITE_PPU_DATA $23CE, %00000001
+
+ rts
+.endproc
+
+.proc init_sprites
+ NUM_SPRITES = 2
+
+ ldx #$00
+@load_sprites_loop:
+ lda initial_sprite_data, x
+ sta $0200, x
+ inx
+ cpx #(4 * NUM_SPRITES)
+ bne @load_sprites_loop
+ rts
+initial_sprite_data:
+ .byte $B0, $00, %00000000, $7A
+ .byte $B0, $00, %01000000, $82
+.endproc
+
+nmi:
+ bit $20
+ bpl @next
+
+ pha
+ txa
+ pha
+ tya
+ pha
+
+ lda #$00
+ sta $2003
+ lda #$02
+ sta $4014
+
+ bit $2002
+ lda #$00
+ sta $2005
+ sta $2005
+
+ lda #%01111111
+ and $20
+ sta $20
+
+ pla
+ tay
+ pla
+ tax
+ pla
+@next:
+ rti
+
+irq:
+ rti