News:

11 March 2016 - Forum Rules

Main Menu

SNES: Trouble writing to VRAM

Started by Crow!, August 16, 2018, 07:22:28 PM

Previous topic - Next topic

Crow!

Hey guys,
This is Crow!, the developer of the FFIV: The Lunarian Shuffle randomizer.  ( Project blog can be found here: https://ff4-lunarianshuffle.blogspot.com/ )

I'm working on a new feature (called "lunacy mode") which will cause the game to shuffle your characters at the end of each battle that you win.  That function appears to be working right, which I was pumped about!  But then I decided I wanted the game to update what your characters looked like and stop to show a message so you can see who your new set is without having to go back to the party menu after every battle.

So, I'm attempting to overwrite the character sprite data (located in VRAM starting at address 0, with a new character every 0x800 bytes or 0x400 words as I understand it).  My first attempt at this was to do a DMA transfer, mimicking what the game had done to load that data into VRAM in the first place.  See a trace of the first iteration of my loop for this below:

Spoiler
208430 phx                    A:0000 X:1000 Y:1d80 S:02dc D:0000 DB:7e nvMxdiZc V:111 H: 76 F:24
208431 phy                    A:0000 X:1000 Y:1d80 S:02da D:0000 DB:7e nvMxdiZc V:111 H: 83 F:24
208432 phb                    A:0000 X:1000 Y:1d80 S:02d8 D:0000 DB:7e nvMxdiZc V:111 H: 91 F:24
208433 lda #$00               A:0000 X:1000 Y:1d80 S:02d7 D:0000 DB:7e nvMxdiZc V:111 H: 96 F:24
208435 pha                    A:0000 X:1000 Y:1d80 S:02d7 D:0000 DB:7e nvMxdiZc V:111 H:100 F:24
208436 plb                    A:0000 X:1000 Y:1d80 S:02d6 D:0000 DB:7e nvMxdiZc V:111 H:106 F:24
208437 stz $2115     [002115] A:0000 X:1000 Y:1d80 S:02d7 D:0000 DB:00 nvMxdiZc V:111 H:113 F:24
20843a ldy #$0000             A:0000 X:1000 Y:1d80 S:02d7 D:0000 DB:00 nvMxdiZc V:111 H:120 F:24
20843d stz $1d0c     [001d0c] A:0000 X:1000 Y:0000 S:02d7 D:0000 DB:00 nvMxdiZc V:111 H:126 F:24
208440 stz $1d0d     [001d0d] A:0000 X:1000 Y:0000 S:02d7 D:0000 DB:00 nvMxdiZc V:111 H:144 F:24
208443 rep #$20               A:0000 X:1000 Y:0000 S:02d7 D:0000 DB:00 nvMxdiZc V:111 H:152 F:24
208445 tya                    A:0000 X:1000 Y:0000 S:02d7 D:0000 DB:00 nvmxdiZc V:111 H:158 F:24
208446 sta $2116     [002116] A:0000 X:1000 Y:0000 S:02d7 D:0000 DB:00 nvmxdiZc V:111 H:161 F:24
208449 clc                    A:0000 X:1000 Y:0000 S:02d7 D:0000 DB:00 nvmxdiZc V:111 H:170 F:24
20844a adc #$0400             A:0000 X:1000 Y:0000 S:02d7 D:0000 DB:00 nvmxdiZc V:111 H:174 F:24
20844d tay                    A:0400 X:1000 Y:0000 S:02d7 D:0000 DB:00 nvmxdizc V:111 H:180 F:24
20844e tdc                    A:0400 X:1000 Y:0400 S:02d7 D:0000 DB:00 nvmxdizc V:111 H:183 F:24
20844f lda $1d0c     [001d0c] A:0000 X:1000 Y:0400 S:02d7 D:0000 DB:00 nvmxdiZc V:111 H:187 F:24
208452 asl a                  A:0000 X:1000 Y:0400 S:02d7 D:0000 DB:00 nvmxdiZc V:111 H:197 F:24
208453 asl a                  A:0000 X:1000 Y:0400 S:02d7 D:0000 DB:00 nvmxdiZc V:111 H:200 F:24
208454 asl a                  A:0000 X:1000 Y:0400 S:02d7 D:0000 DB:00 nvmxdiZc V:111 H:204 F:24
208455 asl a                  A:0000 X:1000 Y:0400 S:02d7 D:0000 DB:00 nvmxdiZc V:111 H:207 F:24
208456 asl a                  A:0000 X:1000 Y:0400 S:02d7 D:0000 DB:00 nvmxdiZc V:111 H:211 F:24
208457 asl a                  A:0000 X:1000 Y:0400 S:02d7 D:0000 DB:00 nvmxdiZc V:111 H:214 F:24
208458 tax                    A:0000 X:1000 Y:0400 S:02d7 D:0000 DB:00 nvmxdiZc V:111 H:218 F:24
208459 lda $1000,x   [001000] A:0000 X:0000 Y:0400 S:02d7 D:0000 DB:00 nvmxdiZc V:111 H:221 F:24
20845c and #$0f00             A:8d81 X:0000 Y:0400 S:02d7 D:0000 DB:00 Nvmxdizc V:111 H:233 F:24
20845f asl a                  A:0d00 X:0000 Y:0400 S:02d7 D:0000 DB:00 nvmxdizc V:111 H:239 F:24
208460 asl a                  A:1a00 X:0000 Y:0400 S:02d7 D:0000 DB:00 nvmxdizc V:111 H:242 F:24
208461 asl a                  A:3400 X:0000 Y:0400 S:02d7 D:0000 DB:00 nvmxdizc V:111 H:246 F:24
208462 clc                    A:6800 X:0000 Y:0400 S:02d7 D:0000 DB:00 nvmxdizc V:111 H:249 F:24
208463 adc #$8000             A:6800 X:0000 Y:0400 S:02d7 D:0000 DB:00 nvmxdizc V:111 H:253 F:24
208466 sta $4352     [004352] A:e800 X:0000 Y:0400 S:02d7 D:0000 DB:00 Nvmxdizc V:111 H:259 F:24
208469 tdc                    A:e800 X:0000 Y:0400 S:02d7 D:0000 DB:00 Nvmxdizc V:111 H:268 F:24
20846a sep #$20               A:0000 X:0000 Y:0400 S:02d7 D:0000 DB:00 nvmxdiZc V:111 H:271 F:24
20846c lda #$1a               A:0000 X:0000 Y:0400 S:02d7 D:0000 DB:00 nvMxdiZc V:111 H:277 F:24
20846e sta $4354     [004354] A:001a X:0000 Y:0400 S:02d7 D:0000 DB:00 nvMxdizc V:111 H:330 F:24
208471 lda #$01               A:001a X:0000 Y:0400 S:02d7 D:0000 DB:00 nvMxdizc V:111 H:337 F:24
208473 sta $4350     [004350] A:0001 X:0000 Y:0400 S:02d7 D:0000 DB:00 nvMxdizc V:112 H:  1 F:24
208476 lda #$18               A:0001 X:0000 Y:0400 S:02d7 D:0000 DB:00 nvMxdizc V:112 H:  9 F:24
208478 sta $4351     [004351] A:0018 X:0000 Y:0400 S:02d7 D:0000 DB:00 nvMxdizc V:112 H: 13 F:24
20847b ldx #$0800             A:0018 X:0000 Y:0400 S:02d7 D:0000 DB:00 nvMxdizc V:112 H: 20 F:24
20847e stx $4355     [004355] A:0018 X:0800 Y:0400 S:02d7 D:0000 DB:00 nvMxdizc V:112 H: 26 F:24
208481 lda #$20               A:0018 X:0800 Y:0400 S:02d7 D:0000 DB:00 nvMxdizc V:112 H: 35 F:24
208483 sta $420b     [00420b] A:0020 X:0800 Y:0400 S:02d7 D:0000 DB:00 nvMxdizc V:112 H: 39 F:24
208486 inc $1d0c     [001d0c] A:0020 X:0800 Y:0400 S:02d7 D:0000 DB:00 nvMxdizc V:112 H: 47 F:24
[close]

However, VRAM was completely unchanged from before and after "sta $420b" was executed.

I spent a while trying to figure out what was wrong with the DMA transfer.  Eventually I just wrote a super slow loop to do the same transfer without the help of the DMA.  However, the VRAM continues to not change in any way.  See a partial trace of this below.

Spoiler
208430 phx                    A:0000 X:1000 Y:1d80 S:02dc D:0000 DB:7e nvMxdiZc V:218 H:259 F:55
208431 phy                    A:0000 X:1000 Y:1d80 S:02da D:0000 DB:7e nvMxdiZc V:218 H:267 F:55
208432 phb                    A:0000 X:1000 Y:1d80 S:02d8 D:0000 DB:7e nvMxdiZc V:218 H:274 F:55
208433 lda #$00               A:0000 X:1000 Y:1d80 S:02d7 D:0000 DB:7e nvMxdiZc V:218 H:314 F:55
208435 pha                    A:0000 X:1000 Y:1d80 S:02d7 D:0000 DB:7e nvMxdiZc V:218 H:318 F:55
208436 plb                    A:0000 X:1000 Y:1d80 S:02d6 D:0000 DB:7e nvMxdiZc V:218 H:323 F:55
208437 stz $2115     [002115] A:0000 X:1000 Y:1d80 S:02d7 D:0000 DB:00 nvMxdiZc V:218 H:329 F:55
20843a ldy #$0000             A:0000 X:1000 Y:1d80 S:02d7 D:0000 DB:00 nvMxdiZc V:218 H:337 F:55
20843d stz $1d0c     [001d0c] A:0000 X:1000 Y:0000 S:02d7 D:0000 DB:00 nvMxdiZc V:219 H:  3 F:55
208440 stz $1d0d     [001d0d] A:0000 X:1000 Y:0000 S:02d7 D:0000 DB:00 nvMxdiZc V:219 H: 11 F:55
208443 rep #$20               A:0000 X:1000 Y:0000 S:02d7 D:0000 DB:00 nvMxdiZc V:219 H: 19 F:55
208445 tya                    A:0000 X:1000 Y:0000 S:02d7 D:0000 DB:00 nvmxdiZc V:219 H: 24 F:55
208446 sta $2116     [002116] A:0000 X:1000 Y:0000 S:02d7 D:0000 DB:00 nvmxdiZc V:219 H: 28 F:55
208449 clc                    A:0000 X:1000 Y:0000 S:02d7 D:0000 DB:00 nvmxdiZc V:219 H: 37 F:55
20844a adc #$0400             A:0000 X:1000 Y:0000 S:02d7 D:0000 DB:00 nvmxdiZc V:219 H: 40 F:55
20844d tay                    A:0400 X:1000 Y:0000 S:02d7 D:0000 DB:00 nvmxdizc V:219 H: 46 F:55
20844e tdc                    A:0400 X:1000 Y:0400 S:02d7 D:0000 DB:00 nvmxdizc V:219 H: 50 F:55
20844f lda $1d0c     [001d0c] A:0000 X:1000 Y:0400 S:02d7 D:0000 DB:00 nvmxdiZc V:219 H: 53 F:55
208452 asl a                  A:0000 X:1000 Y:0400 S:02d7 D:0000 DB:00 nvmxdiZc V:219 H: 63 F:55
208453 asl a                  A:0000 X:1000 Y:0400 S:02d7 D:0000 DB:00 nvmxdiZc V:219 H: 67 F:55
208454 asl a                  A:0000 X:1000 Y:0400 S:02d7 D:0000 DB:00 nvmxdiZc V:219 H: 70 F:55
208455 asl a                  A:0000 X:1000 Y:0400 S:02d7 D:0000 DB:00 nvmxdiZc V:219 H: 74 F:55
208456 asl a                  A:0000 X:1000 Y:0400 S:02d7 D:0000 DB:00 nvmxdiZc V:219 H: 77 F:55
208457 asl a                  A:0000 X:1000 Y:0400 S:02d7 D:0000 DB:00 nvmxdiZc V:219 H: 81 F:55
208458 tax                    A:0000 X:1000 Y:0400 S:02d7 D:0000 DB:00 nvmxdiZc V:219 H: 84 F:55
208459 lda $1000,x   [001000] A:0000 X:0000 Y:0400 S:02d7 D:0000 DB:00 nvmxdiZc V:219 H: 88 F:55
20845c and #$001f             A:a582 X:0000 Y:0400 S:02d7 D:0000 DB:00 Nvmxdizc V:219 H: 99 F:55
20845f beq $847f     [20847f] A:0002 X:0000 Y:0400 S:02d7 D:0000 DB:00 nvmxdizc V:219 H:105 F:55
208461 lda $1000,x   [001000] A:0500 X:0000 Y:0400 S:02d7 D:0000 DB:00 nvmxdizc V:219 H:109 F:55
208464 and #$0f00             A:a582 X:0000 Y:0400 S:02d7 D:0000 DB:00 Nvmxdizc V:219 H:121 F:55
208467 asl a                  A:0500 X:0000 Y:0400 S:02d7 D:0000 DB:00 nvmxdizc V:219 H:127 F:55
208468 asl a                  A:0a00 X:0000 Y:0400 S:02d7 D:0000 DB:00 nvmxdizc V:219 H:130 F:55
208469 asl a                  A:1400 X:0000 Y:0400 S:02d7 D:0000 DB:00 nvmxdizc V:219 H:134 F:55
20846a tax                    A:2800 X:0000 Y:0400 S:02d7 D:0000 DB:00 nvmxdizc V:219 H:147 F:55
20846b adc #$0800             A:2800 X:2800 Y:0400 S:02d7 D:0000 DB:00 nvmxdizc V:219 H:151 F:55
20846e sta $1d0e     [001d0e] A:3000 X:2800 Y:0400 S:02d7 D:0000 DB:00 nvmxdizc V:219 H:157 F:55
208471 lda $1a8000,x [1aa800] A:3000 X:2800 Y:0400 S:02d7 D:0000 DB:00 nvmxdizc V:219 H:167 F:55
208475 sta $2118     [002118] A:001f X:2800 Y:0400 S:02d7 D:0000 DB:00 nvmxdizc V:219 H:179 F:55
208478 inx                    A:001f X:2800 Y:0400 S:02d7 D:0000 DB:00 nvmxdizc V:219 H:188 F:55
208479 inx                    A:001f X:2801 Y:0400 S:02d7 D:0000 DB:00 nvmxdizc V:219 H:191 F:55
20847a cpx #$1d0e             A:001f X:2802 Y:0400 S:02d7 D:0000 DB:00 nvmxdizc V:219 H:195 F:55
20847d bne $8471     [208471] A:001f X:2802 Y:0400 S:02d7 D:0000 DB:00 nvmxdizC V:219 H:201 F:55
208471 lda $1a8000,x [1aa802] A:001f X:2802 Y:0400 S:02d7 D:0000 DB:00 nvmxdizC V:219 H:206 F:55
208475 sta $2118     [002118] A:1f7e X:2802 Y:0400 S:02d7 D:0000 DB:00 nvmxdizC V:219 H:218 F:55
208478 inx                    A:1f7e X:2802 Y:0400 S:02d7 D:0000 DB:00 nvmxdizC V:219 H:227 F:55
208479 inx                    A:1f7e X:2803 Y:0400 S:02d7 D:0000 DB:00 nvmxdizC V:219 H:231 F:55
20847a cpx #$1d0e             A:1f7e X:2804 Y:0400 S:02d7 D:0000 DB:00 nvmxdizC V:219 H:234 F:55
20847d bne $8471     [208471] A:1f7e X:2804 Y:0400 S:02d7 D:0000 DB:00 nvmxdizC V:219 H:240 F:55
208471 lda $1a8000,x [1aa804] A:1f7e X:2804 Y:0400 S:02d7 D:0000 DB:00 nvmxdizC V:219 H:246 F:55
208475 sta $2118     [002118] A:7fb5 X:2804 Y:0400 S:02d7 D:0000 DB:00 nvmxdizC V:219 H:258 F:55
208478 inx                    A:7fb5 X:2804 Y:0400 S:02d7 D:0000 DB:00 nvmxdizC V:219 H:267 F:55
208479 inx                    A:7fb5 X:2805 Y:0400 S:02d7 D:0000 DB:00 nvmxdizC V:219 H:270 F:55
20847a cpx #$1d0e             A:7fb5 X:2806 Y:0400 S:02d7 D:0000 DB:00 nvmxdizC V:219 H:274 F:55
20847d bne $8471     [208471] A:7fb5 X:2806 Y:0400 S:02d7 D:0000 DB:00 nvmxdizC V:219 H:316 F:55
... etc.
[close]

So, what did I do wrong?  As I understand it, writing an address (in this case, 0000) to $00:2116, then writing data to $00:2118 / $00:2119 should make something change in the VRAM.


Some more information in case it is relevant:
- When this code is run, HDMA is currently active on channels 0, 1, 2, and 7.  The first three are targeting scroll registers.  The last one is pointed at $2131, whose meaning I do not know.
- There are sprites using this art data currently active in the OAM.
- My code is not waiting for a VBlank interrupt or anything; it is being called directly after I have swapped the characters (thus necessitating the sprites to be updated).

I've been hitting my head against this problem for about 3 days now, so any help would be greatly appreciated.

- Crow!

jonk

#1
I'm mostly ignorant about the SNES hardware details (though I've spent many years writing 6502 and 65816 code, decades back.) You haven't had a reply yet, so I'll offer only my initial thoughts on reading about your difficulties. Perhaps they will stimulate someone else to reply who knows the details.

