HDMA Sprite demo not working...

Discussion of hardware and software development for Super NES and Super Famicom. See the SNESdev wiki for more information.

Moderator: Moderators

Forum rules
  • For making cartridges of your Super NES games, see Reproduction.
93143
Posts: 1875
Joined: Fri Jul 04, 2014 9:31 pm

Re: HDMA Sprite demo not working...

Post by 93143 »

Espozo wrote:LDA #$80
STA $2100 ; Turn the screen back on

STZ $420C
LDA #$00
STA $4310 ; CPU -> PPU, auto increment, write 1 reg, $2100
LDA #$00
STA $4311
LDY #ForceBlankTable
STY $4312 ; source offset
LDA #$00
STA $4314 ; bank address = $7E (work RAM)
LDA #$02
STA $420C ;start HDMA transfer

Later...

ForceBlankTable:
.DB $60,$80 ; Turns off the screen after line $60
.DB 0
Two things: first, it looks like your "turn the screen back on" part actually turns it off. You have to write $0 (or something less than $8) to the high nibble, and something nonzero to the low nibble because that's what controls the screen brightness - if you want full brightness, as is probable, you should write $0F to $2100 to turn the screen on.

Second, why are you taking your HDMA table values out of WRAM? Is the table actually in WRAM?

Also, the first part of your code is in the NMI routine, right?
Regarding updating everything while the screen is off, how do you arrange everything? Would you put the part that gets updated in force blank between where you turn the screen off and where you turn the screen on?
Generally you'd use an IRQ. HDMA isn't so useful for this in most cases, because it's not synchronized with the main code. You'd need to be writing raster-aligned cycle-timed code to fit ordinary processing or general-purpose DMA in between two specific scanlines, and that's a very advanced programming technique; I can't help you with such a thing...

But with an IRQ, you have a whole separate chunk of code that's guaranteed to run at a certain position on the screen, so you can turn off the display, do whatever needs to be done, and then turn it back on.

You can even have the IRQ fire at different times to do different things; the IRQ code itself can change the settings for when it's supposed to fire ($4200 and $4207-420A), and change a variable in RAM or something each time, so it can check that variable at the start and branch to the appropriate task. This can be useful even in a basic case like the Mario Kart split screen, if you can't guarantee that your IRQ code will take exactly the right amount of time - rather than cycle counting with NOPs or polling the H/V counters, just repurpose the IRQ to turn the screen back on (and then un-hijack it again so it works properly during the next frame, obviously).
Also, about graphics being messed up after force blanking mid screen, How is Super Mario Kart able to transfer sprite Data using force blank then? I don't see any abnormalities with the graphics.
That's only for the same scanline. I assume Mario Kart re-enables rendering soon enough after the end of the last blank scanline that the next scanline works fine.

I suppose if this was causing trouble, you could re-enable the screen early at zero brightness (write $00 to $2100), and then bump it to full brightness ($0F) at the point where you actually wanted it turned on. Now that I think about it, sprites are scanned and cached during the scanline above the one where they're displayed, and I doubt that happens during forced blank, so this may be what SMK is actually doing...

...maybe I should back off and let an actual expert handle this...
Lastly, are pixels from a sprite that are off screen count toward the limit?
No. If all parts of a sprite are off screen, it doesn't count towards the 32-sprite limit, and if a tile is off screen it doesn't count towards the 34-tile limit.

...except when the sprite is at $0100 exactly, in which case all of its tiles count towards the tile limit. Basically, never put a sprite at X=256, especially a big one; no good can come of it.
Sik
Posts: 1589
Joined: Thu Aug 12, 2010 3:43 am

Re: HDMA Sprite demo not working...

Post by Sik »

