CHR RAM & Writing Pattern Table Data On The Fly

TokyoScarab

New member
Hi! I'm working on a game and want to be able to load in different chunks of data into the pattern table to make use of some variable based graphics. The idea is roughly that there would be 4 background tiles reserved for this in page 2 of the PPU's pattern table and I could load in replacement graphics for these tiles on the fly. I have a similar thing set up for changing background tiles in the nametable, but was wondering what all I have to do to get something similar to this running. Any help would be appreciated! :D
 

jorotroid

Member
At a high level, this is roughly the order of things that would need to happen:
  1. At some point during the game loop you'll have a routine that preps the patterns to be updated.
  2. You will probably need some sort of variable to track the timing of the updates if you are doing animated tiles to see if an update is needed this frame or not.
  3. You will need to have some variables dedicated to storing information about where the CHR data is stored.
  4. You will need a variable or a bit of a variable that will get checked during NMI that indicates that it is time to do a pattern table update.
  5. In NMI, you will need a routine that takes the location data that you set earlier, and use it iterated through the CHR data and send it to the PPU.

That should do it, but here are some additional details:
  • You might figure out a different way of going about it, but the variable you will need will probably be one for the bank the CHR is stored in and two for the starting address. If you are always updating the same patterns in the table, the patterns are always consecutive, and the CHR data is also consecutive (meaning you're not jumping around getting CHR data for here and there in a single update), then I think that will be all the variables you need. More flexibility probably needs more variables.
  • You will have to do a bank switch to grab the CHR data during NMI, then switch it back when you are done or you will derail your game's code.
  • There are about 2270 cycles of time available to send information to the PPU. For the most part how NMI works in the current cores is that one task is done per NMI to reduce the chance of going past that 2270 limit. You can try to do your pattern update during the same NMI as something else that is updating as long as you don't go over in cycles, or you can wait for a free NMI. If you wait, the update might just be delayed by a frame or two, and might not be noticeable. If you try to do the update at the same time as other update, I have counted the number of cycles for I think worst case of various update routines:
    • Palette updates: 520 cycles
    • Tile Updates: 1553 cycles
    • Attribute Updates: 557 cycles
    • HUD update: 102 cycles
    Additionally, there are some cycles taken up before and after these updates, but I don't have those numbers off hand. I shave them do to 84 cycles before and 33 cycles after, but I don't know how many cycles they were before.
  • To get an idea of how to do things, I recommend looking at those update routines to see how data is sent to the PPU. They are located in the DataLoadScripts folder. If you want some of that code explained, feel free to ask.
  • Your code might end up looking like the code in LoadChrRam.asm located in the system folder. You might even be able to call that routine instead of writing your own, but I don't know for sure at this moment.
  • To plan out how many cycles you'll be using, this is a helpful reference: http://6502.org/tutorials/6502opcodes.html

Ok, I am probably forgetting or overlooking something obvious. I hope this sends you in the right direction!
 

TokyoScarab

New member
I know a lot of these things already. One problem I'm having is that the graphic tile I'm using to test is loading in the PPU, but it's not in the right spot. I've read the PPU information on pattern tables over and over and am not sure why. I've had 0 other issues writing other data to the PPU (changing nametable tiles, palettes, etc). In my test, I am trying to write to tile $D0 in the right page ($1D00) of the pattern table but each time I do, it just writes the tile to $50 ($0500) in the left page of the pattern table. I even tried changing the address to $0D00 just to see what would happen and that writes to tile $00 of the left page. It seems to make no sense. Only thing I could find is that D can equal 5 in binary if you take off the top bit. Not sure if there is any correlation in that and what is happening but I'm at a loss. The test code I have is:

Code:
	LDA gfxLoad
	BEQ +
	LDX #$00
	STX gfxLoad
	LDA $2002
	LDA $1D
	STA $2006
	LDA #$00
	STA $2006
graphicLoop2:
	LDA Test_Lower2,x
	STA $2007
	CPX #$0F
	BEQ +
	INX
	JMP graphicLoop2
	
	
Test_Lower2:
	.db #$FF, #$DB, #$DB, #$DB, #$FF, #$FF, #$FF, #$FF, #$00, #$24, #$24, #$24, #$00, #$42, #$3C, #$00
	
+

I thought perhaps it could be the placement of the code as I have it at the very beginning of the LoadHudData script. I did this because this game doesn't scroll, so there's pretty much nothing going on between frames except checking to see if the hud needs updating. This is also where I put my other loop to queue up background tile changes in the nametable. Any help on this would be very much appreciated! :D
 

jorotroid

Member
Oh, man. I looked at your code, and I couldn't see anything wrong with it either, so I went ahead and ran it. You might feel a bit silly because it turns out it was a pretty minor typo. You didn't put a # on the line that LDAs 1D, so by just having $1D instead of #$1D, you were loading into the accumulator whatever value was being stored in the zero page address of $1D.

Anyway this is what I did to track down what was wrong.
  • First either check out my label extractor or look into asm6f for getting a .mlb file for Mesen to import the labels from your code.
  • The labels might import automatically, or you might have to check an option somewhere to get them to import automatically, but if they don't import on their own you can open up the debugger (ctrl+D) in Mesen and go to File>Workspace>Import Labels
  • Use ctrl+F to search for a label in the part of the code you are dealing with. (But ctrl+F only works if the bank with that code is currently loaded. If you were trying to look at code that is in a different bank, make a breakpoint somewhere that you know will switch to that bank, and then use ctrl+F.
  • When dealing with writing to the PPU, I also find it very helpful to to set a breakpoint on the PPU address I am trying to change. You can do this by opening up the Memory Tools (ctrl+M), Select the PPU Memory view from the dropdown list, scroll to find the address you are looking for, and right click and select Edit Breakpoint. Uncheck Read and make sure Write is checked. Now Mesen will break when that address gets written to, and you can step back in the code with one of the buttons at the top of the debugger (or Shift+F10) to see if the right values are being used for the code you are trying to run. This is also a good way of figure out if something is writing to that address that isn't suppose to.

Hope that helps!
 
Top Bottom