The upshot of what I took from your writing is that you are following an example you see when the system initially loads up the VRAM but you aren't getting the results you expect. Instead, you are reading back the same old data that was there prior to your code executing. This triggers a few thoughts:


  • VRAM may only be writable during H-sync or V-sync once the video is enabled, because it is otherwise being used by the video system to drive the video display.
  • There may be a write-protect or write-enable port that controls VRAM access.
  • There may be address boundary registers that can be used to create barrier lines in the memory addressing which can work to block access to VRAM, depending on how they are set up.
  • Some code that follows the code you are mimicking otherwise turns off access to VRAM; or some code that precedes the code you are mimicking actively turns on access to VRAM for the duration and you didn't capture that code in your own attempts.

If this were my problem right now, I'd probably attempt to use V-sync (I think you called it VBlank) and see how that goes. (H-sync is probably "too short" of a time. So I wouldn't bother with that.) You said you didn't do that, so I'd recommend DOING THAT and seeing what happens then. That may be your problem and even if it is not the problem, it is worth eliminating as an issue given that you've already spent three days on this.
An equal right to an opinion isn't a right to an equal opinion. -- 1995, me
Saying religion is the source of morality is like saying a squirrel is the source of acorns.  -- 2002, me

Crow!

Doing some more digging eventually led me to that conclusion, and I have solved the problem.

