News: 11 March 2016 - Forum Rules
Current Moderators - DarkSol, KingMike, MathOnNapkins, Azkadellia, Danke

Author Topic: NES HBlank  (Read 2975 times)

Jiggers

  • Sr. Member
  • ****
  • Posts: 274
    • View Profile
NES HBlank
« on: November 17, 2019, 02:58:03 am »
Hmm... what am I missing here? Fiddling around with FF1's code, as usual for me.

The test is to change a single colour of the palette midway through screen drawing. The hope is that I can figure out a way to swap character palettes in battle so that stoned characters can be drawn grey, then have their palette swap back to normal after displaying their sprite. Or find something else cool to do.

Code: [Select]
WaitForVBlank:
    LDA $2002      ; check VBlank flag
    BPL @Wait      ; if off, VBlank hasn't occured yet, so jump to the wait
                   ;  otherwise... whatever code occured since the last frame took longer than
                   ;  1 frame... so...

    LDA #0         ; waste time in this loop for a while...
  @Loop:
      SEC          ; the reason for this is because the game doesn't want NMI to fire during VBlank
      SBC #$01     ;  because then you'd have less than a full VBlank for drawing next frame
      BNE @Loop    ;  Of course... just reading $2002 above will prevent this from happening, so this isn't
                   ;  really necessary, but it doesn't hurt.

  @Wait:
    LDA soft2000   ; Load desired PPU state
    ORA #$80       ; flip on the Enable NMI bit
    STA $2000      ; and write it to PPU status reg

    LDA #44    ; JIGGERS - set scanline #44 as the point to change colour
    STA $5203

OnIRQ:             ; IRQs point here, but the game doesn't use IRQs, so it's moot   
@LoopForever:
    LDA $5204 ; JIGGERS - high bit set when scanling #44 is being drawn
    BPL @LoopForever
    LDA #0
    STA $2001       ; turn off PPU
    ;LDA $2002      ; - is this necessary?
    LDA #$3F        ; set PPU Address to $3F15
    STA $2006
    LDA #$15
    STA $2006
    LDA #$2A
 ;   LDX #$0B       ; waste cycles until Hblank - necessary if PPU is off?
 ; : DEX
 ;   BNE :-
    STA $2007
    LDA #$00       ; reset scroll to 0
    STA $2005
    STA $2005
    STA $5203      ; Clear trigger so this doesn't happen in another 44 scanlines
    LDA #$1E
    STA $2001      ; turn on PPU

   JMP @LoopForever     ; then loop forever! (or really until the NMI is triggered)



Nothing changes by enabling the parts I commented as "necessary?"

My two guesses: The $2006 writes also need to be in Hblank ... Or something about IRQs that I don't understand because I've never had to deal with them, because FF1?

I feel like I grasp the concept and can pull it off, but I'm unaware of any steps I'm missing.
I know exactly what I'm doing. I just don't know what effect it's going to have.

Disch

  • Hero Member
  • *****
  • Posts: 2746
  • NES Junkie
    • View Profile
Re: NES HBlank
« Reply #1 on: November 17, 2019, 03:40:46 pm »
I do not recommend this approach.  It is fraught is big problems.  I don't even think it's possible.


1)  HBlank is extremely short, and moreover, is largely undetectable.  You basically just have to time it out in cycles based on when you expect the IRQ to fire, but even then there's a lot of wiggle room because depending on the instruction that was being executed at the time of the IRQ, there's like a 2-7 cycle window, so you can't get it exactly perfect every time.

There are some tricks to sync to HBlank but they're questionable.  Micro Machines famously polls $2004 to exploit a PPU quirk to help it find HBlank, but I'm a little fuzzy on the details of how it gets it to work.  From what I understand it looks for a large patch of $FFs to be read back from $2004 which is an indication that HBlank has just ended.  But again, very sketchy and I don't really recommend it.  If you want to explore this, reading $2004 mid-frame is an absolute monster and I barely understand it myself.  Here's a reference page as to what you get back when you read it:  https://wiki.nesdev.com/w/index.php/PPU_sprite_evaluation

IIRC MMC5 IRQs fire at the very end of the scanline, which means you've already missed HBlank and will have to wait a full scanline to reach it again.  Probably the most realiable thing to do would be to wait ~76 cycles after the IRQ trips before shutting off the PPU, which would put you right around HBlank on the following line.  Earlier than that might not hurt, as the right side of the screen is black anyway so if you shut the PPU off early you'll just chop off a few pixels that you wouldn't be seeing anyway.




