diff options
| author | Miquel Sabaté Solà <msabate@suse.com> | 2023-11-07 14:14:55 +0100 |
|---|---|---|
| committer | Miquel Sabaté Solà <mikisabate@gmail.com> | 2025-02-04 21:02:07 +0100 |
| commit | cb854722b9890c171f7d0600812a7e9a3a2a25c9 (patch) | |
| tree | 1d9bce62db538471b1c98970115999b8ff93b43e /basics | |
| parent | 988332816972638adfa8000dd7e1a474c5fea424 (diff) | |
| download | code.nes-cb854722b9890c171f7d0600812a7e9a3a2a25c9.tar.gz code.nes-cb854722b9890c171f7d0600812a7e9a3a2a25c9.zip | |
basics: Add an example on UNROM
Signed-off-by: Miquel Sabaté Solà <msabate@suse.com>
Diffstat (limited to 'basics')
| -rw-r--r-- | basics/README.md | 1 | ||||
| -rw-r--r-- | basics/persist.s | 4 | ||||
| -rw-r--r-- | basics/unrom.s | 171 |
3 files changed, 176 insertions, 0 deletions
diff --git a/basics/README.md b/basics/README.md index 7571509..3e4c3a9 100644 --- a/basics/README.md +++ b/basics/README.md @@ -13,3 +13,4 @@ Anyhow, these programs are as follows: to get a detailed explanation on each section. - `input.s`: how to read the input from one controller. - `persist.s`: using the MMC1 chip in order to persist data. +- `unrom.s`: bank switching using the UNROM chip. diff --git a/basics/persist.s b/basics/persist.s index 392797f..430654f 100644 --- a/basics/persist.s +++ b/basics/persist.s @@ -11,6 +11,10 @@ ;; Last but not least, all of this is done thanks to the MMC1 chip. Thus, this ;; file is also a minimalistic example on how to configure it, even if it ;; doesn't take full advantage of it (e.g. we are not doing any bank switching). +;; Because of this, note that we are not using a proper linker configuration for +;; it (I'm using cc65's default, which is tailored for NROM chips). Thus, bank +;; switching will actually never work under this setup. Bank switching is +;; covered through other examples like `basics/unrom.s`. .segment "HEADER" .byte 'N', 'E', 'S', $1A diff --git a/basics/unrom.s b/basics/unrom.s new file mode 100644 index 0000000..c6e9fd8 --- /dev/null +++ b/basics/unrom.s @@ -0,0 +1,171 @@ +;;; +;; This is an example on how you can perform bank switching. In order to do this +;; I am using the UNROM chip (iNES mapper 2), which is one of the first chips to +;; ever support this. Because of this, it's also one of the simplest, which is +;; quite convenient in order to learn this technique. +;; +;; The program will do the following: +;; - Switch to bank 0. +;; - Call a function from bank 0 that will set $10 to #2. +;; - Switch to bank 1. +;; - Call a function from bank 1 that will set $11 to #3. +;; - Sum the values from $10 and $11 and store them in $12. +;; +;; At this point there are two things you can do in order to check that the +;; whole thing worked: +;; +;; 1. The values on $10, $11 and $12 are as expected (2, 3, and 5 +;; respectively). +;; 2. The last bank we switched was 1, which has a fill value on linker +;; configuration (see `config/unrom.cfg`) of `$f9`. Thus, check that except +;; from the code on `hello_bank1`, the rest of the values on $8000-$BFFF +;; are set to `$f9` (i.e. by using FCEUX's Debug -> Hex editor). + +.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 + +;;; +;; Note that we now define code on bank 0 and bank 1. These names are not magic: +;; they are defined on the linker configuration being used (see +;; `config/unrom.cfg`). All swappable banks are called `BANK#`. The bank that is +;; not swappable is simply called `FIXED`, and that's the bank where most of the +;; code from this example will reside. + +.segment "BANK0" + +hello_bank0: + lda #2 + sta $10 + rts + +.segment "BANK1" + +hello_bank1: + lda #3 + sta $11 + rts + +;;; +;; The rest of the banks are simply not used by this example. + +.segment "BANK2" +.segment "BANK3" +.segment "BANK4" +.segment "BANK5" +.segment "BANK6" + +;;; +;; And the following code is "fixed". That is, it is stored at $C000-$FFFF, +;; which is not swappable. You can check this by using FCEUX's hex editor, for +;; example. + +.segment "FIXED" + +;; This table contains the actual sauce of bank switching. In general, the UNROM +;; does bank switching by writing to $8000-$BFFF, but the value we are writing +;; there *must* match the value located at the destination address in ROM +;; (otherwise we get a bus conflict). Writing to this table already ensures that +;; this never happens because the indexing will match the actual value. +banktable: + .byte $00, $01, $02, $03, $04, $05, $06 + +;; Variable containing the bank we are currently in. It's useful to keep track +;; of the bank so the NMI handler can restore it if it does some bank switching +;; of its own. +m_current_bank = $00 + +;; Perform a bankswitch by using the value on the `y` register. Note that you +;; can use the `bankswitch_nosave` variant, which is useful if you just want to +;; perform a temporary bankswitch (e.g. on NMI code). +bankswitch: + sty m_current_bank +bankswitch_nosave: + tya + sta banktable, y + rts + +;; Unused +nmi: +irq: + rti + +;; Check `basics/sprite.s` for a deeper look on the logic below. I have only +;; added code after configuration/reset is done. +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 $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 + + ;; Switch to bank 0 and call the function from there. + ldy #0 + jsr bankswitch + jsr hello_bank0 + + ;; The same but on bank 1. + ldy #1 + jsr bankswitch + jsr hello_bank1 + + ;; And now we can add the numbers that were saved by both banks. + lda $10 + clc + adc $11 + sta $12 + + ;; Loop forever, there's nothing to be done here. +@loop: + jmp @loop |
