It is currently Thu Oct 19, 2017 4:11 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 23 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Mon Jul 11, 2005 11:30 pm 
How exactly is split-screen scrolling done on the MMC3? I have tried out some code:

Code:
;NMI
nmi:
   sei
   pha
   txa
   pha
   tya
   pha
   lda $2002
   lda #$00
   sta $2006
   sta $2006
   lda #$01
   sta <$80
   jsr text_code
   lda <$1a
   bne scroll

endnmi:
   lda #$00
   sta <$80
   pla
   tay
   pla
   tax
   pla
   rti

scroll:
   lda <$1b
   ora <$17
   beq goone
   jmp endnmi

goone:
   lda <$82
   bne endnmi
   lda #$c1
   sta $c000
   sta $c001
   sta $e001
   lda #$01
   sta <$81
   sta <$82
   cli
   jmp endnmi

;IRQ/BRK
irq:
   sei
   pha
   tya
   pha
   txa
   pha
   lda <$81
   beq endirq
   sta $e001
   lda $2002
   lda #00
   sta $2006
   sta $2006
   inc <$30
   lda $2002
   lda <$30
   sta $2005
   sta $2005
   sta $e000
   lda #00
   sta <$81
   sta <$82

endirq:
   pla
   tax
   pla
   tay
   pla
   rti


Now, the screen does scroll properly, so I know the IRQ is executing, but the scrolling does not split-screen. Now I did read Kevin's document on the MMC3, but that only talks about the IRQ and not how it relates to scrolling. Right here:

http://www.tripoint.org/kevtris/mappers/mmc3/

Perhaps somebody could tell me what I'm doing wrong and perhaps point me in the right direction? Even better, could they correct my code?

Thanks. ^_^


Top
  
 
 Post subject:
PostPosted: Tue Jul 12, 2005 8:56 am 
Offline
User avatar

Joined: Wed Nov 10, 2004 6:47 pm
Posts: 1845
Doesn't look like you're doing anything wrong as far as MMC3 IRQs go. Although I don't know why you're writing to $E001 in the IRQ routine (since you write to $E000 right after it)

I'd assume you want to put a status bar or something at the bottom (something that doesn't scroll) based on your dual $2006 writes of 0. So whatever you're scrolling to in the bottom half of the screen would be at the top of the nametable. If that's the case, I would do something simple like this:

Code:
LDA #$00
STA $2005  ; clear fine X scroll
BIT $2002  ; reset flip-flop (another $2005 write would work too)
STA $2006
STA $2006  ; clear loopy_t and loopy_v  (start drawing at very top of nametable at ppu$2000)


Unless you time that to fit in HBlank, the first scanline will be distorted -- so probably have at least a scanline or two which is blank to prevent that disortion from being visible.

Remember that dual $2005 writes WILL NOT change any vertical scrolling (except at frame start)-- you NEED to write to $2006 for split screen vertical scroll changes.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 12, 2005 9:17 am 
Yeah, I removed that part. Still, I can't seem to get it to work. I think one of my problems is that I don't really know how these IRQs cause split-screen scrolling, because of that, I really don't know what to do. I'm just grasping at straws.

Perhaps you could explain it or perhaps you could link me to a document on it? I tried Kevin's document, which is recommended on this forum, but it does not really explain how to utilize the IRQs to cause split-screen scrolling.

Thanks!


Top
  
 
 Post subject:
PostPosted: Tue Jul 12, 2005 9:45 am 
Offline
User avatar

Joined: Wed Nov 10, 2004 6:47 pm
Posts: 1845
well I could be more help if I knew what you were trying to do =P. I mean you already are splitting the screen -- it just sounds like you're not scrolling like you want to (so this is more of a scroll question than an MMC3 question).

The IRQs themselves don't cause the screen to split -- they just trigger an IRQ at the end of the desired scanline. You still have to change the scroll values in your code by manipulating $2005/2006.