2)  $2006 changes the PPU address, but the PPU address is ALSO used as the screen scroll.  You cannot just write to $2005 to reset the scroll back to what it was.  It's a LOT more complicated than that.  The PPU is basically modifying $2006 in real time as the screen is drawn.  You'll need to calculate what $2006 should be based on what scanline you're on, and write that.

But even just writing to $2006 isn't enough, because there are some scroll bits that can only be set by $2005 that $2006 zeros out when you write to it.  It's a total mess.  To accomplish this feat you have to write to $2005 and $2006 in a weird pattern to properly set the scroll.  The Nesdev wiki has a little outline on how to do it:

https://wiki.nesdev.com/w/index.php/PPU_scrolling#Split_X.2FY_scroll

But note that this is 4 STAs, and probably also 4 LDAs.  That's 24 cycles, which is at least like 70% of your HBlank time.  There's no way you have enough time to turn the screen off, change the palette, correct the scroll, then turn the screen back on all before HBlank ends.



3)  Turning off the screen mid frame is necessary if you want to touch the palette.  But it will also completely confuse the MMC5 IRQ counter making it unreliable for the rest of the frame.  It's probably still usable and will resync once the PPU gets turned back on, but the scanline count will be reset and there is NO WAY I would trust an emulator to simulate it properly.


4)  Turning off the screen is also going to trash sprites for the next line, as the PPU uses HBlank to fetch CHR for sprites on the following line (as well as some other stuff).  So to do this safely, you'd need to leave sprites off for 1 scanline after adjusting the palette, and then turn them on afterwards.



TL;DR:  Changing the palette mid-frame is not feasible.  I only know of like 2 games (and some tech demos) that actually do it, and all of them leave the screen off for several consecutive scanlines to pull it off.

Bregalad

  • Hero Member
  • *****
  • Posts: 2650
    • View Profile
Re: NES HBlank
« Reply #2 on: November 17, 2019, 04:20:40 pm »
TL;DR:  Changing the palette mid-frame is not feasible.  I only know of like 2 games (and some tech demos) that actually do it, and all of them leave the screen off for several consecutive scanlines to pull it off.
I'm the author of one of such tech-demoes. Basically, it's complex to do, only work fine if you change the BG palette entry, and still there's glitches on the right side of the screen.

Here you have another interesting thread discussing the matter.

Also, as Dish says, I wouldn't count on emultors to emulate mid-frame palette change correctly and even less to emulate correct behaviour of MMC5 features such as IRQ counter, ExNametable, etc... after turning the rendering off and then on again the same frame.

If you're going to change the palette mid-frame, I'd do it only once per frame if possible, for example between the battle scene and the HUD, and I wouldn't use the scanline counter at all after the switch. Also test on real hardware, which is sparse when it comes to MMC5.

Quote
But even just writing to $2006 isn't enough, because there are some scroll bits that can only be set by $2005 that $2006 zeros out when you write to it.  It's a total mess.  To accomplish this feat you have to write to $2005 and $2006 in a weird pattern to properly set the scroll.  The Nesdev wiki has a little outline on how to do it:
It's not that complicated - especially if you don't use full scrolling below the split. If you're scrolling to a known place such as a fixed-address HUD it's relatively simple and just writing the NT adress of the HUD to $2006 and this will work.

Jiggers

  • Sr. Member
  • ****
  • Posts: 274
    • View Profile
Re: NES HBlank
« Reply #3 on: November 21, 2019, 03:52:34 pm »
2)  $2006 changes the PPU address, but the PPU address is ALSO used as the screen scroll.  You cannot just write to $2005 to reset the scroll back to what it was.  It's a LOT more complicated than that.  The PPU is basically modifying $2006 in real time as the screen is drawn.  You'll need to calculate what $2006 should be based on what scanline you're on, and write that.

But even just writing to $2006 isn't enough, because there are some scroll bits that can only be set by $2005 that $2006 zeros out when you write to it.  It's a total mess.  To accomplish this feat you have to write to $2005 and $2006 in a weird pattern to properly set the scroll.  The Nesdev wiki has a little outline on how to do it:

https://wiki.nesdev.com/w/index.php/PPU_scrolling#Split_X.2FY_scroll

