Mesen Bugged Debugger??

Mihoshi20

Member
Spending a couple days playing around and researching the Object Attribute Memory portion of the PPU I started to run into a strange issue and after taking the last couple days of testing it either comes down to a bug in Mesen's PPU memory viewer or a function of the NES I'm not aware of yet.

The following picture shows the platformer demo in both the Mesen and FCEUX emulators with the PPU VRAM editors open. FCEUX shows expected operation while Mesen shows the discrepancy.

[A]PPU_RAM_Issue.png


The following is the controller example from the Nerdy Nights Week 5 code converted to ASM6 that I was experimenting with when I first discovered the discrepancy. In the code you can clearly see and in the debugger step through the code and see the writes to 3F00 and up and the correct results appear in the PPU palette viewer but never appear in the VRAM.

Code:
; NintendoAge Nerdy Nights Week 5 - Controller

; modified to assemble with ASM6 (and possibly other) assemblers

; away with those .ines directives, this assembler will use a header instead

byte "NES",$1a                          ; basically "NES" plus a terminator
byte $01                                ; 1x16 PRG-ROM block ($c000)
byte $01                                ; 1 CHR-ROM block
byte $00                                ; dontcare
byte $00                                ; dontcare
dsb 8                                   ; 8 bytes padding

;;;;;;;;;;;;;;;

; away with .bank directives
  .org $C000 
RESET:
  SEI          ; disable IRQs
  CLD          ; disable decimal mode
  LDX #$40
  STX $4017    ; disable APU frame IRQ
  LDX #$FF
  TXS          ; Set up stack
  INX          ; now X = 0
  STX $2000    ; disable NMI
  STX $2001    ; disable rendering
  STX $4010    ; disable DMC IRQs

vblankwait1:       ; First wait for vblank to make sure PPU is ready
  BIT $2002
  BPL vblankwait1

clrmem:
  LDA #$00
  STA $0000, x
  STA $0100, x
  STA $0200, x
  STA $0400, x
  STA $0500, x
  STA $0600, x
  STA $0700, x
  LDA #$FE
  STA $0300, x
  INX
  BNE clrmem
   
vblankwait2:      ; Second wait for vblank, PPU is ready after this
  BIT $2002
  BPL vblankwait2

LoadPalettes:
  LDA $2002             ; read PPU status to reset the high/low latch
  LDA #$3F
  STA $2006             ; write the high byte of $3F00 address
  LDA #$00
  STA $2006             ; write the low byte of $3F00 address
  LDX #$00              ; start out at 0
LoadPalettesLoop:
  LDA palette, x        ; load data from address (palette + the value in x)
                          ; 1st time through loop it will load palette+0
                          ; 2nd time through loop it will load palette+1
                          ; 3rd time through loop it will load palette+2
                          ; etc
  STA $2007             ; write to PPU
  INX                   ; X = X + 1
  CPX #$20              ; Compare X to hex $10, decimal 16 - copying 16 bytes = 4 sprites
  BNE LoadPalettesLoop  ; Branch to LoadPalettesLoop if compare was Not Equal to zero
                        ; if compare was equal to 32, keep going down

LoadSprites:
  LDX #$00              ; start at 0
LoadSpritesLoop:
  LDA sprites, x        ; load data from address (sprites +  x)
  STA $0200, x          ; store into RAM address ($0200 + x)
  INX                   ; X = X + 1
  CPX #$20              ; Compare X to hex $20, decimal 32
  BNE LoadSpritesLoop   ; Branch to LoadSpritesLoop if compare was Not Equal to zero
                        ; if compare was equal to 32, keep going down
              
  LDA #%10000000   ; enable NMI, sprites from Pattern Table 1
  STA $2000

  LDA #%00010000   ; enable sprites
  STA $2001

Forever:
  JMP Forever     ;jump back to Forever, infinite loop
  
NMI:
  LDA #$00
  STA $2003       ; set the low byte (00) of the RAM address
  LDA #$02
  STA $4014       ; set the high byte (02) of the RAM address, start the transfer

LatchController:
  LDA #$01
  STA $4016
  LDA #$00
  STA $4016       ; tell both the controllers to latch buttons

ReadA: 
  LDA $4016       ; player 1 - A
  AND #%00000001  ; only look at bit 0
  BEQ ReadADone   ; branch to ReadADone if button is NOT pressed (0)
                  ; add instructions here to do something when button IS pressed (1)
  LDA $0203       ; load sprite X position
  CLC             ; make sure the carry flag is clear
  ADC #$01        ; A = A + 1
  STA $0203       ; save sprite X position
ReadADone:        ; handling this button is done

ReadB: 
  LDA $4016       ; player 1 - B
  AND #%00000001  ; only look at bit 0
  BEQ ReadBDone   ; branch to ReadBDone if button is NOT pressed (0)
                  ; add instructions here to do something when button IS pressed (1)
  LDA $0203       ; load sprite X position
  SEC             ; make sure carry flag is set
  SBC #$01        ; A = A - 1
  STA $0203       ; save sprite X position
