This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

Unable to set y scroll in MMC3 irq

Unable to set y scroll in MMC3 irq
by on (#36700)
Any clues why this irq handler isn't working? The y scrolling doesn't seem to work at all in JNes, Nestopia, and FCEUX in the irq handler. (triggered by mmc3)

However, in JNes and FCEUX the X scrolling is working, so the interrupt does function.

I'm wondering if the emulators are fubar, or there is something else I need to be doing.

Thanks,
Crash

PPU_STAT = $2002 ; Can be read to get current PPU status
PPU_SCRL = $2005 ; Sets X/Y scrolling of background
MMC3_IRQ_ACK = $E000 ;scanline IRQ acknowledge/disable

;variable locations
R_SCRX = $02
R_SCRY = $03


irq:
pha ;push a
txa
pha ;push x
tya
pha ;push y

ldx #0
stx MMC3_IRQ_ACK

ldx PPU_STAT ;read to prime cpu scroll
lda R_SCRX
sta PPU_SCRL
lda R_SCRY
sta PPU_SCRL


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

rti

by on (#36701)
This is a known limitation of the NES, Y scroll can't be modified mid-screen just with a $2005 write. Fully repositioning the scroll mid-frame can only be done with a combination of $2006 and $2005 writes. Something like this. Here's a good explanation of loopy's document on the effects of each write.

by on (#36709)
Quietust wrote:
Anonymous wrote:
Bregalad wrote:
Battletoads does write twite to $2006 to get the vertical scrooling, then twice to $2005 to get horizontal scrooling (the second write is dummy and don't actually need to be there). All licenced games seems to do like this

Yeah, many games I checked did it like this... I can't seem to understand how this works, though, since the second write to $2005 has no effect.

Quote:
and the only restriction is that they need timed code to setup fine vertical scrooling.

What do you mean? How can timed code help them in getting fine vertical scroll?


In order to do smooth scrolling without that trick, you have to black out the background (ideally by switching CHR banks or equivalent), wait N scanlines, set vertical scroll to the coarse offset you want (with two $2006 writes), wait 8-N scanlines, then start rendering the playfield. All vertical scrolling games with a top-mounted status bar use this technique, the most noticeable ones being Battletoads and Castlevania 3.


Think it's possible to do this with MMC interrupts? Makes me wonder if its worth going to a bottom status bar?

by on (#36711)
Are you scrolling in 4 directions, just horizontally, or just vertically?

by on (#36712)
CrashTest wrote:
Quietust wrote:
In order to do smooth scrolling without that trick, you have to black out the background (ideally by switching CHR banks or equivalent), wait N scanlines, set vertical scroll to the coarse offset you want (with two $2006 writes), wait 8-N scanlines, then start rendering the playfield. All vertical scrolling games with a top-mounted status bar use this technique, the most noticeable ones being Battletoads and Castlevania 3.


Think it's possible to do this with MMC interrupts? Makes me wonder if its worth going to a bottom status bar?

Ah, so this is for a status bar after all. Well, it's not that hard to get the correct position with $2005/$2006. You could also use the above technique (using MMC3 IRQ's, yes), if you don't care for the few blank scanlines between the status bar and the playfield, but you'll still have to do *some* $2005/$2006 magic (but much less since you won't need fine Y positioning).

There are some advantages to having the status bar at the top, such as being able to scroll sprites smoothly from the top (this is only really noticeable when you use 8x16 sprites).

by on (#36719)
Thanks to all that responded.

Yes, I'd like to do 4 way scrolling, with a status bar, and have it work on a real NES, and at least one emulator (hopefully FCEUX or something else with a realtime nametable viewer for debugging)

Status bar on top is slightly preferable because of the sprite issue, but I don't know how to write the timed code yet to do yscroll. Any good examples with a _top_ status bar?

Also, I'm thinking I'll use horizontal mirroring and deal with the side artifacts since I want the extra nametable memory for dialog boxes I can scroll in from above or below.

by on (#36722)
Well, it's not the simplest thing but I'll try to explain it the best I can. At least the usage of MMC3 (or any other kind of) IRQs will simplify your life.

First of all you have to decide how you'll work with nametables. Multidirectionnal scrolling with horizontal mirroring can be done with some different techniques, all of them being tricky. I'll do my best to explain them :

1) Changing adress of the status bar (Double Dragon, Chronicles of Radia wars) :
The status bar is located somewhere and the playfield can extend in all directions. When the playfield "reaches" the status bar, the scroll engine automatically switch to another copy of the status bar somewhere else where the playfield isn't used anymore. The main drawback is that you have to constantly re-write the whole status bar at different nametable adresses while scrolling, and keep track of where the status bar is. Personally I'd go this way (I do something similar in the project I'm developping).

2) Changing adress of the playfield (Kirby's Adventure) :
The status bar is at a fixed place this time, but you double the playfield, so that it's possible to see the whole thing and wrap arround while never reaching the status bar. The main drawback is that you have to upload 2 identical playfields simultanously into the PPU.

3) Use another IRQ to change vertical scrolling midframe (Crystalis) :
The status bar is again on a fixed place and the playfield can extend without being "doubled". However, you set an IRQ that reset the vertical scrolling to zero to wrap arround by software. The main drawback is that you have to use multiple IRQs and precise timing.

Just use FCEUXD to see those techniques in details and how they work.

And now about the IRQ for the status bar :
- Bottom status bar : Set an IRQ for the scanline you want. In the IRQ routine just write the PPU adress of your status bar to $2006 (minus $2000) and write $00 to $2005. For example if the status bar is located at $2300, you'd want to write $03, $00 to $2006 and then $00 to $2005.

- Top status bar : You'd need a 4 pixel gap at the bottom of the status bar (or even 8 pixels if you want to simplify the logic).
Set the IRQ for the top of the pixel gap. There, you'll have to disable background but leave sprites enabled via $2001, so that the BG scrolling is intact. Now prepare the adress to be written to $2006, which is equivalent to the nametable adress of the tile you'd want to see on the top-left. In terms of vertical scrolling a few shifts have to be done in order to find that adress.

Now add the low 2 bits of your vertical scroll value to the highest nybble of that adress. For example if the adress is $0120 and the 2 lowest bits of the VScroll value are to '3', you'll want to write $31, $20 to $2006.

Then you'll have to watch the third bit of your fine VScroll value to know when to write to $2006. If the bit is clear, write it immediately, else write it 4 scanline later (at the end of the gap). There, turn BG back on via $2001, write the correct horizontal scroll to $2005 and you're done ! Multi-directional scrolling with top status bar that allow sprites to scroll in it. I'll pase some code to clarify the huge mess that's above :
Code:
   lda VScroll
   asl A
   rol A
   rol A
   and #$03
   sta Temp      ;Get 2 highest bits of tile index (high adress byte) and nametable selection

   lda VScroll      ;Get 2 lowest bit of vertical scrolling
   and #$03
   asl A
   ora FieldNameTable   ;Name table of playfield, assumed to be 0 or 1
   asl A
   asl A
   asl A
   ora Temp      ;Get nametable selection in bit3 and fine scroll in bits 4&5
   sta Temp

   lda #$10
   sta $2001      ;BG off, but sprites on (scrolling intact)

   lda VScroll
   and #$04      ;Compute when to write to $2006
   beq +
   jsr Wait4Scanlines   ;Timed code to wait 4 scanlines
   jsr Writeto2006
   jmp ++

+   jsr Writeto2006
   jsr Wait4Scanlines

++   lda #$18
   sta $2001
   lda HScroll
   sta $2005
   rts

Writeto2006
   bit $2002
   lda Temp
   sta $2006
   lda ScollValue
   asl A
   asl A
   and #$e0      ;Get the mid part of the PPU counter adress
   sta $2006      ;Update the thing, take effect immediately
   rts

Note that you have to write timed code, you can't trigger an MMC3 IRQ if only sprites are enabled. This code is to show the idea, you will have to fine-tune the timing and place it into a IRQ routine yourself.
Hope this huge post clarifies things for everyone.

by on (#36723)
Thanks for the info on the timed code approach. It could be really useful.
I managed to get it to work without the timed code with strightforward interrupts using a similar series of writes to the stomper demo. I'm using the Crystalis method for now, although I might drop it since I'm thinking of using some scanline irqs for sprite multiplexing as well.

I'll have to give this timed code appraoch a shot:

ANyways, heres the write sequence I've used: It works in both FCEUX and JNES. Not sure if the delay is actually necessary, but its in the stomper code.


Code:
.macro setScrollVal scrValX, scrValY

      lda scrValY
      asl A
      asl A
      and #$E0

      ;ldy #$8A
      ldy #$88
      sty $2000
      ldy scrValY

      ; delay might be needed in some cases
      ;ldx #$08
      ;:      
      ;dex
      ;bne :-

      ldx scrValX
      stx $2005
      sty $2005
      stx $2005
      sta $2006
      stx $2005
      sty $2005

.endmacro


by on (#36724)
To help debug your timing, run it in Nintendulator with Debugger, and set a breakpoint for CPU writes to 2005 or 2006. Nintendulator is currently the only debugger which tells you the PPU clock value in addition to scanlines.
If any PPU writes are outside the range of 256-341, you will get graphical glitches.

by on (#36725)
I don't know what you mean by sprite multiplexing, but I don't think this feature is supported by the NES. It's impossible to change anything related to sprites mid-frame without blanking the screen, which would result in a big solid bar in the middle of the screen.

I don't understand your method, there is really a lot of $2005 writes, but if it work for you that's good.

by on (#36734)
Dwedit wrote:
To help debug your timing, run it in Nintendulator with Debugger, and set a breakpoint for CPU writes to 2005 or 2006. Nintendulator is currently the only debugger which tells you the PPU clock value in addition to scanlines.
If any PPU writes are outside the range of 256-341, you will get graphical glitches.


I'll get this, and see whats going on. I've got a few glitches here and there and I can see artifacts in the middle of my 'Crystalis' wraparound irq line, that seem to be timing dependent.

Bregalad wrote:
I don't know what you mean by sprite multiplexing, but I don't think this feature is supported by the NES. It's impossible to change anything related to sprites mid-frame without blanking the screen, which would result in a big solid bar in the middle of the screen.

I don't understand your method, there is really a lot of $2005 writes, but if it work for you that's good.


I have no clue about the $2005 writes either. I just modified this approach from the stomper demo, and its the first time I've seen my scrolling work in both FCEUX and JNES (but still no Nestopia) :)

About the multiplexing... so I can't move a few indivdual the sprites mid-frame so that I can render the same sprite above and below the split to increase my effective number of sprites (like you can on the commodore 64)? That's a bummer. My artwork has 48 pixel high characters, and i was hoping to reuse my sprites to increase the number of onscreen characters. I was thinking I could get away with 8x8 and a more compact sprite char rom.

by on (#36735)
CrashTest wrote:
so I can't move a few indivdual the sprites mid-frame so that I can render the same sprite above and below the split to increase my effective number of sprites (like you can on the commodore 64)?

You can if you disable rendering for a few scanlines, which would be your "split area". So it isn't really an interesting option, unless you have 2 individual playfields, or the sprites only move horizontally and you can afford to have a few blank scanlines.

Quote:
That's a bummer. My artwork has 48 pixel high characters, and i was hoping to reuse my sprites to increase the number of onscreen characters. I was thinking I could get away with 8x8 and a more compact sprite char rom.

Your best bet is using 8x16 sprites I guess.

by on (#36744)
Yes, you can't do sprite multiplexing as you can on the Commodore64, and also you can't acess to VRAM directly as you can on the Commodore, you have to do it during VBlank. However, the graphics the NES have to offer are overall better, and 64 small sprites is infinitely better than 8 limited huge sprites.

by on (#36759)
Bregalad wrote:
you have to do it during VBlank.

OR when rendering is intentionally disabled. This means that it is possible to multiplex the sprites, you just have to disable rendering to request a sprite DMA mid-screen.

by on (#36765)
tokumaru wrote:
Bregalad wrote:
you have to do it during VBlank.

OR when rendering is intentionally disabled. This means that it is possible to multiplex the sprites, you just have to disable rendering to request a sprite DMA mid-screen.


I see youre talking about a sprite DMA, and what you mention would be useful for a split screen two player or 4 player game.

My thought is to just change a few individual y positions and the sprite index number using $2003 and $2004. Does that require disabling rendering? And.. is it only sprite rendering, or is it bkg and sprite rendering.

by on (#36768)
CrashTest wrote:
My thought is to just change a few individual y positions and the sprite index number using $2003 and $2004. Does that require disabling rendering?


yes

Quote:
And.. is it only sprite rendering, or is it bkg and sprite rendering.


Have to disable both.

by on (#36775)
What you have to understand is that the NES architecture is very different from C64's.

On the NES, the memory is clearly split between the vido memory and main CPU memory (there is two different buses) and the only connection between them are $2003/4 and $2006/7. When you write to $2004 (including through $4014) or to $2007, this order the PPU to read that byte, that is buffered, and the PPU will actually write it to memory slightly later when it can. This feature is not available while the PPU is already working on processing the image.

On the C64, the 64k of DRAM is shared between the CPU and the VIC2 ('PPU'), so that each processor have acess to the memory one cycle out of two. The RAM can freely be used as VRAM and the user can tell the 'PPU' to point to any range of the RAM to use data for image information. Also, the CPU can write a byte into memory, and the 'PPU' can read it the next cycle, that doesn't cause problems because each device is iddle one cycle out of two. The price for is is that the CPU runs almost 2 times slower, at only 1MHz instead of 1.79 MHz.

Finally, if your programm works on FCEU but not on NEStopia, chances that it doesn't work at all on the real NES are high (FCEU is innacurate, but Nestopia is very accurate). The problem is probably how you set up your IRQ (do you do it between MMC3's counter limitations, discussed very recently on this forum ?) or how you write to the scroll registers.

by on (#36782)
CrashTest wrote:
My thought is to just change a few individual y positions and the sprite index number using $2003 and $2004.

I can't tell you about the details, and hopefully someone else will give you details, but a while ago the use of $2003/$2003 for updating sprites was very frowned upon. It certainly is slower than using the DMA (if you want to update all sprites), but it seems like there were some bugs also.

You seem to be using a lot of the "extended features" of the NES, which most of the time are controversial. This is certainly not a bad thing, but I'd suggest you find a way to test your programs on a real NES... You simply can't trust emulators for things like these.

by on (#36783)
tokumaru wrote:
I can't tell you about the details, and hopefully someone else will give you details, but a while ago the use of $2003/$2003 for updating sprites was very frowned upon. It certainly is slower than using the DMA (if you want to update all sprites), but it seems like there were some bugs also.


I wasn't aware of any bugs/quirks with updating with $2004.

by on (#36784)
Disch wrote:
I wasn't aware of any bugs/quirks with updating with $2004.

I'm pretty sure someone mentioned a few reasons not to use those registers besides time... Sorry if I can't remember who, or what the reasons were...

I just have this voice in the back of my head that says "someone at NESDEV said that $2003/$2004 are evil, use the DMA instead". I hear a bunch of similar voices because of things I read here, and I just can't remember the reasoning behind all of them.

Sorry if I'm wrong. Just test the thing on a real NES and stick to what works.

P.S.: I don't actually hear voices (O_o), that's just a figure of speech.

EDIT: Maybe it was this post by tepples? I'm too lazy to keep searching, but maybe it had to do with OAM being DRAM and all.

EDIT 2: There is also this comment by Memblers, about demos that don't work (although the demos might not have worked for other reasons?). Anyway, I didn't know much back then, so these recommendations might have been enough for me to avoid those registers.

by on (#36792)
I'm sure you can turn off the display for one scanline just to update coordinate of a few sprites, and it will work fine. You'll just get a spriteless scanline below where you turned the screen back on.
But you need to turn the display off because the PPU is always accessing the sprite memory.

by on (#36799)
Bregalad wrote:
What you have to understand is that the NES architecture is very different from C64's.


Finally, if your programm works on FCEU but not on NEStopia, chances that it doesn't work at all on the real NES are high (FCEU is innacurate, but Nestopia is very accurate). The problem is probably how you set up your IRQ (do you do it between MMC3's counter limitations, discussed very recently on this forum ?) or how you write to the scroll registers.


Yeah thats a good synopsis of C64 vs NES, my big reason for going with the NES was actually the ROM page swapping, and the the fact that turinign on multi-color tiles on the C64 gives you half res unless one uses some crazy tricks. That and there just something I like about the 4 color tiles and the way they look NTSC.

On Nestopia, one thing that got me at first was I needed to write $40 to $4017 to disable the frame irqs.

About the MMC counter limitations. I'm going to have to look into that a bit more. Bottom status bar I can get working Nestopia, but the top one still evades me. With enough time I think I can get it workign on all 3.

Another weird thing, mid-frame palette changes work on jnes for me but not reliably on FCEUX.

by on (#36800)
Gotta turn off the screen at a safe time (like PPU clock 256-341), do your palette changes, set the VRAM address back to scroll properly, then turn the screen back on at a safe time.
You see garbage in the overscan areas during mid-screen palette changes, highly visible in the Angry Video Game Nerd's review of Back to the Future, and also visible in video captures of other NES games, such as Super Off Road, or Fantastic Adventures of Dizzy

by on (#36852)
Here you are a demo I quickly made that show how to scroll vertically with a status bar on the top, and that works on real hardware (meant to be compiled with wla-dx):
http://jonathan.microclub.ch/Scrolling/
Feel free to use the source code for your projects (but not the graphics obviously)

PS : On a side off-topic note, the programm fails at power-up, but always runs fine after pressing the reset button, and that no matter how many VBlanks I wait at the Reset vector. Any ideas Blargg ?

by on (#36857)
Bregalad wrote:
Here you are a demo I quickly made that show how to scroll vertically with a status bar on the top, and that works on real hardware (meant to be compiled with wla-dx):
http://jonathan.microclub.ch/Scrolling/
Feel free to use the source code for your projects (but not the graphics obviously)

PS : On a side off-topic note, the programm fails at power-up, but always runs fine after pressing the reset button, and that no matter how many VBlanks I wait at the Reset vector. Any ideas Blargg ?


I'd like to take a look at this, but either my ISP is acting up, or your website is down. Worth posting a .zip to megaupload or something?

by on (#36859)
Well, it's not down right now, but it's a possibility it has been down this night.
Your browser have to support direct browsing trough file has there is no index.html tough (I'm too lazy to make one). I may upload it in zip format as well if required but I was too lazy to zip it.

by on (#36860)
Directory browsing is done server side, not client side.

by on (#36869)
Bregalad site was working fine during lunch time. I was able to download his sample and it was working fine. I can't wait to get a glimpse at the code.

This is the kind of thing beginners are looking for: in your face concept explained by a code sample. We should have more of these for common programming algorithm on the wiki, that would be great.

by on (#36881)
Still no luck here downloading, but my ISP tends to be troublesome.

I managed to get my example up and running solid on all 3 emulators topbar + 4 way scroll + crystalis irq split using mmc3 irq. Basically I needed to only use the NMI handler to update the frame counter variable, and not have my game loop in the NMI. I've only been programming NES for one week, so I'm pretty happy with my progress so far.


Still though, thanks for posting the example Bregalad, I'll check it out once I can access your site.

by on (#36954)
Here's something really interesting I found I find that $2005 and $2006 share the same flip-flop for determining if the first of second component get write. Becuase of this you can actually write the high part of one and the low part of the other. E.g. write to 2006 once, and the write to 2005 right after is a write to the yscroll value. Then by writing 2005 to get x scroll, then 2006 to set the low part of the y address This way, I can prevent the 2006 write from overwriting bit 2 of the fine y scroll.

I'm going to post this code to the nesdev site about this, but this seems to allow me to be able to do 8 way scrolling with a top bar without the bar/timed code to emulate the effect of bit 2. But I have no clue if it works on an actual NES.


;===========================================
; set scroll using alternating 2005/2006 sequence
;===========================================
.macro setScrollValAlt scrValX, scrValY

;isolate fine y scroll 3 bits, and upper 3 bits of y

lda scrValY ; yyyyyfff
asl A ; yyyyfff0
asl A ; yyyfff00
tax
and #$e0 ; yyy00000
sta R_IRQTMP0

txa ; yyyfff00
asl A ; yyfff000
asl A ; yfff0000
and #$70

sta PPU_ADDR ;
lda scrValY ; yyyyyfff
sta PPU_SCRL ;
lda scrValX ; xxxxxggg
sta PPU_SCRL ;
lda scrValX ; xxxxxggg
lsr A ; 0xxxxxgg
lsr A ; 00xxxxxg
lsr A ; 000xxxxx
ora R_IRQTMP0 ; yyyxxxxx
sta PPU_ADDR ;

.endmacro

by on (#36958)
Oh yeah I remember seeing something likt that before but I didn't really understand it. Also, if you're able to get it working without graphic glitches that's great but this kind of uploads always got me graphical glitches.
Does the second write to $2006 reset bit 2 of YScroll ?

If you can made a version of it using mapper 0 or 3 and with PAL timing I can test it for you, else I can't.

PS : I've tried your method (with a XScroll of zero) and it works, but the first scanline is garbage, so you have to disable BG on it. But yeah it reduce the gap from 4 pixels to 1 pixel which is better.
Also I've gotten weird glitches randomly, probably because of a bad connexion on the CHR side ? I've also gotten pink BG color sometimes.

by on (#36963)
CrashTest wrote:
Here's something really interesting I found I find that $2005 and $2006 share the same flip-flop for determining if the first of second component get write. Becuase of this you can actually write the high part of one and the low part of the other. E.g. write to 2006 once, and the write to 2005 right after is a write to the yscroll value. Then by writing 2005 to get x scroll, then 2006 to set the low part of the y address This way, I can prevent the 2006 write from overwriting bit 2 of the fine y scroll.

I'm going to post this code to the nesdev site about this, but this seems to allow me to be able to do 8 way scrolling with a top bar without the bar/timed code to emulate the effect of bit 2. But I have no clue if it works on an actual NES.

This is what Tokumaru was talking about in the beginning of this thread. Might want to go back and reread it, now that it's "clicked" :) Here's a few demos that use this technique..

Years Behind (PAL)
high hopes (PAL)
unfinished (the second part...)

by on (#36993)
Bregalad wrote:
Oh yeah I remember seeing something likt that before but I didn't really understand it. Also, if you're able to get it working without graphic glitches that's great but this kind of uploads always got me graphical glitches.
Does the second write to $2006 reset bit 2 of YScroll ?

If you can made a version of it using mapper 0 or 3 and with PAL timing I can test it for you, else I can't.

PS : I've tried your method (with a XScroll of zero) and it works, but the first scanline is garbage, so you have to disable BG on it. But yeah it reduce the gap from 4 pixels to 1 pixel which is better.
Also I've gotten weird glitches randomly, probably because of a bad connexion on the CHR side ? I've also gotten pink BG color sometimes.


I _think_ the first write to 2006 resets bit 2 of y scroll, then the second write to 2005 sets it to the desired value. The second write to 2006 is what updates the actual ppu address register. The end of vblank is the other thing that updates the register from what I've read.

I'll send you an example in a couple days once I have a chance to move more code out of vblank. I'd need MMC3 and I think thats mapper 4?

Seems like for PAL there is less time in VBLANK so I'll need to make sure my MMC interrupts get setup within the vblank sooner. And there seems to be that accidental clocking of the scanline register with ppu writes, so I'll try and do all my ppu updates inside the vblank before setting the MMC interrupt

This is a really interesting platform to work with.

by on (#36996)
I currently have no devcart for MMC3 (mapper 4), and for PAL there is more time in VBLank. In fact PAL VBlank is 105/32 (~3.28) times greater than NTSC VBlank (in CPU cycles), which is very significant).
However, the PAL frame is 15/16 shorter (in CPU cycles) than the NTSC frame.
Under most case, if you design your VBlank code so that it works on NTSC, it will work on PAL too. For raster effects, if you do calculation between each raster updates (which is a complex thing to do, but I've done it a couple of times like on my mode7 demo), you'll have to manage them so that they work on PAL first, and they can be portede to NTSC by adding a very small delay each scanline.

If your code does work on Nintendulator and Nestopia it's almost certain it will work the same way on real hardware and if it's not it is certainly for a very stupid fixable detail.