But note that this is 4 STAs, and probably also 4 LDAs.  That's 24 cycles, which is at least like 70% of your HBlank time.  There's no way you have enough time to turn the screen off, change the palette, correct the scroll, then turn the screen back on all before HBlank ends.

That's the step I was missing... It does seem like it would be impossible. It isn't just one colour to change, but 3... then resetting it after the sprite is drawn as well. But at least there's 1-2 lines between the sprites, they're on known and unchanging scanlines, and there's lots of black space on the right side. My idea was to read from a table and write the necessary stuff to where it needs to go... So more than 4 LDA and STAs, with some INX between...

Quote
3)  Turning off the screen mid frame is necessary if you want to touch the palette.  But it will also completely confuse the MMC5 IRQ counter making it unreliable for the rest of the frame.  It's probably still usable and will resync once the PPU gets turned back on, but the scanline count will be reset and there is NO WAY I would trust an emulator to simulate it properly.

Mesen is pretty trustworthy on this though. Before overwriting $5203 again, it was doing the thing every #44 scanlines.

Anyway--you've convinced me not to pursue this exact idea... but I have another one. If I can change ONE background colour, then the turn counter box can go from blue to grey after the battle background is drawn, then go back to blue after the sprites are drawn...

If I can figure out how to use CHR swapping to get even half the background tiles swapped out with CHR Mode 2, then I can load up the character graphics in place of the letters, draw them where the sprites would be (have to undo the 3d effect of the characters though) and swap the letters back in for the message boxes... Maybe? But where is the CHR stuff stored when its not currently being swapped in? Where does it swap OUT to? RAM or ROM? Or am I not understanding this at all? (I am reading up on this at the moment, so kind of answering my own questions...)

Is it possible to swap between CHR-RAM and CHR-ROM, or is that something that can only be decided on reset?
« Last Edit: November 21, 2019, 04:30:31 pm by Jiggers »
I know exactly what I'm doing. I just don't know what effect it's going to have.

Disch

  • Hero Member
  • *****
  • Posts: 2746
  • NES Junkie
    • View Profile
Re: NES HBlank
« Reply #4 on: November 21, 2019, 09:03:20 pm »
Is it possible to swap between CHR-RAM and CHR-ROM, or is that something that can only be decided on reset?

TL;DR:  No, you can't swap between CHR-ROM and CHR-RAM, you're pretty much stuck with CHR-RAM.


Whether a chip is ROM or RAM depends on the physical chip on cartridge -- or how that chip is simulated, if you're talking about an emulator.  I don't believe any MMC5 boards used multiple CHR chips, and I would be absolutely stunned if any emulator simulated such a board.  So you pretty much have to choose either RAM (which FF1 uses) or ROM for the entire game.

It is theoretically possible to change FF1 to use CHR-ROM but it would be a TON of work and you'd have to get REALLY creative in spots.  Additionally, some areas of the game (specifically the dynamic map drawing the B+Select screen and the "The End" sequence at the end of the game) are pretty much tricks that rely on CHR-RAM specifically, so you'd have to gut those things completely or drastically change how they're performed.

Basically --- while I won't say it's impossible, a CHR-ROM conversion is very impractical.  Especially if you're just wanting to do a minor graphic effect.


Jiggers

  • Sr. Member
  • ****
  • Posts: 274
    • View Profile
Re: NES HBlank
« Reply #5 on: November 21, 2019, 09:25:03 pm »
Boo. That's the conclusion I had come to... I thought out some pros and cons regarding converting in the last message on the project thread, but... didn't think about those screens.

More and more I'm coming to the conclusion that for 3 character palettes, I'll just have to get rid of Stone entirely. Or the greying effect of it.

Thanks again for all the info, too!

I might just do the background colour changing trick just to see if I can do it, even if I have no need for it...
I know exactly what I'm doing. I just don't know what effect it's going to have.

Bregalad

  • Hero Member
  • *****
  • Posts: 2650
    • View Profile
Re: NES HBlank
« Reply #6 on: November 26, 2019, 05:22:48 am »
Boo. That's the conclusion I had come to... I thought out some pros and cons regarding converting in the last message on the project thread, but... didn't think about those screens.

More and more I'm coming to the conclusion that for 3 character palettes, I'll just have to get rid of Stone entirely. Or the greying effect of it.

