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

Author Topic: How to program Parallax Scrolling  (Read 4411 times)

Gavzila

  • Jr. Member
  • **
  • Posts: 18
    • View Profile
How to program Parallax Scrolling
« on: August 10, 2015, 07:47:25 pm »
Hey all! Quick question, how do I program Parallax Scrolling? I want to add an effect similar of that in ChargeMan's stage with the moving train in the background in my Megaman 4 hack. I located the code of the effect in Megaman 5, and tried to copy it perfectly, but It doesn't work. The screen does indeed scroll to the speed that I choose, but it scrolls almost the entire screen instead of just the top of the screen, like I want.

I've read the NesDev article on PPU scrolling, and I understand the main registers, but that still doesn't help me.

Can someone give me a rundown of Parallax Scrolling?

dougeff

  • Sr. Member
  • ****
  • Posts: 359
    • View Profile
Re: How to program Parallax Scrolling
« Reply #1 on: August 10, 2015, 11:54:20 pm »
Mega man 4 uses MMC3, which is good, it has a system to count scan lines, and you can generate an IRQ mid-screen, and adjust the scroll in the IRQ handler. It is easy to change the horizontal scroll midscreen, and you can do it multiple times if needed. I found this tutorial on how to set this up...http://bobrost.com/nes/files/mmc3irqs.txt


August 11, 2015, 12:06:23 am - (Auto Merged - Double Posts are not allowed before 7 days.)
On second thought, maybe its only easy to change the x scroll once midscreen. I don't think I've ever tried to do more than that.

August 11, 2015, 10:30:18 am - (Auto Merged - Double Posts are not allowed before 7 days.)
I haven't looked at Megaman5 to see how it does it, but if it IS an MMC3 scanline IRQ, the fix would be to reduce the scanline count. Or, perhaps you need to set it up earlier, like during V-blank.
« Last Edit: August 11, 2015, 10:31:50 am by dougeff »
nesdoug.com -- blog/tutorial on programming for the NES

Disch

  • Hero Member
  • *****
  • Posts: 2746
  • NES Junkie
    • View Profile
Re: How to program Parallax Scrolling
« Reply #2 on: August 11, 2015, 12:16:10 pm »
dougeff is correct.  You'll want to use the mapper IRQ to interrupt at a certain scanline, then change the scroll in the IRQ handler to "split the screen" to make it appear that the background is scrolling slower than the foreground.


During VBlank / NMI Handler:
- Set scroll to "background" scroll rate
- Make sure IRQ mask flag is clear (CLI)
- Set up IRQ counter to trip on (or slightly before) the scanline you want the screen split to occur


In IRQ Handler:
- Set scroll to "foreground" scroll rate

Gavzila

  • Jr. Member
  • **
  • Posts: 18
    • View Profile
Re: How to program Parallax Scrolling
« Reply #3 on: August 11, 2015, 12:52:23 pm »
Thanks guys! I really appreciate the help! I'll be sure to post an update of what I've done when I get it to work!

dougeff

  • Sr. Member
  • ****
  • Posts: 359
    • View Profile
Re: How to program Parallax Scrolling
« Reply #4 on: August 11, 2015, 05:24:01 pm »
Mega man 5 does use IRQs like this. And, several times per frame. To see it, run it in FCEUX, go to the part of the level with splitscreen, and set a breakpoint for writes to 2005. You can see how it also (nearby) writes to C000 and E000 to set up an IRQ.
nesdoug.com -- blog/tutorial on programming for the NES

Gavzila

  • Jr. Member
  • **
  • Posts: 18
    • View Profile
Re: How to program Parallax Scrolling
« Reply #5 on: August 20, 2015, 01:27:48 am »
Update! So I managed to create simple IRQ breaks! For example, I would make a Scan line break at $C0 (192 scan line) and have it simply not move, more slower than the rest of the screen, have it move faster, etc.

