aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiquel Sabaté Solà <mikisabate@gmail.com>2025-02-04 15:46:54 +0100
committerMiquel Sabaté Solà <mikisabate@gmail.com>2025-02-04 22:00:36 +0100
commit3e7549c71f60ee1c4b49a7c09fde3b6017818089 (patch)
treedc132aecc12b2ba2680edca912fa12778618ad02
parent6c4ac7727094259b3b5a25e8c168245983e70b2f (diff)
downloadcode.nes-3e7549c71f60ee1c4b49a7c09fde3b6017818089.tar.gz
code.nes-3e7549c71f60ee1c4b49a7c09fde3b6017818089.zip
Ensure background/sprite tiles are spread properly
At least on the MMC3 chip, it's important to have background tiles on the first pattern table and then sprite tiles on the second pattern table. This avoids a hardware bug on scanline IRQs. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
-rw-r--r--README.md2
-rw-r--r--assets/diskun.chrbin8192 -> 8192 bytes
-rw-r--r--basics/flicker.s2
-rw-r--r--fx/blink.s56
-rw-r--r--scroll/roulette.s17
5 files changed, 50 insertions, 27 deletions
diff --git a/README.md b/README.md
index 42302a5..50bb8dc 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ check out [this list](#other-projects).
You can build everything by just calling `make` and binaries will then be
available in the `out` directory. Before doing that, though, you will need a
-compiler for the 6052 platform. A good option is
+compiler for the 6502 platform. A good option is
[cc65](https://github.com/cc65/cc65), which is available on all major platforms,
and if you are feeling adventurous you can check out
[nasm](https://github.com/mssola/tools.nes). By default the Makefile uses `cc65`
diff --git a/assets/diskun.chr b/assets/diskun.chr
index 3ed0dc4..fe48e96 100644
--- a/assets/diskun.chr
+++ b/assets/diskun.chr
Binary files differ
diff --git a/basics/flicker.s b/basics/flicker.s
index 25f1d33..234c8a1 100644
--- a/basics/flicker.s
+++ b/basics/flicker.s
@@ -27,7 +27,7 @@
jsr init_sprites
cli
- lda #%10010000
+ lda #%10001000
sta $2000 ; PPUCTRL
lda #%00011110
sta $2001 ; PPUMASK
diff --git a/fx/blink.s b/fx/blink.s
index adf8553..4fdaa6b 100644
--- a/fx/blink.s
+++ b/fx/blink.s
@@ -183,8 +183,9 @@ reset:
;; span 4 banks (that is, the first pattern table on the PPU). We have to
;; account for that when writing into the first two registers (and that's
;; why they are set to 0 and 2 respectively). Other than that, R2-R5 hold
- ;; which bank goes into the other remaining regions which are 1KB each.
- ;; Because of this, we can simply assign one 1KB bank to each register.
+ ;; which bank goes into the other remaining regions which are 1KB each
+ ;; (hence, the second pattern table is controlled via R2-R5). Because of
+ ;; this, we can simply assign one 1KB bank to each register.
;;
;; To sum things up, on this setup the first pattern table is equally
;; divided by 2 and it's controlled by R0 and R1 respectively. The second
@@ -246,12 +247,28 @@ reset:
;;; NOTE: mainly as usual except that a bit of game loop has been added to
;;; handle the blinking state.
.proc main
+ ;; The code will iterate between banks 4 and 6 on the pattern table, as
+ ;; that's where sprites are located (check the CHARS segment for more info).
+ lda #4
+ sta Vars::last_bank
+
jsr Diskun::init_palettes
jsr init_sprites
cli
- lda #%10010000
+
+ ;; NOTE: This looks like other examples, but here having background tiles on
+ ;; the first pattern table and sprite tiles on the second pattern table is
+ ;; not a matter of personal taste, but there are technical reasons for it.
+ ;; If you check how IRQs work on the MMC3 chip:
+ ;; https://www.nesdev.org/wiki/MMC3#IRQ_Specifics; you will find that on 8x8
+ ;; tile mode having this arrangement actually spares us from a hardware bug.
+ ;; Long story short, placing background tiles first and sprite tiles second
+ ;; make scanline IRQs reliable. Hence, at least on all MMC3 examples, this
+ ;; will be guaranteed.
+ lda #%10001000
sta $2000
+
lda #%00011110
sta $2001
@@ -277,17 +294,19 @@ reset:
lda #0
sta Vars::counter
- ;; The whole trick is done on R0. So select it.
- lda #0
+ ;; The whole trick is done on R2, which points to $1000, where the sprite
+ ;; tiles begin. Hence, select it.
+ lda #2
sta MMC3::BANK_SELECT
- ;; The value for the register is either 0 or 2 depending on its last value.
+ ;; The value for the register is either 4 or 6 depending on its last value.
lda Vars::last_bank
+ cmp #4
beq :+
- lda #0
- beq @set
+ lda #4
+ jmp @set
:
- lda #2
+ lda #6
@set:
;; Save which is the bank being used both internally and onto the MMC3 chip.
sta Vars::last_bank
@@ -369,14 +388,21 @@ irq:
;;; too much for this example, but it's a reasonable size for an MMC3 game.
;;; Considering only one pair of pattern tables are available at any given
;;; moment (8KB), this means that we need space for 128 / 8 = 16 pairs of
-;;; pattern tables here. For this example the first 8KB are realy only used, and
-;;; the rest are left with a default value ($00).
+;;; pattern tables here. For this example the first 8KB are really only used,
+;;; and the rest are left with a default value ($00).
.segment "CHARS"
-;; First 8KB (note that diskun0.chr and diskun1.chr are both 2KB long)
-.incbin "../assets/diskun0.chr" ; First half of the first pattern table has the default diskun character.
-.incbin "../assets/diskun1.chr" ; Second half of the first pattern table simply has the blinking version.
-.res $1000, $00 ; Second pattern table is left with the default value.
+;; As explained when initializing the PPUCTRL register ($2000), it's actually
+;; important to place background tiles first and sprite tiles second on the
+;; MMC3. This is guaranteed here by setting the first pattern table as empty (we
+;; have no background on this example, really). The second pattern table is then
+;; filled with `diskun0.chr` and `diskun1.chr`, which are 2KB each. Hence, the
+;; 4th CHR bank contains the regular character, and the 6th CHR bank contains
+;; the blinking version. You can see these values used when initializing
+;; `Vars::last_bank`, or when performing bank switching.
+.res $1000, $00
+.incbin "../assets/diskun0.chr" ; First half of the second pattern table has the default diskun character.
+.incbin "../assets/diskun1.chr" ; Second half of the second pattern table simply has the blinking version.
;; The 15 other 8KB portions are left empty.
.res $2000, $00
diff --git a/scroll/roulette.s b/scroll/roulette.s
index ecf2569..4b13c22 100644
--- a/scroll/roulette.s
+++ b/scroll/roulette.s
@@ -1,6 +1,6 @@
;;;
;; Divide the screen in three rows and make them move in different
-;; directions/speed. This is achieved thanks to the MMC3 chip (check the
+;; directions/speed. This is achieved thanks to the MMC3 chip (check
;; `fx/blink.s` for further information on this chip). In particular, we are
;; using the scanline IRQ mechanism provided by this chip to react to different
;; parts of the screen being rendered:
@@ -208,7 +208,7 @@ reset:
;; NOTE: enable back interrupts so we can set them up later on `nmi` code.
cli
- lda #%10010000
+ lda #%10001000
sta $2000
lda #%00011110
sta $2001
@@ -386,9 +386,9 @@ nmi:
ldx #$00
stx MMC3::IRQ_DISABLE
- ;; The screen is made up of 240 scan lines. Since we are dividing the screen
- ;; by 3: 240 / 3 = 80. Hence, the next IRQ should happen on scanline 80,
- ;; where we would need to update the scroll value through the
+ ;; The screen is made up of 240 visible scan lines. Since we are dividing
+ ;; the screen by 3: 240 / 3 = 80. Hence, the next IRQ should happen on
+ ;; scanline 80, where we would need to update the scroll value through the
;; `{center/bottom}_scroll` values instead. Moreover, note that
;; `MMC3::IRQ_ENABLE` accepts any value, so the same value as the two other
;; registers is just fine.
@@ -493,15 +493,12 @@ irq:
rti
-;;; NOTE: pretty much the same as `fx/blink.s` but we also copy the same data on
-;;; the second pattern table as that's the one being used for background
-;;; elements.
+;;; NOTE: pretty much the same as `fx/blink.s`.
.segment "CHARS"
.incbin "../assets/diskun0.chr"
.incbin "../assets/diskun1.chr"
-.incbin "../assets/diskun0.chr"
-.incbin "../assets/diskun1.chr"
+.res $1000, $00
;; The 15 other 8KB portions are left empty.
.res $2000, $00