IRQ Templates and Best Practices?

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

User avatar
Lucradan
Posts: 101
Joined: Wed Sep 21, 2016 12:08 pm

IRQ Templates and Best Practices?

Post by Lucradan »

I am programming the MMC3 in ASM6 and am at a point where I need to use its IRQs. Is there any templates or best practices for filling in the IRQ subroutine?

Here is the basic template I was considering:

1. Save Registers
2a. Indirect Jump to local IRQ code ending with a direct jump back to main IRQ code.
2b. Use an RTS Jump Table
3. Restore Registers

(2b) is the method I typically use for changing program control. The concern is that the RTS tables is universal and is managed in a file (and bank) seperate from all the local code. In my particular case, my game is divided into "scenes" each with their own local IRQs. The addresses for those local IRQ have to be added to an RTS table that's located in the fixed PRG bank file (where the RESET/NMI code resides). I don't mind doing it this way, but it can be a little annoying at times. Is this the "best practice?"
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: IRQ Templates and Best Practices?

Post by tokumaru »

An RTS jump table is a terrible idea unless you really need different IRQ handlers every time an IRQ fires, but I assume you're gonna use the same handler several times in a row, in which case it's much more efficient to store the address in RAM once and use an indirect JMP each time (or even direct the IRQ vector to RAM and put a "JMP $XXXX" instruction there), than to fetch the same address every single time.
User avatar
pubby
Posts: 583
Joined: Thu Mar 31, 2016 11:15 am

Re: IRQ Templates and Best Practices?

Post by pubby »

Make sure interrupts can't fire when you're updating the indirect jump pointer. (use SEI / CLI)
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: IRQ Templates and Best Practices?

Post by tokumaru »

When the NMI vector points to RAM, I actually change the JMP ($4c) to RTI ($40) while updating the pointer, since it's faster (if using ZP) than disabling NMIs via $2000, and doesn't have undesirable side effects like the glitchy scanline in SMB. For IRQs though, yeah, SEI and CLI are more convenient.
User avatar
Lucradan
Posts: 101
Joined: Wed Sep 21, 2016 12:08 pm

Re: IRQ Templates and Best Practices?

Post by Lucradan »

At this point I'm only trying to implement an IRQ that changes the scrolling (for splitscreen and parallax effects). Would anyone be willing to share their code on how they did this? Especially for multiple screen splits.

Thank you!
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: IRQ Templates and Best Practices?

Post by tepples »

Of the following, which do you want someone to explain first?
  1. How the X position for each strip is calculated depending on camera position and time
  2. How to send each X position to the MMC3 at the appropriate time
  3. How to do multiple screen splits without an MMC3
User avatar
Lucradan
Posts: 101
Joined: Wed Sep 21, 2016 12:08 pm

Re: IRQ Templates and Best Practices?

Post by Lucradan »

I don't think I'm asking the right question. Let me try this again. Here some examples I'm trying to figure out. Please correct me if I've made a mistake in my thought process.

CASE 1:

Let's say I want a single IRQ split around scanline 32. I could have a horizontal scrolling game with the status bar at the top and fixed in the first PPU nametable slot.

In the VBLANK I'd set the PPUSCROLL registers (writes to #$2005) both to 0 (versus the variables VSCROLLX and VSCROLLY). I would then set the IRQ latch (#$C000) to 31 and follow with a write to the IRQ reload. I'd then enable IRQs through a write to #$E001.

In the IRQ subroutine for this case I think I'd have to do the following:

1. Save Register
2. Write a timing loop to catch the next HBLANK at the end of line 32
3. At HBLANK Do two writes to $2005 to restore the scrolling (VSCROLLX and Y)
4. Write to #$E000 to disable future IRQs
5. Restore Registers

CASE 2

Let's say I have two IRQs. One at 32 and another at and one at split around scanline 80. I could have to show a dialog box on single screen game. Let's say the dialog box is stored at the top of the PPU nametable 2.

The VBLANK would be similar to the previous case.

The IRQ would not be because there are now two handlers.

FIRST HANDLER:

1. Save Register
2. Write a timing loop to catch the next HBLANK at the end of line 32
3. At HBLANK Do two writes to $2005 to set the scrolling to X=255 and Y=0
4. Set the IRQ latch to 48 = 80 - 32 and do a write to the IRQ reload register.
5. Restore Registers

SECOND HANDLER:
1. Save Register
2. Write a timing loop to catch the next HBLANK at the end of line 80
3. At HBLANK Do two writes to $2005 to set the scrolling to X=0 and Y=80
4. Write to #$E000 to disable future IRQs
5. Restore Registers

