Midscreen scroll/parallax/character bank change glitching?

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

sdwave
Posts: 27
Joined: Mon Feb 13, 2012 1:20 am

Midscreen scroll/parallax/character bank change glitching?

Post by sdwave »

I'm trying to remove the glitching I see at the edges of scanlines I see when setting scroll values
mid hblank in my game. I was wondering if you guys had any suggestions, I included my irq routine and an explanation of how it works.

I included my mmc3 irq routine (multiLineIrq: is the entry point) to see if any of you could provide some pointers as to how I can fix this.

The basic jist is that there is are 4 tables of 2006,2005,2005,2006 for the register writes that store my scroll values for each of the N mmc3 scanline irqs (indexed by the number of irqs hit so far per screen).

IRQ_SCROLL_2006_FIRST
IRQ_SCROLL_2005_Y
IRQ_SCROLL_2005_X
IRQ_SCROLL_2006_SECOND

There is a table of # line deltas between irqs: R_MMC_IRQ_LINE0

Nametable layout:
Foreground layer: page 0
Background layer: page 1
Status bar: last 2 character rows of page 1


Here is what happens per frame:

24 lines (status bar size) after vblank ends:

The first irq set is change part of the palette from gray (for the status bar) to the colors used on the play screen. Also the character set is changed to a blank page to blank the screen and remove some glitching.

2 lines later

The second irq sets the 2006,5,5,6 for the playscreen (can be parallaxing background or foreground), and sets the character set for the playscreen. During the 2006,5,5,6 setting background drawing using the ppumask is disabled to ensure the scroll registers are set properly. This is also where sprite drawing gets enabled for the first time for the screen.

The next (N-3) irqs set 2006,5,5,6 for the different bands of scrolling foreground/background "layers". Also there is an irq for a Crystalis style single screen mirroring.

The final irq depends on the mode.
In play mode about 8 lines above the visible screen bottom, the final irq changes the character set to a blank page to blank the screen and remove some artifacts.

In conversation mode the final irq changes the character set to the alphanumeric font pages, the first 3 colors to grays for the font rendering, and changes the sprite bank to the portrait.