All of the readily available SNES tutorials out there make zero reference to the fact that VRAM is locked except during V-Blank.  Fewer resources still actually dictate when that time is.  Anyway, in the case of FF4, the interrupt that fires when V-Blank begins actually lasts until long after V-Blank has ended, thus making your own code unable to touch VRAM, ever, at least when the combat engine is loaded.

Instead, there was a set of memory locations that the NMI handler would check to see if the program had requested DMA transfer to VRAM, then it would perform that transfer and mark the task as done.  So, long story short, this problem is resolved.

jonk

Good to hear the problem is solved. And interesting to hear how, too. Thanks for the update!!

While you say that the tutorials don't make any references suggesting that the VRAM isn't writable except during V-sync (and I have no source to dispute it), it's been a fact of most video system designs I've seen over more than a 35 year period. While it is possible (and it is done, from time to time) to use dual-ported RAM for video, it's not cheap nor easy. So there is usually only one active address/data bus and most of the time that bus is being used to support the video stream, with allowable moments to gain access to the bus during H- and V- sync periods.

I suspect, and perhaps someday you'll see it in the SNES as well, that you can ALSO use the H-sync period to achieve your goals. I'd bet it is there, if rarely used. However, the time period is VERY short. So, at best, I'd guess you can successfully write only a few bytes during each H-sync. Luckily, there are several hundred of them per field. So you probably could achieve several hundred bytes of transfer per field using the H-sync (assuming that H-sync is available as a signal for your software [I'd off-hand bet that it is; if not as an interrupt than perhaps as a bit you can poll.]) For NTSC, there are two interlaced fields per frame with 262.5 lines per field (and also therefore 60 fields per second.) So if you could only write 4 bytes per H-sync, that would still be over 60 kbyte/second on the H-sync alone. (Not counting what is achievable on the 60 much longer V-sync intervals per second.)