Thanks again for all the info, too!

I might just do the background colour changing trick just to see if I can do it, even if I have no need for it...

Aren't stoned characters moved to the BG in the original FF1 ? Why don't you keep it that way in your hack ? It makes most sense.
  • Stoned characters won't move
  • There's a gray-ish colour anyway for the HUD
An alternate option I'd consider is reserve one sprite palette for each character, 4 palettes, 4 characters, works great. The problem is with other sprites - hand cursors, weapons, etc...

Quote
That's the step I was missing... It does seem like it would be impossible. It isn't just one colour to change, but 3...
This is not an issue if you do it once per frame, for example between the battle scene and the HUD.

Disch

  • Hero Member
  • *****
  • Posts: 2746
  • NES Junkie
    • View Profile
Re: NES HBlank
« Reply #7 on: November 26, 2019, 01:22:35 pm »
Aren't stoned characters moved to the BG in the original FF1 ? Why don't you keep it that way in your hack ? It makes most sense.

That's not how it's done in the original game.  Though this is a good solution.

Like you mention, you can use the palette for the menu borders which already has grey in it, and to save on tile space you could have a common graphic that is used by all classes.  This seems like the most viable solution.

Jiggers

  • Sr. Member
  • ****
  • Posts: 274
    • View Profile
Re: NES HBlank
« Reply #8 on: November 26, 2019, 02:11:03 pm »
FF3 does that.



It does something else that I'm wondering if the MMC5 can do.

Code: [Select]
FB8E 8 145 Mapper Register Write $5114 $AE
FB98 8 199 Mapper Register Write $5115 $AF
FB8E 9 62 Mapper Register Write $5114 $B4
FB98 9 116 Mapper Register Write $5115 $B5
FB82 151 30 IRQ
FB38 151 87 PPU Register Write $2000 $98
FB3D 151 108 PPU Register Write $2005 $00
FB42 151 129 PPU Register Write $2005 $00 2nd Write
FB45 151 141 Mapper Register Read $5204 $C0
FB8E 151 312 Mapper Register Write $5114 $B6
FB98 152 25 Mapper Register Write $5115 $B7
89B8 152 277 Mapper Register Write $5115 $B8
FB8E 162 29 Mapper Register Write $5114 $B4
FB98 162 83 Mapper Register Write $5115 $B5

That's a normal battle frame. I can't find anything about the $5114 or $5115 writes with that game's mapper, but the effect is that the sprite graphics table is drawn as background tiles. The only thing FF3 doesn't do is make sure the 1 blue colour in the menu palette is grey and switch it out before scanline 151 as well.

This is not an issue if you do it once per frame, for example between the battle scene and the HUD.

The idea was to do it almost dozens of times per frame. If Character 1 is stoned, using Palette 2, then change all of Palette 2's colours to greys, then back once it gets past the character sprite... in case there's another character using Palette 2's colours as well. Which is insane. >.<

So I'm really hoping there's a way to set the MMC5 to draw background tiles with the $1000-$1FF0 tiles and a quick toggle for that.
« Last Edit: November 26, 2019, 02:22:13 pm by Jiggers »
I know exactly what I'm doing. I just don't know what effect it's going to have.

Disch

  • Hero Member
  • *****
  • Posts: 2746
  • NES Junkie
    • View Profile
Re: NES HBlank
« Reply #9 on: November 26, 2019, 06:16:08 pm »
I'm really hoping there's a way to set the MMC5 to draw background tiles with the $1000-$1FF0 tiles and a quick toggle for that.

The NES does that natively.  It's bits 3 and 4 of $2000

http://wiki.nesdev.com/w/index.php/PPU_registers#PPUCTRL

Bregalad

  • Hero Member
  • *****
  • Posts: 2650
    • View Profile
Re: NES HBlank
« Reply #10 on: November 27, 2019, 05:38:05 am »
That's not how it's done in the original game.  Though this is a good solution.
Oh so I confused with FF3 (and possibly 2).

Quote
The idea was to do it almost dozens of times per frame. If Character 1 is stoned, using Palette 2, then change all of Palette 2's colours to greys, then back once it gets past the character sprite... in case there's another character using Palette 2's colours as well. Which is insane.
The main problem here (other than the IRQ counter being corrupted by the forced blanking) is that this will corrupt the BG, and affect enemies shown with the BG on the left side of the screen.