My original question was how I could reconcile these two handlers into a single IRQ. From my interpretation of the responses, it looks like the best practice is to have an indirect JMP at the end of the timing loop. This could allow me to customize the IRQ to my needs whether its changing the scrolling or swapping CHR banks.
User avatar
dougeff
Posts: 3079
Joined: Fri May 08, 2015 7:17 pm

Re: IRQ Templates and Best Practices?

Post by dougeff »

If you want an alternative, then have an IRQ state variable.

pseudocode

LDX state
LDA timing_table, x
;if zero, disable
BEQ disable_IRQ
STA (I can't remember the IRQ scanline register)
INC state
nesdoug.com -- blog/tutorial on programming for the NES
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: IRQ Templates and Best Practices?

Post by tepples »

Case 1 sounds correct. But technically, you don't even need an IRQ for a top status bar, as you can arrange for a sprite 0 hit as Super Mario Bros. does it.

Case 2 could be simplified by having the IRQ handler index into an array. The array in the IRQ handler of The Curse of Possum Hollow holds up to eight X positions, bank numbers, and strip heights.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: IRQ Templates and Best Practices?

Post by tokumaru »

You don't need multiple handlers if they all do the same thing (i.e. set the scroll). You can use the same handler and have it pull scroll values from an array in order.
calima
Posts: 1745
Joined: Tue Oct 06, 2015 10:16 am

Re: IRQ Templates and Best Practices?

Post by calima »

Plus, if you only have a few cases, it's not a big deal to get maximum efficiency - you're waiting for hblank anyway.

Code: Select all

enum {
IRQ_SCROLL,
IRQ_SOMETHINGELSE
};

...

if (irq_type == IRQ_SCROLL) {

} else if (irq_type == IRQ_SOMETHINGELSE) {

} else if (...) {

}
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: IRQ Templates and Best Practices?

Post by tokumaru »

calima wrote:you're waiting for hblank anyway.
True. You have this dead time in the IRQ handler anyway, if you can put it to actual use and free some cycles elsewhere, that's better than trying to optimize the handler itself.

For example, if you're doing full X+Y scroll changes (i.e. $2006/5/5/6 trick), it's better to use this otherwise dead time to calculate the values than to sacrifice engine time calculating them beforehand.
User avatar
Lucradan
Posts: 101
Joined: Wed Sep 21, 2016 12:08 pm

Re: IRQ Templates and Best Practices?

Post by Lucradan »

From my understanding the HBlank is about 12 cycles. I'm trying to get my timing down.

1. When the IRQ triggers does the first line of code in the IRQ subroutine excute at 0 cycles or some offset that compensates for the vector jump and stack manipulation? I've seen that the first line starts after CPU cycle 7, not 0, but I can't confirm.

2. How many CPU cycles are between when the IRQ starts and when the NEXT HBlank starts? Or to put it another way: How many CPU cycles are between each HBlank.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: IRQ Templates and Best Practices?

Post by tokumaru »

The wiki says that, under normal circumstances, the IRQ fires at PPU cycle 260, a little after the visible scanline has ended, already during hblank. Add to that the fact that the current instruction has to finish before the IRQ handler will run, and that calling it will take an additional 7 CPU cycles, it's indeed impossible to make any use of that first hblank, so you need to wait for the next one.

From the time the IRQ fires (PPU cycle 260) to the next hblank (PPU cycle 256 of the next scanline), there are 337 PPU cycles, about 112 CPU cycles. Subtract from that the 7 cycles needed to call the IRQ handler and you have around 105 cycles before you hit the next hblank. It could be up to 7 cycles less depending on what instruction is running when the IRQ fires.

Do note that if you're doing the $2006/5/5/6 trick to fully set X and Y scrolling, only the last two writes must happen during hblank, the first two can be done during the visible scanline, so there's plenty of time during hblank to do this safely.
User avatar
Lucradan
Posts: 101
Joined: Wed Sep 21, 2016 12:08 pm

Re: IRQ Templates and Best Practices?

Post by Lucradan »

I deleted my last post because I found some obvious errors causing it not to work.

It works now except for one little issue.

If I set a breakpoint on the IRQ's Indirect JMP (1F:C13E) in FCEUX and press select on the first screen. For the first two 2 triggers of the IRQ, I get the image on the left. This doesn't make sense because the PPU viewer (middle) does NOT have the palettes that the emulator is currently displaying and the image is scrolled in the Y direction by 4 pixel. For every subsequent trigger, the right image appears. Does anyone have any ideas on what might be causing this?
Untitled.png
I've attached the project file in a ZIP.
Attachments
MMC3 Code.zip
(51.69 KiB) Downloaded 114 times
Post Reply