I'm disappointed to hear that the hardware and V-sync and H-sync is not well-documented. It's also interesting that you note the code tied to the V-sync event runs longer than the V-sync interval, itself. How did you manage to tell? (Just curious.) That would mostly mean to me that they would need to perform the memory writes to VRAM early in that interrupt event, in order to make sure the transfers were complete. A way I suspect you might otherwise have made sure your stuff got done, without needing to find out about their specialized memory location details, would be to directly intercept the interrupt yourself, perform your work, and then transfer to their V-sync handler after your work was over.

As a last note, given the fact that you point out the duration of the V-sync handler in FF4 is relatively long, it would mean that you'd miss out on some of the H-sync signals, anyway. Once the V-sync is complete, there will be a series of the H-sync pulses and if the V-sync interrupt handler is still busy for a while then there is no way you could manage to use all of the H-syncs that follow. So perhaps that's a reason why the H-sync isn't well documented. Though there really is no excuse for failing to document V-sync.
An equal right to an opinion isn't a right to an equal opinion. -- 1995, me
Saying religion is the source of morality is like saying a squirrel is the source of acorns.  -- 2002, me

Disch

FWIW Anomie's docs on this site explain this timing business.

Register overview/reference sheet (including what regs are accessible when) + a bazillion other things:
http://www.romhacking.net/documents/196/
Seriously, that doc is like the all-in-one SNES handbook and if you aren't using it you probably should be.