ReadBDone:        ; handling this button is done
  
  RTI             ; return from interrupt
;;;;;;;;;;;;;;  

  .org $E000
palette:
  .db $0F,$31,$32,$33,$34,$35,$36,$37,$38,$39,$3A,$3B,$3C,$3D,$3E,$0F
  .db $0F,$1C,$15,$14,$31,$02,$38,$3C,$0F,$1C,$15,$14,$31,$02,$38,$3C

sprites:
     ;vert tile attr horiz
  .db $80, $32, $00, $80   ;sprite 0
  .db $80, $33, $00, $88   ;sprite 1
  .db $88, $34, $00, $80   ;sprite 2
  .db $88, $35, $00, $88   ;sprite 3

  .org $FFFA     ;first of the three vectors starts here
  .dw NMI        ;when an NMI happens (once per frame if enabled) the 
                   ;processor will jump to the label NMI:
  .dw RESET      ;when the processor first turns on or is reset, it will jump
                   ;to the label RESET:
  .dw 0          ;external interrupt IRQ is not used in this tutorial
  
;;;;;;;;;;;;;;  

  .incbin "mario.chr"   ;includes 8KB graphics file from SMB1
 

Kasumi

New member
I forget the specifics of why this is, but there's a quirk to how palettes are represented in the PPU memory. Mesen, thus, has another option in the pulldown to see the palette memory called palette RAM.

Edit: NovaTheSquirrel and Tepples set me straight on this six months ago, I found the message in discord. Apparently palettes are stored elsewhere even if they are mapped to $3F00. So you can read from the values that are ACTUALLY at PPU $3F00 (read: Not the palette), but not write there.

Here's a screenshot from those actually in the know:

zOyI0uc.png


I prefer FCEUX's approach development-wise, but I don't consider either approach right or wrong, really.
 

Mihoshi20

Member
Kasumi said:
I forget the specifics of why this is, but there's a quirk to how palettes are represented in the PPU memory. Mesen, thus, has another option in the pulldown to see the palette memory called palette RAM.

Edit: NovaTheSquirrel and Tepples set me straight on this six months ago, I found the message in discord. Apparently palettes are stored elsewhere even if they are mapped to $3F00. So you can read from the values that are ACTUALLY at PPU $3F00 (read: Not the palette), but not write there.

Here's a screenshot from those actually in the know:

zOyI0uc.png


I prefer FCEUX's approach development-wise, but I don't consider either approach right or wrong, really.

Good to see you're still with us Kasumi and chiming in on this! Yeah, Mesen has options for OAM RAM and Palette RAM in the drop down, but they always show up as blank, not even as a cluster of 00's so I don't believe those features are implemented yet. I knew something had to be up though as testing UNROM games I noticed other data would be placed there sometimes that wasn't palette data so either I was missing something or it was pulling data from somewhere else in error, but with NROM it would work fine so it had me baffled! Searching for the palette group in both PPU and CPU ram space would only find it in ROM and never RAM so where it is actually placed and sourced from remains a mystery as far as Mesen goes. But I greatly appreciate some info on this big time!

I really enjoy the debugger in Mesen, it's very feature rich especially when it come to stepping through code and manipulating the CPU, but I believe that FCEUX's layout is better in many other ways. I like the PPU and Nametable viewer in FCEUX better along with the trace logger.

Though this doesn't answer why in the nerdy nights code the palette is directly placed into $3F00 and up yet Mesen never shows it being there. Unless this is something the hardware does on its own as I noticed most of the time the stack pointer will be at FF but the moment an NMI starts it will switch on its own to FC.
 

Kasumi

New member
The Palette RAM should not show up blank. What Mesen version/OS/Etc.?
vJhRIwo.png

That's 0.9.7 on Windows Vista. It was definitely also working during the date in those images, since those are responses to the same question. (Why doesn't Mesen show the palette at $3F00 in the PPU memory?)

If you directly place any number in $0A00 on the CPU side, you can read that same number back from $0200 and vice versa. Where is that value considered to be?

Provided my understanding of what they've said is correct, the palette isn't stored at $3F00 in PPU memory, it is simply accessed through that address. It's similar to how in UNROM or Mapper 30 different parts of the ROM can be accessed from the same address on the CPU side. $8000-$BFFF can be multiple 16KB segments of the ROM over the course of program execution. $8000-$BFFF is not necessarily "where it is", just where the NES sees it.

Similarly, $3F00 is not where the palette is, it's just where it's accessed from. The steps PinoBatch shares are how to read what's "actually" at $3F00, but you can only read. When you write, the writes get "redirected" to where the palette is, rather than the "actual" memory at $3F00 on the PPU side. And that probably sounds really weird, but lots of things on NES are like that. It's sort of like how writes to ROM get "redirected" to the mapper hardware for bank swapping.

