Video: https://www.youtube.com/watch?v=Cm3H5mO ... e=youtu.be
ROM attached.
SUMMARY:
So this is a short tech demo featuring Killer Instinct SNES graphics to see if the NES hardware can support mid-frame palette updates with sprites. Please play with Nintendulator or on the real hardware. Tested with an Everdrive cart. You may need to RESET the system several times to get a synchronization that doesn't glitch the sprites. I've determined that the hardware isn't quite capable of pulling off this trick, even with Blargg's NMI sync. The flicker effects look better on my CRT than on my LCD computer screen via emulators. Please test it on a CRT if possible. If nothing else, enjoy the nice sky palettes. There are two PRECISE spots on the screen where rendering can be turned off without completely destroying sprites, but unfortunately both of those spots are on very visible areas (left and right edge). You can experiment with different on/off points with this demo.
FEATURES:
- Characters as background tiles and sprites for largest playble characters possible on NES.
- Flicker effects (palette + pixel) for higher simulated color depth.
- Mid-screen palette updates (adjustable)
CONTROLS:
- Press 'A' and 'B' buttons to increase/decrease the write point where the palette update (grey bouncing lines) starts.
- Hold UP+A or DOWN+A to speed up the setting.
(The palette update point increments 1 cycle at a time (it alternates between 2 cycle nops, and 3 cycle ora's).
Not sure if there's a way to get it finer than that. With this you can experiment how the PPU updates/destroys data.)
- START on controller 1 disables/enables the flicker effect.
- SELECT on controller 1 alternates the fighter's palettes.
- 'A' and 'B' on controller 2 for alt background palettes. Hold A+B together for fast flashing.
- START and SELECT on controller 2 alternates sky palette
- SELECT+START resets
NOTES:
- The code only synchronizes every 3 lines, so maybe if the sky gradient only updated every 3rd line it would be less prone to destroying sprite data. You can see the write point where the screen is turned off by the light grey horizontal lines on the right side of the screen. You can use different colors, but I chose grey to be less distracting. I'm updating the main background palette, so when the screen is off and that palette ($3F00) is updated, VRAM then points to the next palette ($3F01) which is the grey color.
- Blarggs NMI sync helps, but also seems to screw up the dot crawl filter, causing a wavy strobe like effect to the colors (mainly the fire character). This will not be apparent on an emulator though.
SPECIAL THANKS:
loopy and tokumaru - for mid-frame scroll + palette update info
Blargg - for NMI sync
MacBee - flicker effect inspiration
CODE:
Here's how the palette updates. This is the fastest way I could come up with. The first byte of $3F00 can be set BEFORE the screen is turned off. This routine is in RAM so it can be easily altered/updated with new lines/palettes. Sorry that I can't seem to get the formatting to align.
Code:
RAM_routine_PAL_SCROLL_update: ; copy this into $0300
ldx #$00 ; palette color - RELOADED
ldy #%00011110 ; to turn the screen back ON.
; bit 7-5 = color emphasis, bit 4 = sprite on, bit 3 = bkg on,
; bit 2/1 = edge clipping, bit 0 = greyscale
lda #$3F ; first byte of $3F00 vram address
sta $2006
lda #$00 ; this will both set the second byte of $3F00 and turn off the screen
;----------------
sta $2001 ; screen OFF (disables rendering)
sta $2006 ; address set -- you can technically set this with sprites still on, but writing to 2007 won't work.
stx $2007 ; palette write
sty $2001 ; screen ON 4*3= 12 cycles, 4*4 = 16 cycles
;----------------
lda #01 ; 0yyyNNYY <- fine y_scroll. oh so fine
sta $2006 ; 0yyyNNYY YYYXXXXX <- this is the full scroll data for reference
lda #02 ; window_pos_y
sta $2005 ; Y scroll YYYYYyyy
sta $2005 ; X scroll XXXXxxx (new data not needed, as will be reset with another 2005 write later)
lda #04 ; YYYXXXXX
sta $2006 ; 0yyyNNYY YYYXXXXX <- this is the full scroll data for reference
; with this second write to 2006 the scroll registers are finally updated
; (after being corrupted by the palette write)
lda #03 ; window_pos_x <- this fixes the x_scroll. it can be set any time
sta $2005
bit $2002 ; PPU Status (to reset latch) <- not necessarily needed...
rts
ldx #$00 ; palette color - RELOADED
ldy #%00011110 ; to turn the screen back ON.
; bit 7-5 = color emphasis, bit 4 = sprite on, bit 3 = bkg on,
; bit 2/1 = edge clipping, bit 0 = greyscale
lda #$3F ; first byte of $3F00 vram address
sta $2006
lda #$00 ; this will both set the second byte of $3F00 and turn off the screen
;----------------
sta $2001 ; screen OFF (disables rendering)
sta $2006 ; address set -- you can technically set this with sprites still on, but writing to 2007 won't work.
stx $2007 ; palette write
sty $2001 ; screen ON 4*3= 12 cycles, 4*4 = 16 cycles
;----------------
lda #01 ; 0yyyNNYY <- fine y_scroll. oh so fine
sta $2006 ; 0yyyNNYY YYYXXXXX <- this is the full scroll data for reference
lda #02 ; window_pos_y
sta $2005 ; Y scroll YYYYYyyy
sta $2005 ; X scroll XXXXxxx (new data not needed, as will be reset with another 2005 write later)
lda #04 ; YYYXXXXX
sta $2006 ; 0yyyNNYY YYYXXXXX <- this is the full scroll data for reference
; with this second write to 2006 the scroll registers are finally updated
; (after being corrupted by the palette write)
lda #03 ; window_pos_x <- this fixes the x_scroll. it can be set any time
sta $2005
bit $2002 ; PPU Status (to reset latch) <- not necessarily needed...
rts