More detail is explained here:
http://www.romhacking.net/documents/199/
...but this builds on what's explained in the first doc, so check that one first.

Gemini

You don't necessarily need to wait for an NMI to write to vram. If you disable the screen drawing (or lock it) you can write there as soon as the GPU is idling. This will allow you to send as many tiles as you need during transitions to black, with no visual corruption either.

jonk

#6
Quote from: Gemini on August 20, 2018, 07:12:55 AM
You don't necessarily need to wait for an NMI to write to vram. If you disable the screen drawing (or lock it) you can write there as soon as the GPU is idling. This will allow you to send as many tiles as you need during transitions to black, with no visual corruption either.

That would make sense to me, too.

August 22, 2018, 02:33:57 PM - (Auto Merged - Double Posts are not allowed before 7 days.)

Quote from: Disch on August 20, 2018, 01:57:01 AM
FWIW Anomie's docs on this site explain this timing business.

Register overview/reference sheet (including what regs are accessible when) + a bazillion other things:
http://www.romhacking.net/documents/196/
Seriously, that doc is like the all-in-one SNES handbook and if you aren't using it you probably should be.


More detail is explained here:
http://www.romhacking.net/documents/199/
...but this builds on what's explained in the first doc, so check that one first.

I just did a quick scan of both documents for "sync" and didn't find much, at all. Four occurrences in the first case, one occurrence in the second case. I'll need to actually read all of the words in both documents to decide, for sure. But it doesn't bode well, given a quick glance with respect to the sync signals and how to use them well.