Granted, it's weird that the palette is someplace else and the rest of the PPU memory seems not to be. This post offers an explanation on why: https://forums.nesdev.com/viewtopic.php?p=208786#p208786 (Note: Tepples = PinoBatch)

As for why the stack pointer changes the moment the NMI starts, consider that the NMI can happen while the code is doing anything.
How does the CPU know where to return to?
Suppose the carry is set. The NMI clears the carry, then returns. Couldn't that potentially break the code when the NMI does return?

So what happens is that whenever an interrupt (like the NMI, or the IRQ) interrupts, where the address the CPU is currently at (the Program Counter, or PC, two bytes) and the state of the processor status flags (one byte) are pushed onto the stack. (The stack is RAM at $0100-$01FF.)

So a byte at $01FF, a byte at $01FE, a byte at $01FD. The next byte is $01FC, where the stack pointer ends up. (The high byte, $01, is implied by any stack usage.)

When you return from the NMI with an RTI, it pulls those bytes off the stack (copying them back to the PC and Processor Status Byte) to safely return. BUT NOTE! A/X/Y are NOT put on the stack when the interrupt hits. If you don't save the current value of A, X and Y yourself at the start of the NMI, then restore them at the end of the NMI when the NMI returns there is absolutely potential for code to get broken. (Imagine a loop that can only end if X is a multiple of four, like dex dex dex dex bne. If the NMI changed X to something that was not a multiple of 4, the loop would never break)

The stack is also used on JSR and RTS. JSR pushes where to return to, RTS pulls those values to get back. That's why subroutines can be called from any location. This is also how subroutines can call other subroutines (and fundamentally why the concept of a stack exists as it does). (The below is slightly simplified for understanding, the exact details matter less for learning.)
Code:
main:
Jsr sub1;Address of main (really the address following the line of code that contains the jsr) is put onto the stack and we go to sub1
jmp main

sub1:
jsr sub2;Address of sub1 is put onto the stack "on top of" the address of main and we go to sub2
rts


sub2:
rts;The "top" two bytes are pulled off the stack. In this case, the address after the jsr sub2. The PC is set to this address, and the program resumes. Now the "top" two bytes of the stack are the address after jsr sub1.

;There's an RTS after jsr sub2. So the top two bytes are pulled off the stack again. The address after jsr sub1, which is jmp main.
You can have subroutines call subroutines up to 128 levels deep, in theory. New things are put on top. You can only pull the top things off, so to get the bottom all the top things have to be removed in order.

It's like if you had a real stack of plates. When you put a plate onto a stack, you put it on top. That's easier than putting it into the middle or the bottom. If you wanted to take one off, you'd take the top one because that's also easier.

6502's stack grows "downwards" because $01FF is the "top" and $0100 is the "bottom". But that makes it easy to use contiguous "upwards" growing RAM starting at $0100. (But if you use too much the stack might crash into it.)

Try it! Look at $0100-$01FF when the first NMI hits in your program. See it change whenever you JSR. RTS and RTI don't change the RAM, just the index (the stack pointer). Because there is an index, old values don't have to get "erased" on return. (So note that you'll only see the NMI change in particular if the NMI's address wasn't the last thing pushed there already. That said, the flags that are also pushed are likely to be different.)
 

Mihoshi20

Member
Kasumi said:
The Palette RAM should not show up blank. What Mesen version/OS/Etc.?
vJhRIwo.png

That's 0.9.7 on Windows Vista. It was definitely also working during the date in those images, since those are responses to the same question. (Why doesn't Mesen show the palette at $3F00 in the PPU memory?)

If you directly place any number in $0A00 on the CPU side, you can read that same number back from $0200 and vice versa. Where is that value considered to be?

Provided my understanding of what they've said is correct, the palette isn't stored at $3F00 in PPU memory, it is simply accessed through that address. It's similar to how in UNROM or Mapper 30 different parts of the ROM can be accessed from the same address on the CPU side. $8000-$BFFF can be multiple 16KB segments of the ROM over the course of program execution. $8000-$BFFF is not necessarily "where it is", just where the NES sees it.

Similarly, $3F00 is not where the palette is, it's just where it's accessed from. The steps PinoBatch shares are how to read what's "actually" at $3F00, but you can only read. When you write, the writes get "redirected" to where the palette is, rather than the "actual" memory at $3F00 on the PPU side. And that probably sounds really weird, but lots of things on NES are like that. It's sort of like how writes to ROM get "redirected" to the mapper hardware for bank swapping.

Granted, it's weird that the palette is someplace else and the rest of the PPU memory seems not to be. This post offers an explanation on why: https://forums.nesdev.com/viewtopic.php?p=208786#p208786 (Note: Tepples = PinoBatch)