-Sedwave / Hiatus Ward ( http://www.nesworld.com/homebrew/hiatwrd0.zip)

Code: Select all

; ===============================================
endPaletteIrq:
    pla
    tay	;pull y
    pla
    tax	;pull X
    pla	;pull a
    plp	; pull flags
    rti

; ===============================================
setBlankingAndFinishIrq:
    setMMC3ChrBankLo 0, #K_EMPTY_CHR_PAGE
    setMMC3ChrBankLo 1, #K_EMPTY_CHR_PAGE
    setMMC3ChrBankLo 2, #K_EMPTY_CHR_PAGE
    setMMC3ChrBankLo 3, #K_EMPTY_CHR_PAGE
    jmp endIrq


; ===============================================
setPaletteIrq:
    lda R_MMC_IRQ_LINE0 +1   ; setup next irq 
    setMMC3ScanlineIrq;
    inc R_MMC_NUM_IRQ_HIT

    nop
    nop
    nop 
    nop 
    nop

    jsr setPlayScreenPaletteIrq

    ;blank screen but still allow mmc3 irq counter by setting to empty character page 
    setMMC3ChrBankLo 0, #K_EMPTY_CHR_PAGE
    setMMC3ChrBankLo 1, #K_EMPTY_CHR_PAGE
    setMMC3ChrBankLo 2, #K_EMPTY_CHR_PAGE
    setMMC3ChrBankLo 3, #K_EMPTY_CHR_PAGE

    lda #(PPU_MASK_SPR_VIS | PPU_MASK_BKG_VIS )
    sta PPU_MASK

    jmp endPaletteIrq

;note split irq does not turn off ppu
setSplitIrq:
    dex

    lda IRQ_SCROLL_2006_FIRST, x 
    sta PPU_ADDR
    lda IRQ_SCROLL_2005_Y, x
    sta PPU_SCRL
    lda IRQ_SCROLL_2005_X, x
    sta PPU_SCRL
    lda IRQ_SCROLL_2006_SECOND, x
    sta PPU_ADDR
    jmp endIrq

; ===============================================
; Interrupt handlers
; ===============================================
irq:
multiLineIrq:
    php	;push flags
    pha	;push a
    txa
    pha	;push x
    tya
    pha	;push y

    lda #0	
    sta MMC3_IRQ_ACK

    ldx R_MMC_NUM_IRQ_HIT
    beq setPaletteIrq
    cpx R_CONVO_IRQ_NUM
    bne notConvoIrq 
    jmp convoIrq
notConvoIrq:

    inx
    cpx R_MMC_NUM_IRQ 
	
    bne @notFinalIrq
    jmp setBlankingAndFinishIrq
@notFinalIrq:

    lda R_MMC_IRQ_LINE0, x       ; accum contains number of scanlines until next irq, setup next irq 
    setMMC3ScanlineIrq;

@noNewIrq:

    ;how many irq previously hit	
    ldx R_MMC_NUM_IRQ_HIT
    cpx R_SPLIT_IRQ_NUM
    beq setSplitIrq
    dex  ;  irq #1 index 0, irq#2 index 1, etc...

    ; between 2 and x nops stops shaking
    ; 2 seems best on real hardware
    nop
    nop

    ;note, setting scroll value to different X value midscreen requires PPU
    ; drawing to be disabled so that internal X counter does not interfere with
    ; setting scroll address and values
    lda #PPU_MASK_SPR_VIS
    sta PPU_MASK
postBlankIrq:
    lda IRQ_SCROLL_2006_FIRST, x 
    sta PPU_ADDR
    lda IRQ_SCROLL_2005_Y, x
    sta PPU_SCRL
    lda IRQ_SCROLL_2005_X, x
    sta PPU_SCRL
    lda IRQ_SCROLL_2006_SECOND, x
    sta PPU_ADDR

    lda #(PPU_MASK_SPR_VIS | PPU_MASK_BKG_VIS )
    sta PPU_MASK

    ldx R_MMC_NUM_IRQ_HIT
    cpx #1
    bne noSetupPlayscreenChar
    setMMC3ChrBankLo 0, LEVEL_DATA_CHR_PAGE_0
    setMMC3ChrBankLo 1, LEVEL_DATA_CHR_PAGE_1
    setMMC3ChrBankLo 2, LEVEL_DATA_CHR_PAGE_2
    setMMC3ChrBankLo 3, LEVEL_DATA_CHR_PAGE_3
    nop
    nop
    nop
    nop
    nop

noSetupPlayscreenChar:
endIrq:
    inc R_MMC_NUM_IRQ_HIT

    pla
    tay	;pull y
    pla
    tax	;pull X
    pla	;pull a
    plp	;pull flags

    rti
User avatar
thefox
Posts: 3139
Joined: Mon Jan 03, 2005 10:36 am
Location: Tampere, Finland
Contact:

Re: Midscreen scroll/parallax/character bank change glitchin

Post by thefox »

sdwave wrote:

Code: Select all

; ===============================================
; Interrupt handlers
; ===============================================
irq:
multiLineIrq:
    php	;push flags

...

    plp	;pull flags

    rti
Not related to your actual question (I don't have time to go through the code right now), but a small detail: you don't need to push and restore the flags, 6502 will do that automatically.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Midscreen scroll/parallax/character bank change glitchin

Post by tokumaru »

I haven't looked at the source, but I have something I'd like to comment:
sdwave wrote:During the 2006,5,5,6 setting background drawing using the ppumask is disabled to ensure the scroll registers are set properly.
There's no need to disable background rendering if you make sure that the last $2006 write happens during HBlank. In fact, disabling rendering only increases the chances of visual glitches, since you have more instructions to execute during HBlank. When you use the $2006/5/5/6 trick for resetting the scroll, only the last 2 writes have to fall inside the horizontal blank, because the first 2 don't affect rendering at all. So get rid of the disabling/enabling of the background on scroll changes and make sure that the last 2 scroll writes take place during HBlank (that's 8 cycles to fit in a window of 28 cycles, it's not hard at all).
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Re: Midscreen scroll/parallax/character bank change glitchin

Post by Bregalad »

Also, you should really write to
$2005, $2005, $2005, $2006 to make your life easier (I think). This way you only have to write 3 different values instead of 4 :

$2005 := HScroll
$2005 := VScroll
$2005 := HScroll
$2006 := (VScroll << 3) | ((HScroll >> 3) & 0x7)
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Midscreen scroll/parallax/character bank change glitchin

Post by tokumaru »

You need the first $2006 write to set the name table.
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Re: Midscreen scroll/parallax/character bank change glitchin

Post by Bregalad »

No - a write to $2000 can set the nametable if necessarily (and in many cases this might not be necessarily). That's what the $2000 register was originally meant for, folks.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Midscreen scroll/parallax/character bank change glitchin

Post by tokumaru »

Bregalad wrote:No - a write to $2000 can set the nametable if necessarily (and in many cases this might not be necessarily). That's what the $2000 register was originally meant for, folks.
Sure, and during normal scroll operation I always recommend that $2000 be used, but since this is the tricky version of setting the scroll, why put an extra write that will just increase the time it takes to fully update the scroll? I mean, you can do it with 4 writes, why use 5? There's no real advantage, as forming the first $2006 isn't complicated at all... Only the name table bits need to be correct in that byte, the rest will be overwritten by the following writes.
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Re: Midscreen scroll/parallax/character bank change glitchin

Post by Bregalad »

There is a real advantages as the values can be precalculated in A, X, and Y and be updated in such a way that :

Code: Select all

   sta $2005
   stx $2005
   sta $2005
   sty $2006
(for example)
Also, exept for the last $2006 writes, you write directly the scroll value you want and no extra calculations are required, which is a clear advantage.

On the other way I see no point in doing a 6, 5, 5, 6 write, which is overcomplicated and has no real benefit over (0), 5, 5, 5, 6.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Midscreen scroll/parallax/character bank change glitchin

Post by tokumaru »

Bregalad wrote:There is a real advantages as the values can be precalculated in A, X, and Y
Since only the last 2 writes are under timing constraints, this is of very little relevance. You can't preload the $2000 value just like you can't preload the first $2006 value, so you still need 4 values (of which only 3 can be preloaded), no matter the method.
Also, exept for the last $2006 writes, you write directly the scroll value you want and no extra calculations are required
The same goes for the 6/5/5/6 way. Like I said in the previous message, only the name table bits must be correct in the first $2006 write, because the rest is overwritten by the subsequent writes. The trouble of shifting the name table bits into a $2006 byte is nearly the same as that of pushing them into a $2000 byte, you just need a couple more shifts ($2000 has the name table bits at %------NN while $2006 needs them at %----NN--). That's 2 more bytes of code and 4 cycles, which is still less than the 3 bytes of the extra STA $2000, that also takes 4 cycles.
On the other way I see no point in doing a 6, 5, 5, 6 write, which is overcomplicated
Do you really find ASL ASL complicated? It's actually simpler, since none of the other bits matter and you don't even need to clear A before shifting the name table values in. To form a proper $2000 byte on the other hand, you need to make sure that the upper bits contain correct PPU configuration values (pattern table addresses, sprite size, NMI...).
and has no real benefit over (0), 5, 5, 5, 6.
It's faster. Not by much, but it is. It's the 0, 5, 5, 5, 6 way that offers no benefit over the 6, 5, 5, 6 way.

I'm not saying that you shouldn't use the longer method, if that's the one you're comfortable with, go for it! But saying that it's objectively better than the faster method and calling that one overcomplicated is bogus!
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Re: Midscreen scroll/parallax/character bank change glitchin

Post by Bregalad »

Perhaps, but the point is, changing the scroll value multiple times in a row and rely on the last one to be used is dirty, and usually leads to more graphical glitches than using consistent values.
Do you really find ASL ASL complicated?
Yes, it almost made my head explode ! :mrgreen:
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Midscreen scroll/parallax/character bank change glitchin

Post by tepples »

Writing to $2000 requires having the other bits correct (background pattern table base address, sprite size and pattern table base address if 8x8, and NMI). Writing to $2005/first needs to be done in horizontal blank if the fine X scroll ($2005/first bits 2-0) will be changing at all. So I'm with tokumaru: $2006 first is probably the best option.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Midscreen scroll/parallax/character bank change glitchin

Post by tokumaru »

tepples wrote:Writing to $2005/first needs to be done in horizontal blank if the fine X scroll ($2005/first bits 2-0) will be changing at all.
Oh yeah, I didn't even catch that. This means that all 4 writes would have to fall within HBlank, so that's 16 cycles to fit into 28 (as opposed to just 8 with the other method)... Still doable of course, but why would you want to reduce the amount of time you have left to perform other effects (like color emphasis, for example).
Bananmos
Posts: 551
Joined: Wed Mar 09, 2005 9:08 am
Contact:

Re: Midscreen scroll/parallax/character bank change glitchin

Post by Bananmos »

24 lines (status bar size) after vblank ends:

The first irq set is change part of the palette from gray (for the status bar) to the colors used on the play screen. Also the character set is changed to a blank page to blank the screen and remove some glitching.
How do you detect the end of the status bar if the first IRQ is set *after* it?

If you're using timed code then that's likely to be the source of the jitter, as DPCM sample playback can really screw up your timing.
sdwave
Posts: 27
Joined: Mon Feb 13, 2012 1:20 am

Re: Midscreen scroll/parallax/character bank change glitchin

Post by sdwave »

When you use the $2006/5/5/6 trick for resetting the scroll, only the last 2 writes have to fall inside the horizontal blank, because the first 2 don't affect rendering at all.
Thank you! This right here was the magic I needed and I managed to get rid of my scanline glitches due to scrolling/ fg/bg switch on both actual hardware and Nestopia. I still have the rare issue that a DPCM operation can affect my timing and push the writes outside of hblank, but since I now have the cycles to do turn PPU on/off around the critical section, the rare DPCM operations during my timed code don't cause a scroll jump. Here is the change:

Code: Select all

; setup and writes which can occur outside hblank
  lda IRQ_SCROLL_2006_FIRST, x 
  sta PPU_ADDR
  lda IRQ_SCROLL_2005_Y, x
  sta PPU_SCRL
  lda IRQ_SCROLL_2005_X, x
  tay
  lda IRQ_SCROLL_2006_SECOND, x
  tax

; Note, setting scroll value to different X value midscreen requires PPU
; drawing to be disabled so that internal X counter does not interfere with
; setting scroll address and values. 
; (Note this will only occur when DPCM operations interfere with timing and )
   lda #PPU_MASK_SPR_VIS
   sta PPU_MASK
   sty PPU_SCRL
   stx PPU_ADDR
   lda #(PPU_MASK_SPR_VIS | PPU_MASK_BKG_VIS )
   sta PPU_MASK
Not related to your actual question (I don't have time to go through the code right now), but a small detail: you don't need to push and restore the flags, 6502 will do that automatically.
Good advice, that bought me a little more time and flexibility!

How do you detect the end of the status bar if the first IRQ is set *after* it?
What I meant to say what when the first irq is triggered, the code within the irq sets..
The only timed code is within the irq for delaying until the hblank is hit. DPCM can in fact still interfere with this timing, but its a lot more rare.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Midscreen scroll/parallax/character bank change glitchin

Post by tokumaru »

sdwave wrote:

Code: Select all

   lda #PPU_MASK_SPR_VIS
   sta PPU_MASK
You shouldn't need this (i.e. the comment about the need to disable drawing is not correct). If your scrolling breaks without this, maybe the $2005/6 writes are happening too early.
Post Reply