Thanks for the references, though.
An equal right to an opinion isn't a right to an equal opinion. -- 1995, me
Saying religion is the source of morality is like saying a squirrel is the source of acorns.  -- 2002, me

Disch

@jonk:

There's a legend at the very top of the document for a code that exists for every register:


REGISTERS
=========

Addr rw?vhfa Name
        bits

        Explanation

"Addr" is the address this register is mapped into the SNES memory space.
"Name" is the official and unofficial name of the register
"bits" is either 8 or 16 characters explicating the bitfields in this register.

The flags are:
rw?vhfa
||||||+--> '+' if it can be read/written at any time, '-' otherwise
|||||+---> '+' if it can be read/written at any time during force-blank
||||+----> '+' if it can be read/written at any time during H-Blank
|||+-----> '+' if it can be read/written at any time during V-Blank
||+------> Read/Write style: 'b'     => byte
||                           'h'/'l' => read/write high/low byte of a word
||                           'w'     => word read/write twice low then high
|+-------> 'w' if the register is writable for an effect
+--------> 'r' if the register is readable for a value or effect (i.e. not
            open bus).


Note it says "H-Blank" and "V-Blank" which is why a search for Sync came up empty.


Example:


2102  wl+?+- OAMADDL - OAM Address low byte


2102 is accessible via VBlank and Force Blank, HBlank is questionable/unknown, and not accessible during rendering.