93143 wrote:...except when the sprite is at $0100 exactly, in which case all of its tiles count towards the tile limit. Basically, never put a sprite at X=256, especially a big one; no good can come of it.
So that is why the Nintendo documentation says to never allow a sprite at those coordinates no matter what (though sadly they never explain the reason). Huh, I wonder what they had screwed up in the process. I guess it can still help if you need to do sprite clipping though (nowhere near as easy as Sega's own screw-up though where a single sprite would clip every single one after... later they tried to claim that bug was a feature =P).
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: HDMA Sprite demo not working...

Post by Drew Sebastino »

93143 wrote:it looks like your "turn the screen back on" part actually turns it off.
Oops, my bad...
93143 wrote:Second, why are you taking your HDMA table values out of WRAM? Is the table actually in WRAM?
I'm actually taking it from bank 0 ($00 is bank 0 right?), it's just that I accidentally forgot to change some of the leftover writing I got from another code.
93143 wrote:Also, the first part of your code is in the NMI routine, right?
The whole thing is.
93143 wrote:I suppose if this was causing trouble, you could re-enable the screen early at zero brightness (write $00 to $2100), and then bump it to full brightness ($0F) at the point where you actually wanted it turned on.
After playing Super Mario Kart again, I actually noticed that there is a thin black line that runs between the two screens, which might actually be there to hide the messed up graphics.
93143 wrote:Now that I think about it, sprites are scanned and cached during the scanline above the one where they're displayed, and I doubt that happens during forced blank, so this may be what SMK is actually doing....
??? Are you saying that Super Mario Kart uses forced blank, because I thought that had already been determined.
93143 wrote:...maybe I should back off and let an actual expert handle this...
Some help is better than none! But seriously, where's byu when you need him? :roll: It would have been extremely helpful if he bothered to figure out Uniracers... Oh, didn't byu actually say that writing to OAM during h-blank will send your values to where the PPU was last working? would it be possible to somehow align it or something to where it goes to the right place based on the rest of your code or something? Or did he say that it is only possible to write to high OAM during h-blank? I'm kind off starting to eliminate using forced blank if it's going to output messed up graphics. (didn't someone say it actually messes up BG tiles?)
Sik wrote:
93143 wrote:...except when the sprite is at $0100 exactly, in which case all of its tiles count towards the tile limit. Basically, never put a sprite at X=256, especially a big one; no good can come of it.
So that is why the Nintendo documentation says to never allow a sprite at those coordinates no matter what (though sadly they never explain the reason).
Nintendo's docs are a bit of a disaster...
psycopathicteen
Posts: 3197
Joined: Wed May 19, 2010 6:12 pm

Re: HDMA Sprite demo not working...

Post by psycopathicteen »

tepples wrote: If you want to hide a sprite, put it at (384, 224).
When I place sprites off-screen, I don't change the x-coordinates, only the y-coordinates. You probably know why.
93143
Posts: 1875
Joined: Fri Jul 04, 2014 9:31 pm

Re: HDMA Sprite demo not working...

Post by 93143 »

Espozo wrote:
93143 wrote:I suppose if this was causing trouble, you could re-enable the screen early at zero brightness (write $00 to $2100), and then bump it to full brightness ($0F) at the point where you actually wanted it turned on.
After playing Super Mario Kart again, I actually noticed that there is a thin black line that runs between the two screens, which might actually be there to hide the messed up graphics.
That line is what forced blank looks like. You wouldn't have time to rearrange much of OAM during HBlank even if you did turn off rendering during it; a full OAM update takes more than three scanlines of pure DMA. And Mario Kart might have other things to do between the two screens besides the OAM update. So it blanks the display for several scanlines to get it all done.
93143 wrote:Now that I think about it, sprites are scanned and cached during the scanline above the one where they're displayed, and I doubt that happens during forced blank, so this may be what SMK is actually doing....
??? Are you saying that Super Mario Kart uses forced blank, because I thought that had already been determined.
No, I'm saying that if force blank (which SMK uses) prevents sprite prefetch from happening, maybe there's an extra scanline or so of rendering at zero brightness (write #$00 to $2100) after the blanked area, to allow the sprites to work properly on the top scanline of player 2's screen. It's not like you could visually tell the difference between (a) 8 scanlines of forced blank and (b) 7 scanlines of forced blank plus one scanline rendered at zero brightness...

In other words, a thin line to hide the messed up (or absent) graphics, at the bottom of the thicker line that's actually forced blank in order to let the game write to OAM.

Maybe. I haven't investigated it, and I don't have enough experience to say whether or not it's necessary.

I'm pretty sure BG layers work fine a few tiles after force blank is turned off, so as long as you disable force blank early enough after HBlank starts, the next scanline's BG layers should work normally. At least, I've done it with a single 8bpp layer in mode 3. It's only sprites that need extensive preprocessing.
Nintendo's docs are a bit of a disaster...
I would recommend this and this if you find Nintendo's manual hard to work with. They're much easier to search in, for one thing...
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: HDMA Sprite demo not working...

Post by Drew Sebastino »

93143 wrote:That line is what forced blank looks like. You wouldn't have time to rearrange much of OAM during HBlank even if you did turn off rendering during it; a full OAM update takes more than three scanlines of pure DMA. And Mario Kart might have other things to do between the two screens besides the OAM update. So it blanks the display for several scanlines to get it all done.
Doesn't that mean you could actually force blank 1/4 of OAM on one line and there wouldn't be any graphical side effects, Or would the line still be messed up? Or will it still be messed up because it can't "pre fetch" or whatever? It might be worth it to have 4 lines of messed up sprites if it means you could push twice the amount of sprites on screen. (it would probably look better if there were four spread out lines)
93143 wrote:I'm pretty sure BG layers work fine a few tiles after force blank is turned off, so as long as you disable force blank early enough after HBlank starts, the next scanline's BG layers should work normally. At least, I've done it with a single 8bpp layer in mode 3. It's only sprites that need extensive preprocessing."]
Do you mind showing me what you wrote? :oops: I'm not completely sure how to set it up.
93143 wrote:I would recommend this and this if you find Nintendo's manual hard to work with. They're much easier to search in, for one thing...
Thank You! :D I knew about superfamicom.org, but the other website is also very helpful

Oh yeah, If we ever did pull off this stunt, how would you set it up? Say if you wanted 256 sprites, would you create another table in ram that replaces the values of the original sprite table as you go down the screen? And how would you transfer 1 sprite from the top off the screen to the bottom? Would you check to see if the sprites y value is greater than where the line is, store the values of that sprite on the other sprite table, and then turn off the sprite on the first sprite table? Doing this with all 128 sprites seems like it would take a lot of time though...

Just wondering, do you know how Bullet GBA does it? (if you even know what I'm talking about)
tepples
Posts: 22915
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: HDMA Sprite demo not working...

Post by tepples »

On the GBA, OAM and VRAM are writable even during draw time, like on the TG16 and Genesis. It's how the GBA port of Super Puzzle Fighter II draws its playfields.
Shonumi
Posts: 342
Joined: Sun Jan 26, 2014 9:31 am

Re: HDMA Sprite demo not working...

Post by Shonumi »

Actually, to my knowledge, on the GBA OAM and OAM VRAM are read-only during scanline rendering* They can only be written to during HBlank, VBlank, or Forced Blank. For HBlank, there is the additional requirement that you flip a certain bit in one of the GBA's display registers (DISPCNT) to allow the H-Blank Interval Free feature. I believe all DMAs to OAM and OAM VRAM require H-Blank Interval Free too. H-Blank Interval Free has the consequence of reducing the number of cycles available for rendering sprites on a given scanline. The maximum number of sprites under H-Blank Interval Free actually depends on a number of other factors based on what types of sprites are used.

*EDIT: Actually, I don't even know if that "read-only" bit is true. Current documentation just mentions an "access" to OAM is only available during blanking periods, not necessarily a "read".

Now, I have not written any test ROMs to verify this behavior on my hardware, but nocash and Nintendo's official docs both imply that OAM and OAM VRAM can only be modified at specific blanks. Palette data and BG VRAM are fair game for reading and writing at any given moment, however, if you can bear with the few extra cycles it takes for memory access. Overall though, it's a lot more flexible than the GB or GBC ever were.

For reference:
http://problemkaputt.de/gbatek.htm#lcdiodisplaycontrol
http://problemkaputt.de/gbatek.htm#lcdobjoverview
tepples
Posts: 22915
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: HDMA Sprite demo not working...

Post by tepples »

Shonumi wrote:H-Blank Interval Free has the consequence of reducing the number of cycles available for rendering sprites on a given scanline.
Without: 5x overdraw
With: 4x overdraw
(Assumes no rot/scale sprites)

Compare to ~1x overdraw for Super NES, Genesis, and TG16
93143
Posts: 1875
Joined: Fri Jul 04, 2014 9:31 pm

Re: HDMA Sprite demo not working...

Post by 93143 »

Espozo wrote:
93143 wrote:That line is what forced blank looks like. You wouldn't have time to rearrange much of OAM during HBlank even if you did turn off rendering during it; a full OAM update takes more than three scanlines of pure DMA. And Mario Kart might have other things to do between the two screens besides the OAM update. So it blanks the display for several scanlines to get it all done.
Doesn't that mean you could actually force blank 1/4 of OAM on one line and there wouldn't be any graphical side effects, Or would the line still be messed up? Or will it still be messed up because it can't "pre fetch" or whatever? It might be worth it to have 4 lines of messed up sprites if it means you could push twice the amount of sprites on screen. (it would probably look better if there were four spread out lines)
I don't think you're getting this. Force blank isn't something you do to OAM. It means turning the screen off. The fact that you can access OAM while the screen is off is a side effect. And since OAM is 544 bytes and DMA is only 1324 bits per scanline, you'd need to turn the screen off completely for more than three scanlines to update all of OAM in one shot. Those scanlines would be black, not because of the OAM refresh but because you turned off the screen.

While the screen is off, the PPU isn't keeping track of what it would otherwise be doing. This means that if you turn the screen on partway through the active display portion of a frame, it takes a while for the PPU to figure out what's going on. In the case of BG layers, it's a few tiles. In the case of sprites, it's a whole line. So the line under the black area would (we think) have no sprites, or glitched sprites. (Unless you set the screen to zero brightness for the duration of that line, in which case it too would be black.)

So in your example, you'd get one black scanline (during which you can do a partial OAM update), probably followed by one line with sprites absent or not working properly, and then the picture would work normally for subsequent scanlines.
93143 wrote:I'm pretty sure BG layers work fine a few tiles after force blank is turned off, so as long as you disable force blank early enough after HBlank starts, the next scanline's BG layers should work normally. At least, I've done it with a single 8bpp layer in mode 3. It's only sprites that need extensive preprocessing.
Do you mind showing me what you wrote? :oops: I'm not completely sure how to set it up.
I don't really mind, but be aware that there may be a vulnerability in it, so you shouldn't slavishly copy what I've done... I was using force blank to extend VBlank, and using the IRQ as a substitute for NMI (since you can't change when NMI triggers). Here's the relevant part of my code:

Code: Select all

.ENUM $10
disp        db         ; screen brightness; can be given to $2100 to turn the screen on
end_vblank  db         ; IRQ toggle flag
.ENDE

Code: Select all

	ldx #$00B0          ; dot number for interrupt
	stx $4207           ; set H-timer
	ldx #$00DC          ; scanline number for interrupt
	stx $4209           ; set V-timer
	stz end_vblank      ; initialize IRQ switch

Code: Select all

	lda #$31            ; enable IRQ and controller autopoll
	sta $4200

Code: Select all

Interrupt:
	phb
	pha
	phx
	phy
	phd
	php

	rep #$30            ; all 16-bit
	lda #$0000.w        ; reset direct page
	tcd
	sep #$20            ; mem/A = 8 bit
	lda #$00            ; ...but A should already be zero...
	pha
	plb                 ; reset data bank

	lda end_vblank
	beq begin_vblank
	ldx #$00B0          ; dot number for interrupt
	stx $4207           ; set H-timer
	ldx #$00DC          ; scanline number for interrupt
	stx $4209           ; set V-timer
	stz end_vblank      ; toggle IRQ
	lda disp            ; turn the screen back on (brightness stored in RAM)
	sta $2100
	jmp end_interrupt

begin_vblank:
	lda #$80            ; turn the screen off
	sta $2100
	ldx #$0090          ; dot number for interrupt
	stx $4207           ; set H-timer
	ldx #$0004          ; scanline number for interrupt
	stx $4209           ; set V-timer
	inc end_vblank      ; toggle IRQ

	; VBLANK CODE GOES HERE

end_interrupt:
	lda $4211           ; clear interrupt flag

	plp
	pld 
	ply 
	plx 
	pla 
	plb 

	rti
PLEASE NOTE that there is a sporadic timing-sensitive bug hiding somewhere in the program I took this from, and removing the lda #$00 from the direct page and data bank reset code near the top of the IRQ triggers the bug. (Actually, I can replace the lda #$00 with two nops and it will still work, which is very odd as with the lda #$00 there I can delete both lda #$0000.w and tcd and it will still work...) I am not a guru; use my code with caution.

Better yet, don't use my code at all. Look at it, figure out what it does, and then write your own.

Oh, and if any of the experts on here can tell what might be going wrong (the bug doesn't affect the IRQ; it results in a subroutine the IRQ is interrupting occasionally getting a calculation wrong), please speak up...
Oh yeah, If we ever did pull off this stunt, how would you set it up?
I haven't really worked it out yet. The details probably depend on how well it works, if it does (which is looking increasingly unlikely, unless you're willing to accept graphical artifacts). But you sound like you might be on the right track.

As for me, I can't spend a whole lot of time on this right now, because I have a ton of work to do and I'm behind schedule...
ARM9
Posts: 57
Joined: Sun Aug 11, 2013 6:07 am

Re: HDMA Sprite demo not working...

Post by ARM9 »

93143 wrote:what might be going wrong (the bug doesn't affect the IRQ; it results in a subroutine the IRQ is interrupting occasionally getting a calculation wrong), please speak up...
One thing that comes to mind is that your irq is nuking the high byte of the accumulator. If an irq were to be raised when the accumulator is 8 bit and the hidden high byte contains data, it'll contain garbage once the interrupt returns, since you're only pushing the low byte in the irq prologue. The same goes for x/y too, although that's less common.
Here's how I would do it:

Code: Select all

irqHandler:
 rep #$30 ; always preserve the full 16 bits of a/x/y registers
 pha
 phx
 phy
 phb
 phd
 ...
 rep #$30
 pld
 plb
 ply
 plx
 pla
 rti ; irq preserves the psr on 65816, hence we don't have to php/plp with this approach
93143
Posts: 1875
Joined: Fri Jul 04, 2014 9:31 pm

Re: HDMA Sprite demo not working...

Post by 93143 »

[*facepalm*]

That would explain it nicely. Especially since the only part of the entire interrupt routine that touches B is the direct page reset, which is what I had originally removed to fix the problem.

Rewriting the IRQ intro and outro as recommended results in a working code, with or without the extra lda #$00 or a string of between one and five nops. Looks good. Feels good.

Thanks!

...

Is there anything important about the order of the stack operations? I mean, other than having to pull in reverse order to the pushes?
Post Reply