As for why the stack pointer changes the moment the NMI starts, consider that the NMI can happen while the code is doing anything.
How does the CPU know where to return to?
Suppose the carry is set. The NMI clears the carry, then returns. Couldn't that potentially break the code when the NMI does return?

So what happens is that whenever an interrupt (like the NMI, or the IRQ) interrupts, where the address the CPU is currently at (the Program Counter, or PC, two bytes) and the state of the processor status flags (one byte) are pushed onto the stack. (The stack is RAM at $0100-$01FF.)

So a byte at $01FF, a byte at $01FE, a byte at $01FD. The next byte is $01FC, where the stack pointer ends up. (The high byte, $01, is implied by any stack usage.)

When you return from the NMI with an RTI, it pulls those bytes off the stack (copying them back to the PC and Processor Status Byte) to safely return. BUT NOTE! A/X/Y are NOT put on the stack when the interrupt hits. If you don't save the current value of A, X and Y yourself at the start of the NMI, then restore them at the end of the NMI when the NMI returns there is absolutely potential for code to get broken. (Imagine a loop that can only end if X is a multiple of four, like dex dex dex dex bne. If the NMI changed X to something that was not a multiple of 4, the loop would never break)

The stack is also used on JSR and RTS. JSR pushes where to return to, RTS pulls those values to get back. That's why subroutines can be called from any location. This is also how subroutines can call other subroutines (and fundamentally why the concept of a stack exists as it does). (The below is slightly simplified for understanding, the exact details matter less for learning.)
Code:
main:
Jsr sub1;Address of main (really the address following the line of code that contains the jsr) is put onto the stack and we go to sub1
jmp main

sub1:
jsr sub2;Address of sub1 is put onto the stack "on top of" the address of main and we go to sub2
rts


sub2:
rts;The "top" two bytes are pulled off the stack. In this case, the address after the jsr sub2. The PC is set to this address, and the program resumes. Now the "top" two bytes of the stack are the address after jsr sub1.

;There's an RTS after jsr sub2. So the top two bytes are pulled off the stack again. The address after jsr sub1, which is jmp main.
You can have subroutines call subroutines up to 128 levels deep, in theory. New things are put on top. You can only pull the top things off, so to get the bottom all the top things have to be removed in order.

It's like if you had a real stack of plates. When you put a plate onto a stack, you put it on top. That's easier than putting it into the middle or the bottom. If you wanted to take one off, you'd take the top one because that's also easier.

6502's stack grows "downwards" because $01FF is the "top" and $0100 is the "bottom". But that makes it easy to use contiguous "upwards" growing RAM starting at $0100. (But if you use too much the stack might crash into it.)

Try it! Look at $0100-$01FF when the first NMI hits in your program. See it change whenever you JSR. RTS and RTI don't change the RAM, just the index (the stack pointer). Because there is an index, old values don't have to get "erased" on return. (So note that you'll only see the NMI change in particular if the NMI's address wasn't the last thing pushed there already. That said, the flags that are also pushed are likely to be different.)

I'm using Mesen version 0.9.7 on Windows 7 64-bit and the Palette RAM viewer is blank for me.

[A]Mesen097_Screenshot.png

The mirror ram locations I'm not actually too concerned with as when I was looking at the CPU and PPU RAM when I was using FCEUX before switching to Mesen I could still see all the values, if I place something into $0A00, so long as it's not a read only location I expect the value to be there at least, let alone in mirrored locations.

But I believe what you're saying is that the $3F00 is basically read only, more like a reference area and that the palette is actually written elsewhere. The NES is quite the quirky machine. Just yesterday I found out what OAM stood for and that it was a completely separate memory space from VRAM when I always though it was an I/O register. The more I learn about the hardware, the more a lot of mysteries fall into place.

It's great to have you with us an tolerating us noobs as a lot of time I copy and paste you posts to a text file and refer back to it later and this post will be one of them as there's a lot of good info in here and also solves the stack pointer NMI mystery I had.

Update 11-18-2018: I found the issue with the values not showing up. Apparently CHR RAM, Palette RAM, Sprite/OAM RAM, and Secondary OAM RAM portions of the Memory viewer use the 3D Objects font color from the Windows theme while other portions seem to use a built in color scheme and in my current theme the 3D Objects font color is set to white.
 

Kasumi

New member
I think Sour (Mesen dev) is on vacation right now, but definitely report that. It's weird that only those sections don't display rather than the entire memory viewer not displaying, I'm super curious why, heh. Edit: Oh, I saw your update. That makes sense, and even more information to make the report better!

Yes, that's a good summary of the palette stuff.

I want to write NES tutorials, but not enough to actually do it. There's so much weird NES stuff. Like writes to $2006 affect some of the same things that writes to the scroll position ($2005) do, so you have to write the scroll position after all $2006 writes. The 2 frame PPU warm up wait. Sprites being drawn one pixel lower than you set. (Really! It's impossible for sprites to occupy the top pixel of the screen. Position 0 will be drawn 1 pixel below the top and you always write the position you actually want minus one.) DPCM sample playback messing with the joypad... start of frame signals getting missed if you use $2002 (Not a huge effect, but can happen), reads from $2007 generally requiring an extra throwaway read (which is the behavior that allows reading from "actual" $3F00), palette colors getting rendered to the screen if rendering is off and the address written to $2006 is in the palette range...