jonk

Quote from: Disch on August 23, 2018, 10:36:17 PM
Note it says "H-Blank" and "V-Blank" which is why a search for Sync came up empty.

Got it! Thanks. Now I see 34 references and 21 references, respectively. Better!

By the way, now that I'm looking more, I googled and found, "H-DMA transfers are automatically invoked on H-Blank, each H-DMA is limited to a single unit (max 4 bytes) per scanline. This is commonly used to manipulate PPU I/O ports (eg. to change scroll offsets)" here: SNES hardware specifications. This suggests that there is some use of the H-sync (blank) signal.

Regardless, thanks very much! (I've directly generated interlaced and non-interlaced video output from microcontrollers using custom hardware and software I designed and wrote. Hence the modest interest.)
An equal right to an opinion isn't a right to an equal opinion. -- 1995, me
Saying religion is the source of morality is like saying a squirrel is the source of acorns.  -- 2002, me

pianohombre

I tried for a week or so to understand how OAM, VRAM, PPU, and registers work together. A few other members from this site even attempted to help me until they didn't like the way I worked so they demanded I leave them alone. For a couple of graphics the graphic would load partially, and then for whatever reason in a separate routine the remaining part of the graphic would load (this for a graphic less than 1kb). Also, when switching enemy sprites I tried to determine if there was some data about the sprites near the graphic table indicating VRAM size since this information was required to properly switch enemy sprites in the editor. Unfortunately, there was no way to determine (without manually counting the tiles how large the sprite was) and I couldn't figure out a formula based on other known information about the graphic to determine VRAM size....

Such a complicated piece of hardware for being a tiny gray box. I bless the programming lords daily that high-level languages are infinitely easier to work with than hex and assembly.
"Programming in itself is beauty,
whether or not the operating system actually functions." - Steve Wozniak