nametable questions

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

User avatar
bbbirddd
Posts: 21
Joined: Thu Jan 08, 2009 12:02 pm
Location: New Hampshire, US
Contact:

Post by bbbirddd »

:? this works but i still get the flicker. hmmm
Celius
Posts: 2159
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

Oh, try just remove "lda #3 sta $4014" really quick and see if the flickering continues then. If it doesn't, that means that sprite DMA, which I'm pretty sure takes 513 cycles to execute, and then the extra ~1950 to execute the other update code would combine to equal an amount that would cause a spill out of Vblank.
User avatar
bbbirddd
Posts: 21
Joined: Thu Jan 08, 2009 12:02 pm
Location: New Hampshire, US
Contact:

Post by bbbirddd »

Ok cool, that does prevent the flickering. So I guess I actually don't really need a sprite on screen for the rom I'm trying to make (sort of a live visual type deal), but I am wondering, how do I draw an entire screen full of background tiles instead of just a few rows. I'm already working with 128 bits (?) of background information for only a few lines and isn't there a limit when I'm doing this:

Code: Select all

	lda #$20
	sta $2006 					; $2020-$23C0 = name table 0 (32x25 tiles)
	sta $2006 

	ldx #$00
loadNames:						; load background name table (bkg12.map)
	lda ourMap, x
	inx
	sta $2007					; $2007 = PPU memory data
	cpx #4*32
	bne loadNames
Celius
Posts: 2159
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

If you have a 1k (1024 bytes) name table arrangement in a file (Like your files, just for the whole name table), in order to load it you have to use indirect addressing. So you'd put the address of the start of the background data file into say $50 and $51, where $50 contains the low byte and $51 contains the high byte of the address. Then you start with Y = 0, and you do "lda ($50),y sta $2007" and "iny" in a loop until y wraps back to 0, at which point you add 1 to $51. And you'll loop 3 more times after that. You basically copy 4 256 byte sections to the name table. But if you do this, you're going to want to shut off the screen entirely for a frame or so, because this is probably not going to fit into 2200 cycles (in fact, that's impossible for that to happen).
User avatar
bbbirddd
Posts: 21
Joined: Thu Jan 08, 2009 12:02 pm
Location: New Hampshire, US
Contact:

Post by bbbirddd »

Thanks again Celius. This has been really helpful. But this all leads me to a question. Isn't it better to perform as few operations during an interrupt as possible? I understand that what I am doing at the moment is taking less than 2200 cycles and therefore will complete without flickering during a VBlank, but wouldn't it generally be better to keep as much out of the VBlank routine as possible? In your last response you suggested that I could not redraw a whole background image within 2200 cycles, and therefore would need to turn the screen off and on around that code. If I do this, should I take all that controller stuff out of the VBlank routine and put it back in my Infinite loop?
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

You can keep the controller reading code in the NMI handler, as long as you do it after you turn rendering back on at PPUMASK. In fact, Super Mario Bros. does virtually everything in its NMI handler.
User avatar
bbbirddd
Posts: 21
Joined: Thu Jan 08, 2009 12:02 pm
Location: New Hampshire, US
Contact:

Post by bbbirddd »

so what does one generally put in the infinite loop? anything?
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Post by Bregalad »

I don't put an infinite loop. The closer I have a game loop, but it's not intinite because it breaks on game over or level beaten condiitons.

SMB have an infinite loop tough.
Useless, lumbering half-wits don't scare us.
User avatar
Disch
Posts: 1848
Joined: Wed Nov 10, 2004 6:47 pm

Post by Disch »

Seperating the infinite loop from the NMI is unimportant in simple programs, but to handle slowdown effectively, it can be useful to keep them each doing seperate jobs. The approach I'd use would be something like this:

In Infinite loop:
---------------------
1) Update joypad data
2) Do game logic (collision detection, AI, etc)
3) Draw sprites and stuff to shadow OAM
4) Prepare an organized list of PPU writes in a buffer so that you can quickly dump stuff to $2007 next vblank.
5) Set a "can draw" flag
6) Set a "waiting for VBl" flag
7) Wait for "waiting for VBl" flag to clear
8) Repeat from step 1