But once you hit them, you learn!
 

Mihoshi20

Member
Kasumi said:
I think Sour (Mesen dev) is on vacation right now, but definitely report that. It's weird that only those sections don't display rather than the entire memory viewer not displaying, I'm super curious why, heh. Edit: Oh, I saw your update. That makes sense, and even more information to make the report better!

Yes, that's a good summary of the palette stuff.

I want to write NES tutorials, but not enough to actually do it. There's so much weird NES stuff. Like writes to $2006 affect some of the same things that writes to the scroll position ($2005) do, so you have to write the scroll position after all $2006 writes. The 2 frame PPU warm up wait. Sprites being drawn one pixel lower than you set. (Really! It's impossible for sprites to occupy the top pixel of the screen. Position 0 will be drawn 1 pixel below the top and you always write the position you actually want minus one.) DPCM sample playback messing with the joypad... start of frame signals getting missed if you use $2002 (Not a huge effect, but can happen), reads from $2007 generally requiring an extra throwaway read (which is the behavior that allows reading from "actual" $3F00), palette colors getting rendered to the screen if rendering is off and the address written to $2006 is in the palette range...

But once you hit them, you learn!

It doesn't seem to easily change the text color for the default 'black' like it is to change all the highlight colors, it would be nice if it obeyed windows themeing too for high contrast sets. The backdrop for the memory viewer and debugger code window I'd rather not be white when I use a custom windows theme. NESmaker had that issue too in the script editor but will thankfully be fixed in the latest release (Presumed version is 4.1.0)

It would be nice if someone wrote an actual technical and programming manual for the NES as I'm discovering the NES Dev wiki to be very contradictory or sometimes vague almost as if it's out of date against the latest discoveries about the hardware.

I've learned so much from both your posts and others, using NESmaker and studying its engine code and assembled games, along with my own experiments and then reading through the publicly available technical documents especially when something doesn't behave as I expect or I have an ah-ha moment and learn something new that puts another puzzle piece in place, though the more I learn the deeper the rabbit hole goes it seems. It's like no other system I've ever developed for before and that's exactly the challenge I was looking for.

It has been quite the adventure the whole way so far.
 

Kasumi

New member
The wiki can be a little dense, but it's primarily raw documentation. If you have questions about something NES-hardware specific you can ask here and I'll probably answer. And maybe I can edit the wiki too if you list specific issues with it, but it's a fine line to walk for the perfect amount of information.
 

Mihoshi20

Member
Kasumi said:
The wiki can be a little dense, but it's primarily raw documentation. If you have questions about something NES-hardware specific you can ask here and I'll probably answer. And maybe I can edit the wiki too if you list specific issues with it, but it's a fine line to walk for the perfect amount of information.

Okay, cool, I'll start cataloging portions of the wiki I feel are lacking information or wording I feel is vague or confusing. Mostly recently since our last talk now that I have a clearer view of what's actually going on with the palette and have all portions of the mesen memory viewer actually displaying as it should, I've been running experiments and pooling over what schematics I can find for the famicom and NES-001 and haven't needed to look over the NESdev wiki again yet.

I thank you deeply for being here as our lighthouse as many of us set out on our NESveleopment adventures. Phase 2 of my adventure will be trying to discover the the troll burner animated tiles as it doesn't appear to be a palette/color cycle but actual tile replacement.
 

Kasumi

New member
Animated tiles are an easy question to answer outside the NES Maker context.

The benefit of not writing the engine: You don't have to the write the engine!
The con of not writing the engine: You have to be REALLY careful adding things to it, and do a lot of study to make sure the additions won't break it. You also have to research how things are set up to know where to make the additions.

So animated tiles. The NES has tiles in either ROM or RAM depending on the configuration of the cartridge. Mapper 30 is always RAM, a lot of others can be either or.

Assuming you have the tiles in RAM, you update them the same way as the palette. You write the address of the tile you want to update in two writes to $2006. Then you write the data itself to $2007. You can only update for 2270 cycles after the NMI triggers, or when rendering is turned off. Just like the palette!

$0000-$1FFF is the address range for tiles. Each tile is 16 bytes. The format for those 16 bytes is on this page: https://wiki.nesdev.com/w/index.php/PPU_pattern_tables

