It is currently Tue Jun 19, 2018 7:44 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 16 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Thu Mar 08, 2018 3:37 pm 
Offline
User avatar

Joined: Wed Sep 21, 2016 12:08 pm
Posts: 83
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?"


Top
 Profile  
 
PostPosted: Thu Mar 08, 2018 7:53 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10513
Location: Rio de Janeiro - Brazil
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.


Top
 Profile  
 
PostPosted: Thu Mar 08, 2018 9:02 pm 
Offline
User avatar

Joined: Thu Mar 31, 2016 11:15 am
Posts: 308
Make sure interrupts can't fire when you're updating the indirect jump pointer. (use SEI / CLI)


Top
 Profile  
 
PostPosted: Thu Mar 08, 2018 9:20 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10513
Location: Rio de Janeiro - Brazil
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.


Top
 Profile  
 
PostPosted: Thu Mar 08, 2018 9:42 pm 
Offline
User avatar

Joined: Wed Sep 21, 2016 12:08 pm
Posts: 83
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!


Top
 Profile  
 
PostPosted: Thu Mar 08, 2018 10:04 pm 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 20159
Location: NE Indiana, USA (NTSC)
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


Top
 Profile  
 
PostPosted: Fri Mar 09, 2018 4:48 am 
Offline
User avatar

Joined: Wed Sep 21, 2016 12:08 pm
Posts: 83
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.


Top
 Profile  
 
PostPosted: Fri Mar 09, 2018 5:19 am 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2091
Location: DIGDUG
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


Top
 Profile  
 
PostPosted: Fri Mar 09, 2018 6:16 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 20159
Location: NE Indiana, USA (NTSC)
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.


Top
 Profile  
 
PostPosted: Fri Mar 09, 2018 2:27 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10513
Location: Rio de Janeiro - Brazil
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.


Top
 Profile  
 
PostPosted: Sat Mar 10, 2018 4:18 am 
Offline

Joined: Tue Oct 06, 2015 10:16 am
Posts: 746
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:
enum {
IRQ_SCROLL,
IRQ_SOMETHINGELSE
};

...

if (irq_type == IRQ_SCROLL) {

} else if (irq_type == IRQ_SOMETHINGELSE) {

} else if (...) {

}


Top
 Profile  
 
PostPosted: Sat Mar 10, 2018 6:53 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10513
Location: Rio de Janeiro - Brazil
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.


Top
 Profile  
 
PostPosted: Sun Mar 11, 2018 9:40 pm 
Offline
User avatar

Joined: Wed Sep 21, 2016 12:08 pm
Posts: 83
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.


Top
 Profile  
 
PostPosted: Sun Mar 11, 2018 10:11 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10513
Location: Rio de Janeiro - Brazil
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.


Top
 Profile  
 
PostPosted: Mon Mar 12, 2018 3:28 pm 
Offline
User avatar

Joined: Wed Sep 21, 2016 12:08 pm
Posts: 83
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?

Attachment:
Untitled.png
Untitled.png [ 35.32 KiB | Viewed 654 times ]


I've attached the project file in a ZIP.


Attachments:
MMC3 Code.zip [51.69 KiB]
Downloaded 18 times
Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 16 posts ]  Go to page 1, 2  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 4 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