But how do I create a break that only effects between certain scan lines? For example, I want to the screen to split between scan line $20 and scan line $C0, and have it scroll slower than the rest of the screen.

What I tried to do was create an IRQ at $20, then create a seperate IRQ at $C0, but that ended up making the screen blink back and forth every frame to each IRQ. Not very pleasant to look at.

Does anyone know how to do this?

Disch

  • Hero Member
  • *****
  • Posts: 2746
  • NES Junkie
    • View Profile
Re: How to program Parallax Scrolling
« Reply #6 on: August 20, 2015, 10:37:57 am »
Quote
What I tried to do was create an IRQ at $20, then create a seperate IRQ at $C0, but that ended up making the screen blink back and forth every frame to each IRQ.

That is exactly what you need to do.

If it's not working you must be doing it wrong.  Can't be any more specific without seeing what you're actually doing.

Gavzila

  • Jr. Member
  • **
  • Posts: 18
    • View Profile
Re: How to program Parallax Scrolling
« Reply #7 on: August 20, 2015, 01:13:13 pm »
I made that reply at about midnight - 1am last night, after about a three hour session of IRQ testing, so sorry if it felt a little rushed. I was very annoyed, irritated, tired, etc.

This is what I'm trying to do, I'll post the code also. Throughout my testing of MegaMan 4, $933E (7934E) which is a JMP to $938D, gets executed in every level. (Well, every boss stage, don't know about castle yet) I change the JMP to $BD9A, which is just junk space at the end of bank 3D (or 1E according to the debugger)

This is the following code that SETS UP the IRQ;

Code: [Select]
LDA $22 ;Checks if you're in
CMP #$7 ;SkullMan's stage, and if
BNE RETURN ;you aren't, skip this routine.

LDA $95
AND #$1
CLC ;I'm actually quite proud of this code... I made it myself. Code grows up so fast... *sniff*
ROR A ;What I wanted to do was make a routine that activates each IRQ every other frame. Well,
ROR A ;$95 is a Frame Counter built into MegaMan 4's engine. I had the idea of comparing the numbers in binary
ROR A ;when I realized all ODD binary numbers ALWAYS end with 1. But... you can't JUST compare the last bit of each number...
ROR A ; So what this code does, is it AND's $95 with #$1, which either makes the number "0000 0000" if even,
ROR A ;or, "0000 0001" if odd. The CLC and all seven ROR A's are to shift the right most "0" or "1" to the left without carry,
ROR A ;so it becomes #$80, if odd, or #$0, if even. It then branches to IRQ2 if it is an even frame.
ROR A         ;Cool isn't it?
CMP #$80
BNE IRQ2

IRQ1:
 LDA #$1 ;Loads and stores #$1 into $F0, which
 STA $F0 ;controls the IRQ handler.
 LDA #$20        ;Loads and stores #$20 into $F1, which
 STA $F1 ;controls the Scan lines that are stored at $C000 and $C001.
 JMP $938D ;Return to normal routine.

IRQ2:
 LDA #$2 ;Loads and stores #$2 into $F0, which
 STA $F0 ;controls the IRQ handler.
 LDA #$C0 ;Loads and stores #$C0 into $F1, which
 STA $F1 ;controls the Scan lines that are stored at $C000 and $C001

RETURN:
 JMP $938D ;Return to the normal routine.

Like I said, that SETS UP the IRQs and the Scan lines that should be stored at $C000 and $C001. $F0 and $F1 get copied to $5D and $5C which Controls both things respectively, which is why I can't store them to $5D and $5C directly. It is convenient though, because the game does everything for me.

Now it's time for the actual IRQ code.

Here's what the first IRQ's code looks like;

Code: [Select]

LDA $2002 ;Unlatches the VRam address at $2006
LDA #$80 ;The next to commands loads #$80 into the accumulator
LDY #$22 ;and #$22 into Y that will be stored into $2006 for a VRAM address of $2080.
STY $2006
STA $2006
LDA $FF
STA $2000
LDA #$0 ;Because this is to control scan lines #$20 down, I'm loading #$0 into $2005 so that it doesn't scroll.
STA $2005 ;I'll change it later after I get the effect working so that it slowly scrolls instead of just staying still.
LDA $65
STA $2005
JMP $C30E ;Returns to the normal routine.


Remember, this is in the IRQ handler of Megaman 4, which has 10 "Entry Points" for IRQ code. Only about 7 of them are used, so I have a whopping THREE entry points I can use. Or I could simply replace my code with existing IRQ code, but we'll cross that bridge when I get there.

Now, remember, on every other frame it switches to a second IRQ, which code is this;

Code: [Select]

LDA $2002
LDA #$0
LDY #$23
STY $2006
STA $2006
LDA $FF
STA $2000
LDA $66 ;Code is almost identical to IRQ1, only that the VRAM address is $2300, and because I only want the scanlines
STA $2005 ;between $20 and $C0 to be affected, this IRQ just makes the scanlines past $C0 to just scroll properly.
LDA $65
STA $2005
JMP $C30E


Now, I didn't do much testing on this, but I'm pretty sure that the flickering of the screen is caused by my "Other Frame" IRQ technique.

That's pretty much the code I used to make my first IRQ break. Hopefully you guys could help me understand this better now that you (hopefully) got a better idea about what I'm doing.

I apologize if this reply got long and... "cringe" worthy with my notes. I tend to naturally explain things as if the people I'm talking to don't know what I'm talking about. So I apologize for that.

Disch

  • Hero Member
  • *****
  • Posts: 2746
  • NES Junkie
    • View Profile
Re: How to program Parallax Scrolling
« Reply #8 on: August 20, 2015, 03:36:48 pm »
Setup

Your ROR fest seems to be the problem, as the code does not do what you intended.

Let's say 'v' is our frame counter, C is the C flag.  a dot '.' is uninteresting bits, and x is the bit you're interested in:

Code: [Select]
v=.... ...x   C=.   <- start with this
v=x... ....   C=.   <- goal is this

v=.... ...x   C=.   <- start
v=.... ....   C=x   <- ROR x1
v=x... ....   C=.   <- ROR x2
v=.x.. ....   C=.   <- ROR x3
v=..x. ....   C=.   <- ROR x4
v=...x ....   C=.   <- ROR x5
v=.... x...   C=.   <- ROR x6
v=.... .x..   C=.   <- ROR x7

As you can see, it's not quite what you want.  You actually achieve your goal after 2x RORs (1st to move bit 0 into C, and 2nd to move C into bit 7).
Also note that CLC+ROR is the same as LSR.  So you can just LSR the first time to get rid of the CLC

Though really -- what is the point of moving that bit to bit 7 position anyway?  You never do anything with it after that.  If all you want is to do something on odd frames... you could just mask out the low bit and be done with it:

Code: [Select]
LDA $95     ; get frame counter
AND #1      ; is it an odd frame?
BNE IRQ2    ; if yes, branch to IRQ2


Though -- regardless... you don't want to do this every other frame.  A frame is a full image.  This means that every other on-screen image will be split at different spots.  This will not split the screen in two separate places, but instead will create a hellish flicker due to the frame being drawn differently every time.

Each frame should be set up the same way:

- set the IRQ to trip at scanline $20 (handler 1)
- set scroll for top portion of screen
- wait for IRQ
- IRQ Handler 1 (line $20):  set IRQ to trip at scanline $C0 (handler 2)
- IRQ Handler 1 (line $20):  set scroll for middle portion of screen
- IRQ Handler 2 (line $C0):  disable IRQ (no more needed this frame)
- IRQ Handler 2 (line $C0):  set scroll for bottom portion of screen


So realy... the setup code should look like this:

Code: [Select]
    LDA $22         ;Checks if you're in
    CMP #$7         ;SkullMan's stage, and if
    BNE RETURN      ;you aren't, skip this routine.

        LDA #1
        STA $F0
        LDA #$20
        STA $F1
   
RETURN:
    JMP $938D   ; why a jump instead of an RTS?  Are you JMPing here only to JMP back to where you came from?
                ;  If so, it's easier if you JSR here so you can just RTS back to where you came from.



IRQ Handler

$2006 and $2005 both mess with the scroll.  There's no need for you to touch both of them.  Since you're doing $2000+$2005 writes, your $2006 writes are pretty much worthless here.
Also note that the low bit of $2000 controls which nametable is scrolled to.  So you need to zero that bit if you want to remove all horizontal scrolling.

Apart from that, you have the right idea:

Code: [Select]
; First IRQ handler:

LDA $2002   ; probably not strictly necessary to do this, but it's a good idea.  Kudos.

LDA $FF
AND #$FE    ; turn off low bit to make sure the PPU scrolls uses $2000 NT and not the $2400 NT
STA $2000

LDA #0
STA $2005   ; kill horizontal scroll
STA $2005   ; vertical scroll doesn't really matter -- as that can't be changed mid-frame without $2006 writes

LDA #2
STA $F0     ; next IRQ to use handler 2
LDA #$A0    ;   set scanline counter to $A0, which is $C0-$20  .. because MMC3 is a "count down" counter, meaning we have to tell it how
STA $F1     ;   many scanlines we want to wait... not which scanline we want to fire on.

;  Note that the $F0/$F1 writes here will only work if the IRQ code does the appropriate writes to $C000/1 $E000/1 before it RTI's
;  out of the interrupt.  I'm going to assume the game does -- but if this doesn't work, then that's something to look at.  If the
;  game doesn't do that, you'll have to do it manually here.

JMP $C30E   ; <- ...not RTS?

So in addition to setting the scroll to split the screen, we also set up the next IRQ.  Note that the $A0 value here might take some tinkering as it assumes the IRQ routine completes in less than 1 scanline worth of time.  You might have to subtract 1 or 2 from that value to account for IRQ overhead.

For the next IRQ, you just return scroll to it's original place:
Code: [Select]
; 2nd IRQ handler:

LDA $2002
LDA $FF
STA $2000
LDA $66
STA $2005
STA $2005

JMP $C30E



Quote
I apologize if this reply got long and... "cringe" worthy with my notes. I tend to naturally explain things as if the people I'm talking to don't know what I'm talking about. So I apologize for that.

No apology necessary.  I much prefer clarity over ambiguity.

Gavzila

  • Jr. Member
  • **
  • Posts: 18
    • View Profile
Re: How to program Parallax Scrolling
« Reply #9 on: August 20, 2015, 03:48:40 pm »
Disch, I thank you tremendously for helping me with this topic! I was actually pondering the idea of storing the pointers for IRQ2 DURING the actual IRQ and see how that goes, but I never got to it.

Actually, I made a mistake in my reply. It should be "ROL" not "ROR." But I thank you for going into it! Anything thing helps really. I never thought about masking the bite... which is technically what I did, but you see how that ended up. XD

The reason why it's a JMP instead of a JSR is because JMP $933E actually jumps to a JSR routine. If I were to use a JSR, which I actually thought of using, but I just used a JMP to keep it simple.

I haven't tried the code yet, but I'll be sure to post a couple pictures and so on when I get it working!

Disch, thanks again!
« Last Edit: August 20, 2015, 04:04:19 pm by Gavzila »

dougeff

  • Sr. Member
  • ****
  • Posts: 359
    • View Profile
Re: How to program Parallax Scrolling
« Reply #10 on: August 20, 2015, 09:03:37 pm »
And, don't forget to do this...

IRQ:
  pha
   txa
   pha
   tya
   pha
  ...
EndofIRQ:
  Pla
  Tay
  Pla
  Tax
  Pla
  RTI

Or else your game will have lots of problems and possibly crash. The IRQ will break from the Main code at a random point, and when it returns the registers will have the wrong value (if you don't restore them)

August 20, 2015, 09:49:40 pm - (Auto Merged - Double Posts are not allowed before 7 days.)
Also. 2 things to consider. You may have to subtract 1 from the scanline count to get it to split at the exact point you want.

And, you might want to have a very short wait loop before you change the scroll midscreen, to avoid a midscreen glitch by making sure the scroll change happens at the h_blank period (when the scanline is at the far right and isn't drawing for a few cycles while it jumps to the left side of the screen to start the next scanline).

I say "might" because if the scroll split happens at a plain mono-color portion of the screen, it may not be noticeable if the scroll change happens one scanline down or while its in the middle of the screen.

August 20, 2015, 09:54:30 pm - (Auto Merged - Double Posts are not allowed before 7 days.)

More blah blah from me...
The scanline IRQ happens when the scanline is at the far right, and by the time you do all that PHA crap and LDA STA $2005 stuff, you will be well into the next scanline. Which is why I say to subtract 1 from the count, and add a very small wait loop in the IRQ handler. Let me know if you have any problems... I can go into more detail.
« Last Edit: August 20, 2015, 09:54:30 pm by dougeff »
nesdoug.com -- blog/tutorial on programming for the NES

Gavzila

  • Jr. Member
  • **
  • Posts: 18
    • View Profile
Re: How to program Parallax Scrolling
« Reply #11 on: August 20, 2015, 10:54:18 pm »
*sigh* I'm either really... I mean REALLY stupid, or Megaman 4 just HATES me! But... The code doesn't work... :banghead: I apologize in advance for the annoyance... again. I really did think that Disch's last reply solved everything, but unfortunately, I'm back... Thanks dougeff for the reply! You guys have been very helpful in my efforts of trying to tackle this "beast."

SO! Disch, I never knew you wrote Documents on all of the NES mappers, and I even referenced your MMC3 mapper document to see what you had about IRQs, and have testing for about the last couple hours or so with no avail. Let's see if I can explain what I'm dealing with, when using Megaman 4's IRQ handler, maybe that might help?

As I said, Megaman 4 already has an IRQ handler, and it already includes the code that you posted dougeff. Here's what it looks like;

Code: [Select]
PHP
PHA
TXA
PHA
TYA
PHA
STA $E000
STA $E001
JMP ($009C)

This code is located at 7C159. It pushed everything into the stack, like dougeff said, and then does an indirect jump from $9C, which stores the IRQ effect. (i.e #$1, #$2, #$3, like when I was storing #$1 into $F0) So, according to our code, the IRQ effect is one, so it's going to JMP to C158. Now, according to Disch, I need store #$A0, or whatever Scanline I would need, into $C000 and $C001, and write to $E000 and $E001 before the RTI at 7C327. Sadly, the IRQ Handler only does one IRQ at a time, meaning, like Disch said, I need to do this manually set the second IRQ.

SO! I modified the first IRQ code to look like this;

Code: [Select]
LDA $2002
LDA $FF
AND #$FE
STA $2000
LDA #0
STA $2005
STA $2005
$E000
LDA #$A8
STA $9C
LDA #$C1
STA $9D
LDA #$A0
STA $C000
STA $C001
JMP $C152

The beginning is normal, just like the code Disch posted, but I modified the ending to setup for a second IRQ.

I wrote to $E000, to disable to IRQ, then I stored the necessary values into $9C and $9D. (We'll get to that shortly.) Then I loaded $A0, the Scanline that I want our second IRQ to break at, into $C000 and $C001. I then jumped BACK to $C152;

Code: [Select]
STA $E001

Which enables the IRQ once again. Now, this is also why I stored the values I did earlier into $9C and $9D. The CPU wasn't going to get back to the area in the NMI routine where it would've stored the values into $9C and $9D via a STX, which is why I needed to store them directly. Here's the code for reference.

Code: [Select]
LDX $005D
LDA $C318,X
STA $009C
LDA $C324,X
STA $009D

$F0 is copied to $5D by the way, and we can't directly store to $5D because it will just get overwritten when $F0 is eventually stored there during the NMI routine. $C318 is where all the LOW bytes are stored, while $C324 is where all the HIGH bytes are stored for the IRQ effect addresses.

Anyway,

When I jump back to $C152, I enable the IRQ effect once more, then using the JMP ($9C), I send it to the second "Entrance Point" of the IRQ handler, and activating our second IRQ code that Disch posted in his response before.

Code: [Select]
LDA $2002
LDA $FF
STA $2000
LDA $66
STA $2005
STA $2005
JMP $C30E

It then jumps to $C30E where it disables the IRQ effect, pulls all the status from the stack, and returns from the interrupt.

Code: [Select]
STA $E000
PLA
TAY
PLA
TAX
PLA
PLP
RTI

Now, here's the kicker, the code actually works. But, it still only effects the scanlines #$20 down, while using the scroll code we used in the second IRQ. So pretty much, it's nothing more than a glorified IRQ.

To be honest, I practically now this IRQ handler inside and out, and it annoys me that I can't understand this somewhat simple procedure. I always hate having to come here and ask this many times about one thing...
« Last Edit: August 20, 2015, 11:00:20 pm by Gavzila »

Disch

  • Hero Member
  • *****
  • Posts: 2746
  • NES Junkie
    • View Profile
Re: How to program Parallax Scrolling
« Reply #12 on: August 21, 2015, 12:19:51 am »
Quote
As I said, Megaman 4 already has an IRQ handler, and it already includes the code that you posted dougeff. Here's what it looks like;

That code is writing to $E000 and $E001 already.  Effectively what that's doing is:
$E000 = acknowledge (and disable) IRQs
$E001 = re-enables them.

The end result is that IRQs remain enabled, but the one that just fired is "acknowledged" so that another IRQ does not fire immediately after an RTI.  (once an IRQ fires -- you can think of it as firing constantly over and over again until you acknowledge it).

With this said... the only thing you'll have to do to fire another IRQ is set the reload counter ($C000), then make it actually reload ($C001).  You will not have to touch $E000/$E001 at all.

You CAN do what you're doing now and write to $E000/E001 again.  As long as you write to $E001 after E000, you'll be fine.  It's just unnecessary.

Quote
SO! I modified the first IRQ code to look like this;

That looks good.  (though again you don't need the $E000 write)

Quote
I send it to the second "Entrance Point" of the IRQ handler, and activating our second IRQ code that Disch posted in his response before.

That also looks good.

Quote
It then jumps to $C30E where it disables the IRQ effect, pulls all the status from the stack, and returns from the interrupt.

It doesn't hurt to leave IRQs enabled -- so you can probably scrap the $E000 write there too.

Is IRQ1's code eventually jumping to $C30E?  Like to exit the interrupt?  If yes, that $E000 is disabling IRQs and killing your 2nd IRQ.


If you want to see what's really happening here do the following:

1)  Set breakpoints on writes to $C000/$C001/$E000/$E001
2)  Run the game for a frame
3)  Write down the following:
--- What order are those regs written to?
--- What value is being written?  (only really matters for $C000 -- the value written to other regs doesn't matter)
--- What scanline are they being written on?


If you do this (and post it) we'll be able to see exactly which write is screwing up the works -- and thus see exactly where the code is going wrong.

dougeff

  • Sr. Member
  • ****
  • Posts: 359
    • View Profile
Re: How to program Parallax Scrolling
« Reply #13 on: August 21, 2015, 12:29:16 am »
I got to be honest, I can't follow all your code. I don't know why you're storing values at $F0 and $F1, or why reading from $95 "sets up the IRQ".

This is what I would do...

This sets up the first IRQ to work at scanline $20
Code: [Select]
Beginning of NMI:
lda #$01
sta $e000
lda #$1f     ;or $20, I think 1 fewer lines the first up better
sta $c000
sta $c001
lda #$01
sta $e001
blah, near end of NMI code...
lda #$00
sta $xx   ;flag for which IRQ


This is the first IRQ (minus the PHA stuff)
-sets up the next IRQ count, and shifts the scroll
Code: [Select]
IRQ:
lda $xx    ;flag for which IRQ
bne IRQ2
lda #$01
sta $e000
lda #$a0
sta $c000
sta $c001
lda #$01
sta $e001
lda $2002  ;resets scroll latch
then lda x / sta new scroll to $2005
inc $xx  ;sets the flag to go to IRQ2 next break.

Then the last IRQ, turns off more IRQs and sets new scroll
Code: [Select]
IRQ2:
lda #$01
sta $E000
lda $2002  ;resets scroll latch
then lda x / sta new scroll to $2005
nesdoug.com -- blog/tutorial on programming for the NES

Disch

  • Hero Member
  • *****
  • Posts: 2746
  • NES Junkie
    • View Profile
Re: How to program Parallax Scrolling
« Reply #14 on: August 21, 2015, 12:32:52 am »
The problem, dougeff, is that he has to work within the game's existing code because IRQs are used for other things elsewhere in the game.  So he can't completely scrap and rewrite the IRQ handler from scratch.

His code seems like it would work except for that $E000 write before the RTI that might be screwing up the works.

Gavzila

  • Jr. Member
  • **
  • Posts: 18
    • View Profile
Re: How to program Parallax Scrolling
« Reply #15 on: August 21, 2015, 01:13:29 am »
Actually, if you want to get technical, I could completely redo Megaman 4's IRQ Handler. It always enters at $C149, so I could replace literally everything with EA (NOP) BUT the php, pha, txa, ect., replace it with my own code, and theoretically work just fine. Just an idea to ponder...

August 21, 2015, 03:05:16 pm - (Auto Merged - Double Posts are not allowed before 7 days.)
As requested by Disch, here's a disassembly of the order the registers are in, whats written to them, and the Scan line they're written on. Let's hope this issue ends here and now!

The first register to be written to is $C000 with the value of $20 (Our Scanline). It's being written on Scanline 246. This is during the NMI routine, so we haven't reached the IRQ handler yet.

Code: [Select]
F:C0D4: STA $C000

Immediately after that our Scanline value (still #$20) is being written to $C001, still on Scanline 246, and still in the NMI routine.

Code: [Select]
1F:C0D7: STA $C001

Almost immediatly after that, #$20 is written to $E001, on Scanline 247, like I said before, still in the NMI routine.

Code: [Select]
1F:C0DC: STA $E000,X

Just to clarify, #$1 is loaded into the X register right before the write to $E000,X. #$20 is still loaded into the accumulator, which is why I said #$20 is written to $E001, even though that really doesn't matter.

NOTE!: We our now in MegaMan 4's IRQ handler!

#$0 is written to $E000 on Scanline 32.

Code: [Select]
>1F:C14F: STA $E000

Then #$0 is immediatly stored to $E001 at Scanline 32.

Code: [Select]
1F:C152: STA $E001

#$A0 is written to $C000 on Scanline 32. This is during our first IRQ code.

Code: [Select]
1F:C177: STA $C000

#$A0 is then immediatly written to $C001, also on Scanline 32.

Code: [Select]
1F:C17A: STA $C001

At the end of our first IRQ, we jump back to $C152, which leads us to our next register write. #$A0 is written to $E001 on Scanline 32.

Code: [Select]
1F:C152: STA $E001

And finally, at the end of our second IRQ code, we jump to $C30E which writes #$0 into $E000 at Scanline 32.

Code: [Select]
1F:C30E: STA $E000

So to recap, it goes;

$C000
$C001  ; This is during the NMI routine
$E001

$E000
$E001
$C000   ; This is during the IRQ handler (Before the RTI command, which still makes this during the NMI routine)
$C001
$E000

Sorry if I made that a little confusing, I'll experiment a little but more with the code, and see if anything works.
« Last Edit: August 21, 2015, 03:07:20 pm by Gavzila »

Disch

  • Hero Member
  • *****
  • Posts: 2746
  • NES Junkie
    • View Profile
Re: How to program Parallax Scrolling
« Reply #16 on: August 21, 2015, 03:34:39 pm »
Wow that was really verbose.  I ended up transcribing it to a more raw data form.  Here's a look:

Code: [Select]
C000 = 20   @ 246
C001        @ 247
E001        @ 247
E000        @ 32
E001        @ 32
C000 = A0   @ 32
C001        @ 32
E001        @ 32
E000        @ 32

We can clearly see the problem here.  That last write of E000 is disabling IRQs.  And that's happening on scanline 32 -- which is before the 2nd IRQ has tripped (it won't trip until scanline 192).  Meaning you are disabling IRQs and thus preventing the 2nd IRQ from ever happening.

Quote
And finally, at the end of our second IRQ code, we jump to $C30E which writes #$0 into $E000 at Scanline 32.

Your 2nd IRQ code should NOT be running on scanline 32.  You are either jumping somewhere you shouldn't be... or you are not in the 2nd IRQ code like you think you are.  You probably are just at the end of your 1st IRQ code.

Gavzila

  • Jr. Member
  • **
  • Posts: 18
    • View Profile
Re: How to program Parallax Scrolling
« Reply #17 on: August 21, 2015, 03:37:33 pm »
UPDATE!!!: I GOT IT! I DID IT! THANK YOU GUYS SO MUCH!

Disch, you were COMPLETELY write about the JMP to $C30E. I had a look at Megaman 5's code, which I referenced frequently due to how similar the code between the games are, and finally figured out what it was doing. I don't really know how to explain it, but it was jumping to a different RTI, completely avoiding $E000 at the end. But this is what got me, I tried skipping the $E000 at $C30E, and it still didn't work, which must mean that the RTI I skipped to must lead to someplace different.

Here's a gif of the effect!

http://imgur.com/aI4fiwW

(the "Insert Image" didn't display the gif directly, so I just changed it to the link.)

Thanks Disch! Thanks Dougeff! I SERIOUSLY appreciate the help you guys have given me! And with this knowledge in hand, I shall create something awesome! Well... in my opinion...

I changed up the IRQ1 code, so here's what it looks like;

Code: [Select]
LDA $2002
LDA $FF
AND #$FE
STA $2000
LDA #0
STA $2005
STA $2005
LDA #$A8
STA $9C
LDA #$C1
STA $9D
LDA #$9D
STA $C000
STA $C001
JMP $C120

As you can see, I'm jumping to $C120 instead if $C30E. I messed around with the Scanlines which is why it's loading $9D instead of $A0.

Everything else isn't changed. IRQ2 still even jumps to $C30E.

Code: [Select]
LDA $2002
LDA $FF
STA $2000
LDA $66
STA $2005
STA $2005
JMP $C30E

And it works. Which is awesome! Megaman 4... you be weird with these IRQ's!
« Last Edit: August 21, 2015, 03:46:10 pm by Gavzila »

Disch

  • Hero Member
  • *****
  • Posts: 2746
  • NES Junkie
    • View Profile
Re: How to program Parallax Scrolling
« Reply #18 on: August 21, 2015, 03:43:17 pm »
Yay!  Glad it's working.   :D

dougeff

  • Sr. Member
  • ****
  • Posts: 359
    • View Profile
Re: How to program Parallax Scrolling
« Reply #19 on: August 21, 2015, 03:56:41 pm »
 :thumbsup:
nesdoug.com -- blog/tutorial on programming for the NES