Suppose you're using $0000-$0FFF for your background tiles. This means $0000-$000F are all of the bytes of the first tile in your background set.
Code:
;Make sure the address increment bit for $2000 is go up by 1 rather than 32
lda #$00
sta $2006
sta $2006
ldy #$0F
tileloop:
sta $2007
dey
bpl tileloop
BAM, you've made the first tile entirely blank.

Mapper 30 has 32KB of RAM for tiles, but a whole set of 512 tiles is only 8KB. So it has four full sets. The NES can still only see one set, but you can change which set is in its vision at any time. The sets are commonly called banks. Mapper 30 also lets you swap in new banks to the CPU, which gives you access to more data that isn't tiles.

To do both, you write a byte to $8000. It's really that simple! And the format for that byte is on this page: https://wiki.nesdev.com/w/index.php/UNROM_512
But I'll quote it here:
Code:
  Range,Mask:   $8000-FFFF, $8000
  
    $8000:  [MCCP PPPP]
      M = One screen Mirroring select
      C = CHR RAM bank
      P = PRG ROM bank
  
  PRG Setup:
  ---------------------------
       $8000   $A000   $C000   $E000  
      +-------------------------------+
      |     $8000     |     { -1}     |
      +---------------+---------------+
So the lowest five bits are which PRG ROM bank you want (32 possible 16KB banks = 512KB total)
The two higher than that are which of the 4 sets of tiles Mapper 30 has the NES will use to display. Confused? No worries! Example time.
Code:
lda #%00000000;For now, let's assume we want PRG bank 0 and don't care about mirroring
sta $8000;Now CHR bank 0 and PRG bank 0 are what the NES is looking at.

;Make sure the address increment bit for $2000 is go up by 1 rather than 32
lda #$00
sta $2006
sta $2006
ldy #$0F
tileloop:
sta $2007
dey
bpl tileloop
;We have just made the first tile of the first (well, 0th) bank totally blank.

;Now we put in a new set, and we can make the first tile of that bank something else.

lda #%00100000;For now, let's assume we want PRG bank 0 and don't care about mirroring
sta $8000;Now CHR bank 1 and PRG bank 0 are what the NES is looking at.

lda #$00
sta $2006
sta $2006

lda #%11111111;This will be a tile that is filled with color 3, rather than color zero
ldy #$0F
tileloop2:
sta $2007
dey
bpl tileloop2

;Even though we have written to the exact same address, something different got changed because we put something new in NES' vision.
So in bank 0, tile 0 is entirely color 0.
In bank 1, tile 0 is entirely color 3.

To make it animate, in your NMI, you'd just do:
Code:
lda currentbank
eor #%00100000;We only have two frames with the above code, so we can just eor one bit to alternate frames
sta currentbank
sta $8000
And then every frame, anywhere tile 0 is used will alternate between being color 3 and color 0 every frame.

Short, review version:
Turn rendering off (write a zero to $2001) so you can do a lot of updates through $2006/2007. Write all sprite and background tiles to all 4 CHR RAM banks. Now you have four identical tileset "frames" set up.
Then, any tile index you want to be animated, you write the 16 bytes for the different versions of it to the appropriate banks.

Then you can turn rendering on, and all you have to do change frames is write a new CHR bank to $8000 in your NMI. You can ALSO write entirely new tiles there through $2006/$2007 but when rendering is on you have a VERY limited number of updates you can do in a frame before it starts to become unsafe. With $8000, you can change all 512 tiles multiple times per frame.

