aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorMiquel Sabaté Solà <mssola@mssola.com>2026-07-08 21:13:00 +0200
committerMiquel Sabaté Solà <mssola@mssola.com>2026-07-08 21:27:49 +0200
commit0664f0bad653ca750d320e513f685ab0c852f359 (patch)
tree29c077834217c30f88b669864400cdb9099a26a5 /tests
parent34243afa568ffeb6433200062f0d3c9bc7c2c0ca (diff)
downloadtools.nes-0664f0bad653ca750d320e513f685ab0c852f359.tar.gz
tools.nes-0664f0bad653ca750d320e513f685ab0c852f359.zip
Add a warning for unknown cross-mapping references
Some segments, like the 'vectors' one, will reference code that is outside of its mapping. But in some other configurations, segments cannot make these cross-mapping references so happily. Imagine: .segment "SWAPPABLE" .proc foo rts .endproc .segment "FIXED" jsr foo Here the assembler will properly detect the address of 'foo' in the context of the 'SWAPPABLE' segment. But what this assembler doesn't know is that this segment is swappable (e.g. UNROM chip). Hence, if the bank being mapped right now is not the one containing the 'SWAPPABLE' segment, then the address computed for 'foo' and used in that 'jsr' instruction will point to something else entirely. This would be similar to a use-after-free bug. This is something that can only be inspected at runtime, and so the assembler cannot be of much help here. Hence, this commit adds a warning so the programmer can understand the potentially dangerous operation. All of that being said, this commit also adds support for "asan:safe" or "check:safe", which is a magic comment that the programmer can write to re-assure the assembler that this operation is fine (e.g. there is a guarantee that the mapped bank is that one we are expecting). Hence, the code above could now be written like so: jsr foo ; check:safe Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
Diffstat (limited to 'tests')
-rw-r--r--tests/expected/unrom.nesbin0 -> 131088 bytes
-rw-r--r--tests/expected/unrom.txt1
-rw-r--r--tests/unrom.s129
3 files changed, 130 insertions, 0 deletions
diff --git a/tests/expected/unrom.nes b/tests/expected/unrom.nes
new file mode 100644
index 0000000..06959e0
--- /dev/null
+++ b/tests/expected/unrom.nes
Binary files differ
diff --git a/tests/expected/unrom.txt b/tests/expected/unrom.txt
new file mode 100644
index 0000000..6ce1cd9
--- /dev/null
+++ b/tests/expected/unrom.txt
@@ -0,0 +1 @@
+warning: referencing 'hello_bank0' from 'FIXED', which belongs to the 'BANK0' segment (unrom.s: line 108)
diff --git a/tests/unrom.s b/tests/unrom.s
new file mode 100644
index 0000000..3761029
--- /dev/null
+++ b/tests/unrom.s
@@ -0,0 +1,129 @@
+.segment "HEADER"
+ .byte 'N', 'E', 'S', $1A
+ .byte $08 ; 128KB of PRG-ROM (8 x 16KB)
+ .byte $00 ; No CHR-ROM. Games using this chip used RAM instead.
+
+ ;; On the 6th byte we have to set the lower nibble of the mapper (#%0010 for
+ ;; UNROM).
+ .byte $20
+
+ ;; Forcing iNES 2.0 format, which will help us for the next bytes.
+ .byte $08
+
+ ;; And now iNES 2.0-specific thingies.
+ .byte $00 ; No submapper
+ .byte $00 ; PRG ROM not 4 MiB or larger
+ .byte $00 ; No PRG RAM
+ .byte $07 ; 8192 (64 * 2^7) bytes CHR RAM, no battery
+ .byte $00 ; NTSC; use $01 for PAL
+ .byte $00 ; No special PPU
+
+.segment "VECTORS"
+ .addr nmi, reset, irq
+
+.segment "BANK0"
+
+hello_bank0:
+ lda #2
+ sta $10
+ rts
+
+.segment "BANK1"
+
+hello_bank1:
+ lda #3
+ sta $11
+ rts
+
+.segment "BANK2"
+.byte $00
+
+.segment "BANK3"
+.byte $00
+
+.segment "BANK4"
+.byte $00
+
+.segment "BANK5"
+.byte $00
+
+.segment "BANK6"
+.byte $00
+
+.segment "FIXED"
+
+banktable:
+ .byte $00, $01, $02, $03, $04, $05, $06
+
+zp_current_bank = $00
+
+bankswitch:
+ sty zp_current_bank
+ tya
+ sta banktable, y
+ rts
+
+.proc reset
+ sei
+ cld
+ ldx #$40
+ stx $4017
+
+ ldx #$FF
+ txs
+
+ inx
+ stx $2000
+ stx $2001
+ stx $4010
+
+ bit $2002
+@vblankwait1:
+ bit $2002
+ bpl @vblankwait1
+
+ ldx #0
+ lda #0
+@ram_reset_loop:
+ sta $000, x
+ sta $100, x
+ sta $200, x
+ sta $300, x
+ sta $400, x
+ sta $500, x
+ sta $600, x
+ sta $700, x
+ inx
+ bne @ram_reset_loop
+
+@vblankwait2:
+ bit $2002
+ bpl @vblankwait2
+
+ ;;;
+ ;; NOTE: configuration/reset is done, the code below is our actual program :D
+
+ ldy #0
+ jsr bankswitch
+ jsr hello_bank0
+
+ ldy #1
+ jsr bankswitch
+ jsr hello_bank1 ; check:safe
+
+ lda $10
+ clc
+ adc $11
+ sta $12
+
+@loop:
+ jmp @loop
+.endproc
+
+.proc nmi
+ rti
+.endproc
+
+.proc irq
+ rti
+.endproc