In NMI:
---------------------
1) Set up CPU cycle based IRQ if needed for raster effects (if mapper 069 or something -- probably not applicable most of the time).
2) Do Sprite DMA (only if "can draw" flag is set)
3) Scan the prepared drawing buffer and do all desired $2007 writes (step 4 in infinite loop) -- (only if "can draw" flag is set)
4) Clear the "can draw" flag to indicate drawing is done
5) Set scroll
6) Set up PPU based IRQ if needed for raster effects (if mapper 004 or something)
7) Just to music routine to keep music/sound effects playing
8) Clear "waiting for vbl" flag
9) RTI



This setup keeps the logic and drawing code seperated so that if NMI interrupts your game logic, you can still do necessary raster effects like splitting the screen, as well as keep music playing without disrupting game logic.

Also the "can draw" flag prevents your NMI handler from drawing half-completed stuff if the NMI happens in the middle of your preparation.

So this setup gracefully handles slowdown and stuff without causing weird graphical glitches.


EDIT:

it dawns on me that you can use the "waiting for vblank" flag as the "can draw" flag -- so you only need one flag. *shrug*
User avatar
bbbirddd
Posts: 21
Joined: Thu Jan 08, 2009 12:02 pm
Location: New Hampshire, US
Contact:

Post by bbbirddd »

wow, thanks for the great explanations. i'm still a bit hung up on indirect addressing. I understand how the actual addressing works I think, but how do I store my background data in a specific location to access by indirect addressing? For example, I have this included in my code at the moment, how do I store it in a specific location?

Code: Select all

ourMap2:.db  1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0
		.db 17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0 
		.db  0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2
		.db  0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18
		.db  1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0
		.db 17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0 
		.db  0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2
		.db  0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18
		.db  1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0
		.db 17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0 
		.db  0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2
		.db  0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18
		.db  1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0
		.db 17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0 
		.db  0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2
		.db  0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18
		.db  1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0
		.db 17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0 
		.db  0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2
		.db  0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18
		.db  1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0
		.db 17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0 
		.db  0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2
		.db  0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18
		.db  1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0
		.db 17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0 
		.db  0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2
		.db  0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18
		.db  1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0
		.db 17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0,17,18, 0, 0 
Is this table ready to be stored somewhere/accesses? Or do I need to save this info in some other way? Again, I'm a beginner, so sorry if this is pretty simple.
User avatar
Disch
Posts: 1848
Joined: Wed Nov 10, 2004 6:47 pm

Post by Disch »

Your data should be immediately accessable via the ourMap2 label. What you need to do is copy the address this label represents (aka create a "pointer" to the label) and put that pointer somewhere in zero page memory. From there you can use indirect addressing on that pointer.

I don't know the NESASM syntax for it (assuming that's what you're using -- I haven't really been paying attention), but here's how you'd do that in ca65:

Code: Select all

; assuming 'ptr' is a 2-byte variable in zero page

lda #<ourMap2   ; put low byte of ourMap2 address in A
sta ptr         ;  write as low byte of our pointer

lda #>ourMap2   ; get high byte
sta ptr+1       ;  write as high byte of pointer

ldy #0          ; zero Y for indexing
lda (ptr),Y     ; do an indirect read from the address our pointer points to
                ;  plus Y.  Since ptr points to 'ourMap2', and since Y=0, this
                ;  will read from ourMap2+0

                ; you can increase Y to go to the next byte, then increase ptr+1 once
                ;  Y wraps to point to the next 256-byte block
iirc, nesasm code for this is probably something like:

Code: Select all

lda #LO(ourMap2)
sta <ptr

lda #HI(ourMap2)
sta <ptr+1

ldy #0
lda [ptr],Y
User avatar
bbbirddd
Posts: 21
Joined: Thu Jan 08, 2009 12:02 pm
Location: New Hampshire, US
Contact:

Post by bbbirddd »

Again, I understand the explanation but I am in fact using NESASM and can't seem to get this to work. Does anyone know how to handle this in NESASM? Thanks for all the help
User avatar
Banshaku
Posts: 2404
Joined: Tue Jun 24, 2008 8:38 pm
Location: Japan
Contact:

Post by Banshaku »

I didn't follow the thread completely but the proper syntax for nesasm is:

Code: Select all

	lda #LOW(ourMap2)
	sta ptr
	lda #HIGH(ourMap2)
	sta ptr + 1
Post Reply