Page 1 of 2

IRQ Templates and Best Practices?

Posted: Thu Mar 08, 2018 3:37 pm
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?"

Re: IRQ Templates and Best Practices?

Posted: Thu Mar 08, 2018 7:53 pm
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.

Re: IRQ Templates and Best Practices?

Posted: Thu Mar 08, 2018 9:02 pm
by pubby
Make sure interrupts can't fire when you're updating the indirect jump pointer. (use SEI / CLI)

Re: IRQ Templates and Best Practices?

Posted: Thu Mar 08, 2018 9:20 pm
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.

Re: IRQ Templates and Best Practices?

Posted: Thu Mar 08, 2018 9:42 pm
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!

Re: IRQ Templates and Best Practices?

Posted: Thu Mar 08, 2018 10:04 pm
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

Re: IRQ Templates and Best Practices?

Posted: Fri Mar 09, 2018 4:48 am
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.

Re: IRQ Templates and Best Practices?

Posted: Fri Mar 09, 2018 5:19 am
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

Re: IRQ Templates and Best Practices?

Posted: Fri Mar 09, 2018 6:16 am
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.

Re: IRQ Templates and Best Practices?

Posted: Fri Mar 09, 2018 2:27 pm
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.

Re: IRQ Templates and Best Practices?

Posted: Sat Mar 10, 2018 4:18 am
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 (...) {

}

Re: IRQ Templates and Best Practices?

Posted: Sat Mar 10, 2018 6:53 am
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.

Re: IRQ Templates and Best Practices?

Posted: Sun Mar 11, 2018 9:40 pm
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.

Re: IRQ Templates and Best Practices?

Posted: Sun Mar 11, 2018 10:11 pm
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.

Re: IRQ Templates and Best Practices?

Posted: Mon Mar 12, 2018 3:28 pm
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.