aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiquel Sabaté Solà <mssola@mssola.com>2026-09-06 22:55:50 +0200
committerMiquel Sabaté Solà <mssola@mssola.com>2026-09-06 22:55:50 +0200
commit2d61e42fab657d61212a7aff1ecacbd674be8cda (patch)
treef8064b630e1caa4b6e329ab1dcc72b14f0f5c8d5
parent076b1b31f37b37560382545d424ee063903b6b31 (diff)
downloadtools.nes-2d61e42fab657d61212a7aff1ecacbd674be8cda.tar.gz
tools.nes-2d61e42fab657d61212a7aff1ecacbd674be8cda.zip
tests: add a test on branching instructions
Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
-rwxr-xr-xscripts/test-e2e.sh6
-rw-r--r--tests/expected/runrom/branching.txt71
-rw-r--r--tests/runrom/branching.s128
3 files changed, 205 insertions, 0 deletions
diff --git a/scripts/test-e2e.sh b/scripts/test-e2e.sh
index be43685..573a729 100755
--- a/scripts/test-e2e.sh
+++ b/scripts/test-e2e.sh
@@ -294,6 +294,12 @@ echo "test: runrom => cmp.nes"
diff tests/out/cmp.txt tests/expected/runrom/cmp.txt
exit_code=$((exit_code + $?))
+echo "test: runrom => branching.nes"
+./target/debug/nasm -Werror --asan -o tests/out/branching.nes tests/runrom/branching.s
+./target/debug/runrom --function --dump-memory tests/out/branching.nes > tests/out/branching.txt
+diff tests/out/branching.txt tests/expected/runrom/branching.txt
+exit_code=$((exit_code + $?))
+
echo "test: runrom => misc.nes"
./target/debug/nasm -Werror --asan --write-info --allow-unused --out tests/out/misc.nes tests/runrom/misc.s
./target/debug/runrom --nasm .nasm --dump-memory --function tests/out/misc.nes > tests/out/misc-all.txt
diff --git a/tests/expected/runrom/branching.txt b/tests/expected/runrom/branching.txt
new file mode 100644
index 0000000..8c2eb7f
--- /dev/null
+++ b/tests/expected/runrom/branching.txt
@@ -0,0 +1,71 @@
+<start> PC: $8000, cycles: 7, registers: [a: $00, x: $00, y: $00, sp: $FD], status: ----I--
+ldx #$0A PC: $8002, cycles: 9, registers: [a: $00, x: $0A, y: $00, sp: $FD], status: ----I--
+ldy #$05 PC: $8004, cycles: 11, registers: [a: $00, x: $0A, y: $05, sp: $FD], status: ----I--
+lda $030C, y PC: $8007, cycles: 15, registers: [a: $00, x: $0A, y: $05, sp: $FD], status: ----IZ-
+cmp $0300, x PC: $800A, cycles: 19, registers: [a: $00, x: $0A, y: $05, sp: $FD], status: ----IZC
+bcc $1F PC: $800C, cycles: 21, registers: [a: $00, x: $0A, y: $05, sp: $FD], status: ----IZC
+dex PC: $800D, cycles: 23, registers: [a: $00, x: $09, y: $05, sp: $FD], status: ----I-C
+dex PC: $800E, cycles: 25, registers: [a: $00, x: $08, y: $05, sp: $FD], status: ----I-C
+dey PC: $800F, cycles: 27, registers: [a: $00, x: $08, y: $04, sp: $FD], status: ----I-C
+cpy #$FF PC: $8011, cycles: 29, registers: [a: $00, x: $08, y: $04, sp: $FD], status: ----I--
+bne $F1 PC: $8004, cycles: 32, registers: [a: $00, x: $08, y: $04, sp: $FD], status: ----I--
+lda $030C, y PC: $8007, cycles: 36, registers: [a: $00, x: $08, y: $04, sp: $FD], status: ----IZ-
+cmp $0300, x PC: $800A, cycles: 40, registers: [a: $00, x: $08, y: $04, sp: $FD], status: ----IZC
+bcc $1F PC: $800C, cycles: 42, registers: [a: $00, x: $08, y: $04, sp: $FD], status: ----IZC
+dex PC: $800D, cycles: 44, registers: [a: $00, x: $07, y: $04, sp: $FD], status: ----I-C
+dex PC: $800E, cycles: 46, registers: [a: $00, x: $06, y: $04, sp: $FD], status: ----I-C
+dey PC: $800F, cycles: 48, registers: [a: $00, x: $06, y: $03, sp: $FD], status: ----I-C
+cpy #$FF PC: $8011, cycles: 50, registers: [a: $00, x: $06, y: $03, sp: $FD], status: ----I--
+bne $F1 PC: $8004, cycles: 53, registers: [a: $00, x: $06, y: $03, sp: $FD], status: ----I--
+lda $030C, y PC: $8007, cycles: 57, registers: [a: $00, x: $06, y: $03, sp: $FD], status: ----IZ-
+cmp $0300, x PC: $800A, cycles: 61, registers: [a: $00, x: $06, y: $03, sp: $FD], status: ----IZC
+bcc $1F PC: $800C, cycles: 63, registers: [a: $00, x: $06, y: $03, sp: $FD], status: ----IZC
+dex PC: $800D, cycles: 65, registers: [a: $00, x: $05, y: $03, sp: $FD], status: ----I-C
+dex PC: $800E, cycles: 67, registers: [a: $00, x: $04, y: $03, sp: $FD], status: ----I-C
+dey PC: $800F, cycles: 69, registers: [a: $00, x: $04, y: $02, sp: $FD], status: ----I-C
+cpy #$FF PC: $8011, cycles: 71, registers: [a: $00, x: $04, y: $02, sp: $FD], status: ----I--
+bne $F1 PC: $8004, cycles: 74, registers: [a: $00, x: $04, y: $02, sp: $FD], status: ----I--
+lda $030C, y PC: $8007, cycles: 78, registers: [a: $00, x: $04, y: $02, sp: $FD], status: ----IZ-
+cmp $0300, x PC: $800A, cycles: 82, registers: [a: $00, x: $04, y: $02, sp: $FD], status: ----IZC
+bcc $1F PC: $800C, cycles: 84, registers: [a: $00, x: $04, y: $02, sp: $FD], status: ----IZC
+dex PC: $800D, cycles: 86, registers: [a: $00, x: $03, y: $02, sp: $FD], status: ----I-C
+dex PC: $800E, cycles: 88, registers: [a: $00, x: $02, y: $02, sp: $FD], status: ----I-C
+dey PC: $800F, cycles: 90, registers: [a: $00, x: $02, y: $01, sp: $FD], status: ----I-C
+cpy #$FF PC: $8011, cycles: 92, registers: [a: $00, x: $02, y: $01, sp: $FD], status: ----I--
+bne $F1 PC: $8004, cycles: 95, registers: [a: $00, x: $02, y: $01, sp: $FD], status: ----I--
+lda $030C, y PC: $8007, cycles: 99, registers: [a: $00, x: $02, y: $01, sp: $FD], status: ----IZ-
+cmp $0300, x PC: $800A, cycles: 103, registers: [a: $00, x: $02, y: $01, sp: $FD], status: ----IZC
+bcc $1F PC: $800C, cycles: 105, registers: [a: $00, x: $02, y: $01, sp: $FD], status: ----IZC
+dex PC: $800D, cycles: 107, registers: [a: $00, x: $01, y: $01, sp: $FD], status: ----I-C
+dex PC: $800E, cycles: 109, registers: [a: $00, x: $00, y: $01, sp: $FD], status: ----IZC
+dey PC: $800F, cycles: 111, registers: [a: $00, x: $00, y: $00, sp: $FD], status: ----IZC
+cpy #$FF PC: $8011, cycles: 113, registers: [a: $00, x: $00, y: $00, sp: $FD], status: ----I--
+bne $F1 PC: $8004, cycles: 116, registers: [a: $00, x: $00, y: $00, sp: $FD], status: ----I--
+lda $030C, y PC: $8007, cycles: 120, registers: [a: $00, x: $00, y: $00, sp: $FD], status: ----IZ-
+cmp $0300, x PC: $800A, cycles: 124, registers: [a: $00, x: $00, y: $00, sp: $FD], status: ----IZC
+bcc $1F PC: $800C, cycles: 126, registers: [a: $00, x: $00, y: $00, sp: $FD], status: ----IZC
+dex PC: $800D, cycles: 128, registers: [a: $00, x: $FF, y: $00, sp: $FD], status: ----I-C
+dex PC: $800E, cycles: 130, registers: [a: $00, x: $FE, y: $00, sp: $FD], status: N---I-C
+dey PC: $800F, cycles: 132, registers: [a: $00, x: $FE, y: $FF, sp: $FD], status: ----I-C
+cpy #$FF PC: $8011, cycles: 134, registers: [a: $00, x: $FE, y: $FF, sp: $FD], status: ----IZC
+bne $F1 PC: $8013, cycles: 136, registers: [a: $00, x: $FE, y: $FF, sp: $FD], status: ----IZC
+bit $27 PC: $8015, cycles: 139, registers: [a: $00, x: $FE, y: $FF, sp: $FD], status: ----IZC
+bpl $2F PC: $8046, cycles: 142, registers: [a: $00, x: $FE, y: $FF, sp: $FD], status: ----IZC
+rts PC: $8047, cycles: 148, registers: [a: $00, x: $FE, y: $FF, sp: $FD], status: ----IZC
+<end>
+
+== Memory dump ==
+
+[$27] = $00 [reads=1, writes=0]
+[$300] = $00 [reads=1, writes=0]
+[$302] = $00 [reads=1, writes=0]
+[$304] = $00 [reads=1, writes=0]
+[$306] = $00 [reads=1, writes=0]
+[$308] = $00 [reads=1, writes=0]
+[$30A] = $00 [reads=1, writes=0]
+[$30C] = $00 [reads=1, writes=0]
+[$30D] = $00 [reads=1, writes=0]
+[$30E] = $00 [reads=1, writes=0]
+[$30F] = $00 [reads=1, writes=0]
+[$310] = $00 [reads=1, writes=0]
+[$311] = $00 [reads=1, writes=0]
diff --git a/tests/runrom/branching.s b/tests/runrom/branching.s
new file mode 100644
index 0000000..a96888c
--- /dev/null
+++ b/tests/runrom/branching.s
@@ -0,0 +1,128 @@
+.segment "HEADER"
+ .byte 'N', 'E', 'S', $1A
+ .byte $02, $01
+ .byte $00
+ .byte $00
+
+.segment "CHARS"
+.byte 0
+
+.segment "VECTORS"
+ .addr reset, reset, reset
+
+.segment "CODE"
+;; asan:stack full
+
+;; From: https://git.mssola.com/nes/jetpac.nes/.
+.scope Globals
+ ;; | Bit | Short name | Meaning |
+ ;; |-----+------------------+--------------------------|
+ ;; | 7 | enabled | Multiplayer enabled. |
+ ;; | 6-3 | - | Unused. |
+ ;; | 2 | player's 2 state | 0: over; 1: alive |
+ ;; | 1 | player's 1 state | 0: over; 1: alive |
+ ;; | 0 | active | 0: player 1; 1: player 2 |
+ zp_multiplayer = $27
+
+ ;; Extra bitmap that was needed beyond the ones that we already have. Yeah,
+ ;; I know, bad planning from my side, but now it's a bit complex to untangle
+ ;; variables like 'Globals::zp_flags'.
+ ;;
+ ;; | Bit | Short name | Meaning |
+ ;; |-----+------------------+----------------------------------|
+ ;; | 7 | score | The score has been updated. |
+ ;; | 6 | high | The high score has been updated. |
+ ;; | 5-0 | - | Unused. |
+ zp_extra_flags = $28
+.endscope
+
+;; From: https://git.mssola.com/nes/jetpac.nes/.
+.scope Score
+ ;; Scores for both players are stored in a single buffer. Even indeces
+ ;; contain digits for the first player, and odd indeces contain digits for
+ ;; the second player. Digits are stored in little-endian format.
+ ;;
+ ;; Interweaving digits this way might seem weird, but it actually makes
+ ;; indexing things super easy: the 'active' bit from
+ ;; 'Globals::zp_multiplayer' can be used to index the first item, and then
+ ;; it's a matter of advancing the 'x' register twice in order to get the
+ ;; next digit.
+ PLAYERS_BUFF_SIZE = $0C
+ m_players = $300 ; asan:reserve PLAYERS_BUFF_SIZE
+
+ ;; The high score for this session.
+ m_hi = $30C ; asan:reserve $06
+.endscope
+
+;; This is basically Score::save_hi_score() from
+;; https://git.mssola.com/nes/jetpac.nes/.
+.proc reset
+ ;;;
+ ;; Check player 1.
+
+ ldx #(Score::PLAYERS_BUFF_SIZE - 2)
+ ldy #5
+
+@player1_loop:
+ lda Score::m_hi, y
+ cmp Score::m_players, x
+ bcc @save_player1
+ dex
+ dex
+ dey
+ cpy #$FF
+ bne @player1_loop
+
+ ;; No dice! If we are in multiplayer mode, then check player 2,
+ ;; otherwise just quit.
+ bit Globals::zp_multiplayer
+ bpl @end
+
+ ;;;
+ ;; Check player 2.
+
+ ldx #(Score::PLAYERS_BUFF_SIZE - 1)
+ ldy #5
+
+@player2_loop:
+ lda Score::m_hi, y
+ cmp Score::m_players, x
+ bcc @save_player2
+ dex
+ dex
+ dey
+ cpy #$FF
+ bne @player2_loop
+
+ ;; Not player 2 either. Just quit.
+ rts
+
+ ;;;
+ ;; One of the players actually achieved a high score. Let's save it.
+
+@save_player1:
+ ldx #0
+ beq @save
+@save_player2:
+ ldx #1
+
+@save:
+ ldy #0
+@save_loop:
+ lda Score::m_players, x
+ sta Score::m_hi, y
+
+ inx
+ inx
+ iny
+ cpy #6
+ bne @save_loop
+
+ ;; And set the 'high' bit so this change is reflected on screen.
+ lda Globals::zp_extra_flags
+ ora #$40
+ sta Globals::zp_extra_flags
+
+@end:
+ rts
+.endproc