Your code IS splitting the screen -- maybe it's not noticable because of how your nametables are set up, but it should be happening:

- You're setting your screen to draw from ppu$2000 in your NMI routine (by the dual $2006 writes of 0)

- You set up the MMC3 to trigger an IRQ at the end of scanline 194 (by writing $C1 to the IRQ reload)

- Then in your IRQ routine, you reset the scroll so that it starts drawing at ppu$2000 again (by the dual $2006 writes of 0)

- You scroll the bottom of the screen horizontally (but not vertically) by writing to $2005 (however you never seem to reset the fine scroll for the top half of the screen, so does the top half jitter? Or do you write to $2005 elsewhere (like in text_code)?)

I don't have any docs to recommend on the subject other than loopy's scrolling doc -- although I don't know if it'd be helpful for you. Like I said if I knew what you were going for I might be able to help more. But yeah -- it looks like you are splittnig the screen just fine.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 12, 2005 10:02 am 
- You scroll the bottom of the screen horizontally (but not vertically) by writing to $2005 (however you never seem to reset the fine scroll for the top half of the screen, so does the top half jitter? Or do you write to $2005 elsewhere (like in text_code)?)

What do you mean resetting the fine scroll for the top half of the screen? In my latest code, I do write the scrolling I want for outside the status bar in the NMI, then in the IRQ I simply write zeroes to 2005 twice (so it won't scroll). It still doesn't work. When I run it in FCEUXD and open up the Name Table window, I see that the whole screen scrolls to the zero position. Am I close to what I need to do?

Also, how do I know how many scanlines I need to start at a particular location? The IRQ can go up to FF, indicating that it will go through 256 scanlines, but I don't see how there can be that many scanlines. Each screen has only 30 lines right?


Top
  
 
 Post subject:
PostPosted: Tue Jul 12, 2005 10:41 am 
Offline
User avatar

Joined: Wed Nov 10, 2004 6:47 pm
Posts: 1845
Anonymous wrote:
What do you mean resetting the fine scroll for the top half of the screen?


The first $2005 write sets the fine horizontal scroll. That is the only way to set the fine horizontal scroll. If you change the fine H scroll for the bottom half of the screen, it will affect the top half of the screen in the next frame unless you do another $2005 write at the start of the frame to reset the fine H scroll. I didn't see you doing that in the code you pasted.

Quote:
In my latest code, I do write the scrolling I want for outside the status bar in the NMI, then in the IRQ I simply write zeroes to 2005 twice (so it won't scroll). It still doesn't work.


If you want to scroll vertically, that won't work -- you'd need to write to $2006. If you want to scroll horizontally, then $2005 writes alone should suffice. But it sounds to me like you're just punching in random values and seeing what will work.

Basically... how it works is like this:

$2005 is used for scrolling. You write to it in pairs of two... the first write sets the horizontal scroll (in pixels). The second write sets the vertical scroll (in pixels). Note that writes to $2005 do not actually change the PPU address (therefore coarse horizontal scroll effects do not appear until the start of the next scanline -- and vertical scroll effects do not appear until the start of the next frame).

The second $2006 write actually sets the PPU address (it's commonly used for setting the address before writing to $2007, but that same address also determines scroll position). If you want to change vertical scrolling mid-frame, you will NEED to manipulate $2006. However if you don't want to disturb V scroll and just want to change H scroll midscreen (for example, like Super Mario Bros, or Castlevania, or Excitebike), then you should be just fine with only $2005 writes.

How $2006 affects scrolling is kind of weird to explain -- if you want to know check out loopy's scrolling doc, as it lays it out fairly well (although it's somewhat weird to understand at first)

Again... I could give you a more specific explaination if I knew what you were trying to do :P. All I know is that you want to split the screen and it's not working the way you want -- but your code IS splitting the screen so I can't tell you how to fix it unless you tell me what you're going for.

Quote:
Also, how do I know how many scanlines I need to start at a particular location? The IRQ can go up to FF, indicating that it will go through 256 scanlines, but I don't see how there can be that many scanlines. Each screen has only 30 lines right?


1 scanline = 1 row of pixels. There are 240 visible scanlines per frame (although the top and bottom 8 are usually hidden on NTSC displays).


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 12, 2005 10:58 am 
Disch wrote:
Anonymous wrote:
What do you mean resetting the fine scroll for the top half of the screen?


The first $2005 write sets the fine horizontal scroll. That is the only way to set the fine horizontal scroll. If you change the fine H scroll for the bottom half of the screen, it will affect the top half of the screen in the next frame unless you do another $2005 write at the start of the frame to reset the fine H scroll. I didn't see you doing that in the code you pasted.

Quote:
In my latest code, I do write the scrolling I want for outside the status bar in the NMI, then in the IRQ I simply write zeroes to 2005 twice (so it won't scroll). It still doesn't work.


If you want to scroll vertically, that won't work -- you'd need to write to $2006. If you want to scroll horizontally, then $2005 writes alone should suffice. But it sounds to me like you're just punching in random values and seeing what will work.

Basically... how it works is like this:

$2005 is used for scrolling. You write to it in pairs of two... the first write sets the horizontal scroll (in pixels). The second write sets the vertical scroll (in pixels). Note that writes to $2005 do not actually change the PPU address (therefore coarse horizontal scroll effects do not appear until the start of the next scanline -- and vertical scroll effects do not appear until the start of the next frame).

The second $2006 write actually sets the PPU address (it's commonly used for setting the address before writing to $2007, but that same address also determines scroll position). If you want to change vertical scrolling mid-frame, you will NEED to manipulate $2006. However if you don't want to disturb V scroll and just want to change H scroll midscreen (for example, like Super Mario Bros, or Castlevania, or Excitebike), then you should be just fine with only $2005 writes.

How $2006 affects scrolling is kind of weird to explain -- if you want to know check out loopy's scrolling doc, as it lays it out fairly well (although it's somewhat weird to understand at first)

Again... I could give you a more specific explaination if I knew what you were trying to do :P. All I know is that you want to split the screen and it's not working the way you want -- but your code IS splitting the screen so I can't tell you how to fix it unless you tell me what you're going for.

Quote:
Also, how do I know how many scanlines I need to start at a particular location? The IRQ can go up to FF, indicating that it will go through 256 scanlines, but I don't see how there can be that many scanlines. Each screen has only 30 lines right?


1 scanline = 1 row of pixels. There are 240 visible scanlines per frame (although the top and bottom 8 are usually hidden on NTSC displays).


I have read loopy's document, but the way it's written just doesn't make much sense to me. All it shows is a bunch of register writes that are mostly meaningless to me. Perhaps it could explain why it's making those register writes, perhaps it can explain exactly what it's doing with each register writes, but the way it's doing it just isn't cutting it for me.

Oh, by pixel! Thanks!

Here's what I'm trying to do:

The picture I'm scrolling starts at 2800 (lower left nametable) and I'll just scroll it diagonally. I then want a picture starting at 2000 (upper left nametable) and 6 tiles tall to be at the bottom 6 tiles of the display and not scrolling while the rest of the display (again, starting at 2800) does. Perhaps through an example of implementing this I can come to understand.


Top
  
 
 Post subject:
PostPosted: Tue Jul 12, 2005 12:34 pm 
Now don't get me wrong, I can implement basic scrolling. I know to write the horz. byte first then the vert. byte second to 2005, etc. What I don't understand very well is Loopy's document.

I sorta know how 2006 fits in with the NMI, you write the address of the nametable you want to display minus 2000 into 2006. Doing that also sets the origin for the scroll. You gotta read from 2002 again to reset the bytes for 2005.

But some things you mention, like coarse and fine scrolling and stuff like that, and also what exactly the IRQ does to prevent a whole part of the display from scrolling, is what's so confusing. I mean, to do what I stated here:

The picture I'm scrolling starts at 2800 (lower left nametable) and I'll just scroll it diagonally. I then want a picture starting at 2000 (upper left nametable) and 6 tiles tall to be at the bottom 6 tiles of the display and not scrolling while the rest of the display (again, starting at 2800) does. Perhaps through an example of implementing this I can come to understand.

What do I need to do in the NMI (should I set the values of the part I want to scroll here to 2005/2006? I know to set the MMC3 stuff here) and the IRQ (should I set the part I don't want to scroll here to 2005/2006?))? What you've shown me does not seem to be enough information.


Top
  
 
 Post subject:
PostPosted: Tue Jul 12, 2005 2:24 pm 
Offline
Site Admin
User avatar

Joined: Mon Sep 20, 2004 6:04 am
Posts: 3470
Location: Indianapolis
You may need to write in the order of $2006, $2005, $2005, $2006 to get the vertical scrolling set correctly after the screen splits. Keep in mind that'd make the first $2005 write vertical.

And also, the CPU disables IRQs automatically while the IRQ/NMI is being run. So the SEI instruction can be dropped there.


Last edited by Memblers on Tue Jul 12, 2005 2:28 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 12, 2005 2:25 pm 
Okay, I wrote a simpler piece of code (in a new program). Here it is:

Code:
nmi:
   pha
   txa
   pha
   tya
   pha
   lda $2002
   lda #$00
   sta $2006
   sta $2006
   inc <$30
   lda <$30
   cmp #$C0
   bcs reset

finishnmi:
   lda $2002
   lda #$00
   sta $2005
   lda <$30
   sta $2005
   lda #$c0
   sta $c000
   sta $c001
   sta $e001
   cli
   pla
   tay
   pla
   tax
   pla
   rti

reset:
   lda #$00
   sta <$30
   jmp finishnmi

irq:
   sei
   sta $e000
   pha
   lda $2002
   lda #$00
   sta $2005
   sta $2005
   lda $2002
   lda #$08
   sta $2006
   lda #$00
   sta $2006
   pla
   rti


The problem is that it sets the whole display to what is specified in the IRQ. It's like the IRQ takes precedence over the NMI for the whole screen. Here's a download of the NES file so that you guys can take a look at it:

http://www.freewebs.com/beneficii/scroll.nes

The image that gets loaded into the $2000 name table is the one I want to scroll, and the one I load into the $2800 name table is the one I don't want to.


Top
  
 
 Post subject:
PostPosted: Tue Jul 12, 2005 2:43 pm 
I ran a trace of the code. Almost immediately after CLI'ing (after one instruction after), the IRQ was executed 6 times in rapid succession, with only one instruction being executed at a time between IRQs.

Code:
$E117:48        PHA                        A:0A X:00 Y:C0 P:nvubdIzc   ;;;start of NMI
$E118:8A        TXA                        A:0A X:00 Y:C0 P:nvubdIzc
$E119:48        PHA                        A:00 X:00 Y:C0 P:nvubdIZc
$E11A:98        TYA                        A:00 X:00 Y:C0 P:nvubdIZc
$E11B:48        PHA                        A:C0 X:00 Y:C0 P:NvubdIzc
$E11C:AD 02 20  LDA $2002 = #$AA           A:C0 X:00 Y:C0 P:NvubdIzc
$E11F:A9 00     LDA #$00                   A:AA X:00 Y:C0 P:NvubdIzc
$E121:8D 06 20  STA $2006 = #$00           A:00 X:00 Y:C0 P:nvubdIZc
$E124:8D 06 20  STA $2006 = #$00           A:00 X:00 Y:C0 P:nvubdIZc
$E127:E6 30     INC $30 = #$00             A:00 X:00 Y:C0 P:nvubdIZc
$E129:A5 30     LDA $30 = #$01             A:00 X:00 Y:C0 P:nvubdIzc
$E12B:C9 C0     CMP #$C0                   A:01 X:00 Y:C0 P:nvubdIzc
$E12D:B0 1F     BCS $E14E                  A:01 X:00 Y:C0 P:nvubdIzc
$E12F:AD 02 20  LDA $2002 = #$20           A:01 X:00 Y:C0 P:nvubdIzc
$E132:A9 00     LDA #$00                   A:20 X:00 Y:C0 P:nvubdIzc
$E134:8D 05 20  STA $2005 = #$00           A:00 X:00 Y:C0 P:nvubdIZc
$E137:A5 30     LDA $30 = #$01             A:00 X:00 Y:C0 P:nvubdIZc
$E139:8D 05 20  STA $2005 = #$00           A:01 X:00 Y:C0 P:nvubdIzc
$E13C:A9 C0     LDA #$C0                   A:01 X:00 Y:C0 P:nvubdIzc
$E13E:8D 00 C0  STA $C000 = #$FF           A:C0 X:00 Y:C0 P:NvubdIzc
$E141:8D 01 C0  STA $C001 = #$FF           A:C0 X:00 Y:C0 P:NvubdIzc
$E144:8D 01 E0  STA $E001 = #$02           A:C0 X:00 Y:C0 P:NvubdIzc
$E147:58        CLI                        A:C0 X:00 Y:C0 P:NvubdIzc
$E148:68        PLA                        A:C0 X:00 Y:C0 P:Nvubdizc   ;;; not the end of the NMI!
$E155:78        SEI                        A:C0 X:00 Y:C0 P:NvubdIzc    ;;; start of IRQ
$E156:8D 00 E0  STA $E000 = #$0F           A:C0 X:00 Y:C0 P:NvubdIzc
$E159:48        PHA                        A:C0 X:00 Y:C0 P:NvubdIzc
$E15A:AD 02 20  LDA $2002 = #$21           A:C0 X:00 Y:C0 P:NvubdIzc
$E15D:A9 00     LDA #$00                   A:21 X:00 Y:C0 P:nvubdIzc
$E15F:8D 05 20  STA $2005 = #$00           A:00 X:00 Y:C0 P:nvubdIZc
$E162:8D 05 20  STA $2005 = #$00           A:00 X:00 Y:C0 P:nvubdIZc
$E165:AD 02 20  LDA $2002 = #$20           A:00 X:00 Y:C0 P:nvubdIZc
$E168:A9 08     LDA #$08                   A:20 X:00 Y:C0 P:nvubdIzc
$E16A:8D 06 20  STA $2006 = #$00           A:08 X:00 Y:C0 P:nvubdIzc
$E16D:A9 00     LDA #$00                   A:08 X:00 Y:C0 P:nvubdIzc
$E16F:8D 06 20  STA $2006 = #$00           A:00 X:00 Y:C0 P:nvubdIZc
$E172:68        PLA                        A:00 X:00 Y:C0 P:nvubdIZc
$E173:40        RTI                        A:C0 X:00 Y:C0 P:NvubdIzc
$E149:A8        TAY                        A:C0 X:00 Y:C0 P:NvUbdizc
$E155:78        SEI                        A:C0 X:00 Y:C0 P:NvUbdIzc   ;;;start of IRQ (again)
$E156:8D 00 E0  STA $E000 = #$0F           A:C0 X:00 Y:C0 P:NvUbdIzc
$E159:48        PHA                        A:C0 X:00 Y:C0 P:NvUbdIzc
$E15A:AD 02 20  LDA $2002 = #$20           A:C0 X:00 Y:C0 P:NvUbdIzc
$E15D:A9 00     LDA #$00                   A:20 X:00 Y:C0 P:nvUbdIzc
$E15F:8D 05 20  STA $2005 = #$00           A:00 X:00 Y:C0 P:nvUbdIZc
$E162:8D 05 20  STA $2005 = #$00           A:00 X:00 Y:C0 P:nvUbdIZc
$E165:AD 02 20  LDA $2002 = #$20           A:00 X:00 Y:C0 P:nvUbdIZc
$E168:A9 08     LDA #$08                   A:20 X:00 Y:C0 P:nvUbdIzc
$E16A:8D 06 20  STA $2006 = #$00           A:08 X:00 Y:C0 P:nvUbdIzc
$E16D:A9 00     LDA #$00                   A:08 X:00 Y:C0 P:nvUbdIzc
$E16F:8D 06 20  STA $2006 = #$00           A:00 X:00 Y:C0 P:nvUbdIZc
$E172:68        PLA                        A:00 X:00 Y:C0 P:nvUbdIZc
$E173:40        RTI                        A:C0 X:00 Y:C0 P:NvUbdIzc
$E14A:68        PLA                        A:C0 X:00 Y:C0 P:NvUbdizc


I think I'm doing something wrong. Perhaps people can finally look at it.


Top
  
 
 Post subject:
PostPosted: Tue Jul 12, 2005 2:44 pm 
^ Note that I did not show all the instances of the IRQs, which all did the same thing.


Top
  
 
 Post subject:
PostPosted: Tue Jul 12, 2005 2:48 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 10:59 pm
Posts: 1389
The problem is that you are never clearing the I flag outside of NMI - when you do CLI at the end of your NMI routine, the RTI shortly afterwards sets the I flag again. As such, IRQs will ONLY be serviced at the end of your NMI routine (when the I flag is briefly cleared).

If your NMI routine is short, you shouldn't even be doing CLI inside in the first place - you should be doing it at the end of your initialization code, right after you enable NMIs (and be sure to write to $E000 before doing CLI, otherwise an unacknowledged MMC3 IRQ might go off).

The fact that your IRQ handler is being called 6 times in a row means there is probably a flaw in your MMC3 IRQ implementation, since writing to $E000 should acknowledge any pending MMC3 IRQs.

_________________
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 12, 2005 3:13 pm 
Quietust wrote:
The problem is that you are never clearing the I flag outside of NMI - when you do CLI at the end of your NMI routine, the RTI shortly afterwards sets the I flag again. As such, IRQs will ONLY be serviced at the end of your NMI routine (when the I flag is briefly cleared).

If your NMI routine is short, you shouldn't even be doing CLI inside in the first place - you should be doing it at the end of your initialization code, right after you enable NMIs (and be sure to write to $E000 before doing CLI, otherwise an unacknowledged MMC3 IRQ might go off).

The fact that your IRQ handler is being called 6 times in a row means there is probably a flaw in your MMC3 IRQ implementation, since writing to $E000 should acknowledge any pending MMC3 IRQs.


Dur, :idea: I knew that. Thanks for that info. I can't believe I missed that. LOL.

Still, I'm missing something. I do have one good piece of news, since fixing that interrupt flag thing, the whole screen is now black. Now that may not sound so good to you, but if you look at the name tables in the ROM, you'll see that there is no way the whole screen can be black without split-screen scrolling. So I have implemented it; now I need to figure out how to set the right value.

Uh, in some experimenation, I ran into some problems. The display got weird. Because of that, I'll go ahead and include the source files with the ROM so perhaps you can look at it and tell me how I'm implementing the IRQs wrongly:

http://www.freewebs.com/beneficii/scroll.zip


Top
  
 
 Post subject:
PostPosted: Tue Jul 12, 2005 3:18 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 10:59 pm
Posts: 1389
Your IRQ code is pointing the screen at the FIRST nametable and is trying to set vertical scroll via $2005, which cannot be done mid-frame. If you want a setup similar to what SMB3 does, you should be writing $0B,$00 to $2006 and $00 once to $2005.

_________________
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 23 posts ]  Go to page 1, 2  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: Yahoo [Bot] and 6 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group