Edit: Just to be maximum clear, the write doesn't necessarily have to be to $8000. It can be anywhere from $8000-$FFFF. Occasionally for bank swapping the value stored at the location you write to has to be the same byte as what you write to ensure you get the right result (due to "Bus Conflicts"), but NES Maker's configuration I believe is one where this does not matter. (i.e. Mapper 30 is subject to Bus Conflicts for non self flashable boards of it, but NES Maker's carts are likely to be self flashable since saving is possible.)

Edit2: Troll Burner stores which CHR bank in $06D0 if you want to look at it with a debugger. The value is updated with either #%00000000, #%00100000, #%01000000, or #%01100000. Then the bank swap subroutine loads the PRG bank (a value with a range of #$00-#$1F) from $0045, and does ORA with $06D0 to get the right animation frame (CHR bank) in the proper bits. Then it stores the result to $C000. So if you freeze $06D0, the animation will stop.
 

Mihoshi20

Member
Kasumi said:
Animated tiles are an easy question to answer outside the NES Maker context.

The benefit of not writing the engine: You don't have to the write the engine!
The con of not writing the engine: You have to be REALLY careful adding things to it, and do a lot of study to make sure the additions won't break it. You also have to research how things are set up to know where to make the additions.

So animated tiles. The NES has tiles in either ROM or RAM depending on the configuration of the cartridge. Mapper 30 is always RAM, a lot of others can be either or.

Assuming you have the tiles in RAM, you update them the same way as the palette. You write the address of the tile you want to update in two writes to $2006. Then you write the data itself to $2007. You can only update for 2270 cycles after the NMI triggers, or when rendering is turned off. Just like the palette!

$0000-$1FFF is the address range for tiles. Each tile is 16 bytes. The format for those 16 bytes is on this page: https://wiki.nesdev.com/w/index.php/PPU_pattern_tables

Suppose you're using $0000-$0FFF for your background tiles. This means $0000-$000F are all of the bytes of the first tile in your background set.
Code:
;Make sure the address increment bit for $2000 is go up by 1 rather than 32
lda #$00
sta $2006
sta $2006
ldy #$0F
tileloop:
sta $2007
dey
bpl tileloop
BAM, you've made the first tile entirely blank.

Mapper 30 has 32KB of RAM for tiles, but a whole set of 512 tiles is only 8KB. So it has four full sets. The NES can still only see one set, but you can change which set is in its vision at any time. The sets are commonly called banks. Mapper 30 also lets you swap in new banks to the CPU, which gives you access to more data that isn't tiles.

To do both, you write a byte to $8000. It's really that simple! And the format for that byte is on this page: https://wiki.nesdev.com/w/index.php/UNROM_512
But I'll quote it here:
Code:
  Range,Mask:   $8000-FFFF, $8000
  
    $8000:  [MCCP PPPP]
      M = One screen Mirroring select
      C = CHR RAM bank
      P = PRG ROM bank
  
  PRG Setup:
  ---------------------------
       $8000   $A000   $C000   $E000  
      +-------------------------------+
      |     $8000     |     { -1}     |
      +---------------+---------------+
So the lowest five bits are which PRG ROM bank you want (32 possible 16KB banks = 512KB total)
The two higher than that are which of the 4 sets of tiles Mapper 30 has the NES will use to display. Confused? No worries! Example time.
Code:
lda #%00000000;For now, let's assume we want PRG bank 0 and don't care about mirroring
sta $8000;Now CHR bank 0 and PRG bank 0 are what the NES is looking at.

;Make sure the address increment bit for $2000 is go up by 1 rather than 32
lda #$00
sta $2006
sta $2006
ldy #$0F
tileloop:
sta $2007
dey
bpl tileloop
;We have just made the first tile of the first (well, 0th) bank totally blank.

;Now we put in a new set, and we can make the first tile of that bank something else.

lda #%00100000;For now, let's assume we want PRG bank 0 and don't care about mirroring
sta $8000;Now CHR bank 1 and PRG bank 0 are what the NES is looking at.

lda #$00
sta $2006
sta $2006

lda #%11111111;This will be a tile that is filled with color 3, rather than color zero
ldy #$0F
tileloop2:
sta $2007
dey
bpl tileloop2

;Even though we have written to the exact same address, something different got changed because we put something new in NES' vision.
So in bank 0, tile 0 is entirely color 0.
In bank 1, tile 0 is entirely color 3.

To make it animate, in your NMI, you'd just do:
Code:
lda currentbank
eor #%00100000;We only have two frames with the above code, so we can just eor one bit to alternate frames
sta currentbank
sta $8000
And then every frame, anywhere tile 0 is used will alternate between being color 3 and color 0 every frame.

Short, review version:
Turn rendering off (write a zero to $2001) so you can do a lot of updates through $2006/2007. Write all sprite and background tiles to all 4 CHR RAM banks. Now you have four identical tileset "frames" set up.
Then, any tile index you want to be animated, you write the 16 bytes for the different versions of it to the appropriate banks.

Then you can turn rendering on, and all you have to do change frames is write a new CHR bank to $8000 in your NMI. You can ALSO write entirely new tiles there through $2006/$2007 but when rendering is on you have a VERY limited number of updates you can do in a frame before it starts to become unsafe. With $8000, you can change all 512 tiles multiple times per frame.

Edit: Just to be maximum clear, the write doesn't necessarily have to be to $8000. It can be anywhere from $8000-$FFFF. Occasionally for bank swapping the value stored at the location you write to has to be the same byte as what you write to ensure you get the right result (due to "Bus Conflicts"), but NES Maker's configuration I believe is one where this does not matter. (i.e. Mapper 30 is subject to Bus Conflicts for non self flashable boards of it, but NES Maker's carts are likely to be self flashable since saving is possible.)

Edit2: Troll Burner stores which CHR bank in $06D0 if you want to look at it with a debugger. The value is updated with either #%00000000, #%00100000, #%01000000, or #%01100000. Then the bank swap subroutine loads the PRG bank (a value with a range of #$00-#$1F) from $0045, and does ORA with $06D0 to get the right animation frame (CHR bank) in the proper bits. Then it stores the result to $C000. So if you freeze $06D0, the animation will stop.

This is really going to be fun to experiment and play around with and yeah, I become all to familiar with the dreaded Value out of Range when trying to add new tile types and a palette/color cycle to my current game. i put everything on hold until 4.1.0/5.0's release and I port the game over. I'd been using the down time to take a deeper look at the hardware and experiment with it to get a better understanding of why it does what it does in hopes that it will help me in the long run makes adjustments not only to the NESmaker engine but also significant modifications for future projects as my goal is to make games for not only the home consoles but the NES variant arcade hardware as well.
 

Mihoshi20

Member
I didn't get very far in my experiment as I hit a few snags. I'm used to experimenting only with NROM and this is my first time outside NESmaker raw coding pure ASM. I'm assuming currentbank is a variable I need to declare and assign a value before-hand and then load a palette and other init work before this code is useable. I'm also assuming I need an RTI at the end of the NMI code or should I label jump it back to the main code? Would it be possible to create a simple assemble-able example for study that demonstrates the concepts being talked about?
 

Kasumi

New member
There aren't many cartridge types that allow animated tiles in exactly the way Mapper 30 does.

You can use RAM instead of ROM for tiles with NROM (mapper 0), but it doesn't have bank switching. (The writes to $8000 I mentioned. That's specifically how mapper 30's bank switching works.)
You can use CNROM to try bank switching, but it doesn't have CHR RAM. The way bank switching is done is also slightly different from Mapper 30. https://wiki.nesdev.com/w/index.php/CNROM

Yes, currentbank would be a byte in RAM. You would initialize all NES things, then before you turn rendering back on after initialization you would write the tiles you want. Then turn the screen on and enable NMIs.

You must absolutely use RTI at the end of the NMI code. For two reasons:
1. Remember when you noticed when the NMI hit, the stack pointer went to $FC? RTI makes it go back to $FF. If you jmp back, the next frame will move the stack pointer to $F9. It will keep going down. The NMI happening at all puts plates on the stack. RTI pulls them off. Putting something on the stack and not properly removing it tends to break things whenever the stack is next used.
2. The NMI is an interrupt. It can happen at anytime, after any instruction. If you jmp back to a fixed location, if the program didn't happen to be in that fixed location whatever the code was doing before the NMI interrupted will never get done.

Example file: View attachment Mapper30 Switch.zip
I did end up having to deal with Bus Conflicts after all, that's what the BYTETABLE,y stuff is for. Besides that, it's basically what I posted except with more initialization. (Have to write tile 0 to a nametable/write palettes so the change can actually be seen.)
 

Mihoshi20

Member
Kasumi said:
There aren't many cartridge types that allow animated tiles in exactly the way Mapper 30 does.

You can use RAM instead of ROM for tiles with NROM (mapper 0), but it doesn't have bank switching. (The writes to $8000 I mentioned. That's specifically how mapper 30's bank switching works.)
You can use CNROM to try bank switching, but it doesn't have CHR RAM. The way bank switching is done is also slightly different from Mapper 30. https://wiki.nesdev.com/w/index.php/CNROM

Yes, currentbank would be a byte in RAM. You would initialize all NES things, then before you turn rendering back on after initialization you would write the tiles you want. Then turn the screen on and enable NMIs.

You must absolutely use RTI at the end of the NMI code. For two reasons:
1. Remember when you noticed when the NMI hit, the stack pointer went to $FC? RTI makes it go back to $FF. If you jmp back, the next frame will move the stack pointer to $F9. It will keep going down. The NMI happening at all puts plates on the stack. RTI pulls them off. Putting something on the stack and not properly removing it tends to break things whenever the stack is next used.
2. The NMI is an interrupt. It can happen at anytime, after any instruction. If you jmp back to a fixed location, if the program didn't happen to be in that fixed location whatever the code was doing before the NMI interrupted will never get done.

Example file: Mapper30 Switch.zip
I did end up having to deal with Bus Conflicts after all, that's what the BYTETABLE,y stuff is for. Besides that, it's basically what I posted except with more initialization. (Have to write tile 0 to a nametable/write palettes so the change can actually be seen.)

I can't thank you enough Kasumi as you've been such an incredible help, not to mention a wealth of valuable information and giving me some nice starting points. Not to mention a wonderful example to study and play around with along with your previous information outlining it.
 

Kasumi

New member
No problem. This example doesn't exactly show why this is cool, but I didn't want to include graphics from a commercial game and I don't have a four frame background animation at the ready. Then again, I just remembered I shared a ROM here with SF3 and Kirby Adventure's water animation: https://i.imgur.com/FrwmyMd.gif >_>

So maybe I should just upload a version with Kirby's Water animation... Maybe later, other things require attention for the moment.
 

Kasumi

New member
And I found time to do it. Here's Kirby's Water animation. For what it's worth, I think the use of this data could be defended by fair use. It's an educational purpose through and through.
 

Attachments

  • Mapper30 Switch 2 Kirby Style.zip
    34.9 KB · Views: 19
Top Bottom