IRQ

Chat about anything CX16 related that doesn't fit elsewhere
User avatar
svenvandevelde
Posts: 488
Joined: Wed Dec 23, 2020 6:30 am
Location: Belgium, Antwerpen

IRQ

Post by svenvandevelde »


This is quite an important sequence for the CX16 to handle interrupts ?.

At the start:

    asm {

        lda $9f21

        pha

        lda $9f20

        pha

        lda $9f24

        pha

        lda $9f23

        pha

        lda $9f22

        pha

        lda $9f25

        pha

    }

 

At the end:

        pla

        sta $9f25

        pla

        sta $9f22

        pla

        sta $9f23

        pla

        sta $9f24

        pla

        sta $9f20

        pla

        sta $9f21


 

Learning by doing ...

KICKC home page by Jesper Gravgaard.
My KICKC alpha with Commander X16 extensions.
SlithyMatt
Posts: 913
Joined: Tue Apr 28, 2020 2:45 am

IRQ

Post by SlithyMatt »


Um... that doesn't seem like a good idea. You could mess up the display or the expected stride by writing to the data ports. Better idea is to only write to VRAM inside the interrupt.

ZeroByte
Posts: 714
Joined: Wed Feb 10, 2021 2:40 pm

IRQ

Post by ZeroByte »



On 5/25/2022 at 11:49 AM, SlithyMatt said:




Um... that doesn't seem like a good idea. You could mess up the display or the expected stride by writing to the data ports. Better idea is to only write to VRAM inside the interrupt.



I think that's why he recommended snapshotting it to the stack first. I made a special wrapped version of the music playback routine in Zsound because it stomps all over the VERA ports too. I recommend against advancing the music during IRQ but if you must, here's a safe function wrapper that takes a few dozen extra cycles to accomplish.... have a nice day.

?

 

User avatar
svenvandevelde
Posts: 488
Joined: Wed Dec 23, 2020 6:30 am
Location: Belgium, Antwerpen

IRQ

Post by svenvandevelde »


Actually it is very tricky ... 

So indeed, during interrupt i first push these vera registers to the stack, and later before interrupt exit, i pull the registers from the stack.

However, I made a mistake ... The DATA registers should NOT be pulled from the stack as the ADDRSEL in CTRL configures if ADDR0 or ADDR1 is addressed.

So when you pull DATA0 and DATA1 from the stack, it messes up the data originally in the vera.

So I have just removed the push and pull of DATA0 and DATA1.

 

image.thumb.png.0aa6f6f1c92bf27f037d38d9946bcbbf.png

 

At the start of the interrupt routine:


        lda $9f20

        pha

        lda $9f21

        pha

        lda $9f22

        pha

        lda $9f25

        pha

 


At the end of the interrupt routine:


        pla

        sta $9f25

        pla

        sta $9f22

        pla

        sta $9f21

        pla

        sta $9f20


 

KICKC home page by Jesper Gravgaard.
My KICKC alpha with Commander X16 extensions.
ZeroByte
Posts: 714
Joined: Wed Feb 10, 2021 2:40 pm

IRQ

Post by ZeroByte »


One thing to note: The above code backs up the currently-selected data port.

If your code then proceeds to use the one that was not active, you'd be screwed.

If you just want to back up and use a specific data port, then return to current state:

; save the current port selection

        lda $9f25

        pha

 ; switch to the desired data port

        lda #desired_data_port_number

        sta $9f25

; back up the state of that data port

        lda $9f20

        pha

        lda $9f21

        pha

        lda $9f22

        pha

        ; Your code here

        ; Use ONLY the desired data port - be sure to leave $9f25 alone during this execution

; restore state of the used data port

        pla

        sta $9f22

        pla

        sta $9f21

        pla

        sta $9f20

; return active port selection to previous state

        pla

        sta $9f25

If you want to back up and use both ports:

; backup currently-active data port

        lda $9f20

        pha

        lda $9f21

        pha

        lda $9f22

        pha

        lda $9f25

        pha

; switch to the other port....

        eor #1

        sta $9f25

; ... and back it up as well

        lda $9f20

        pha

        lda $9f21

        pha

        lda $9f22

        sta

        lda $9f25

        pha



        ; your code goes here. Feel free to use both ports as required.



; restore both data port addresses and previous selected-port state

        pla

        sta $9f25

        pla

        sta $9f22

        pla

        sta $9f21

        pla

        sta $9f20


        pla

        sta $9f25

        pla

        sta $9f22

        pla

        sta $9f21

        pla

        sta $9f20




Note that the order of the CTRL register in these two examples is different!

The first example pushes CTRL first because this guarantees the final write of the restore will be to select the correct active port.

It then switches to the desired data port (which may be the same one that was already active, but it takes more code to test than to just blindly swap to the same port) and backs up the address of the desired data port.

When the restore begins, you'll still have the port you borrowed as the active one, which will be pointed back to wherever it was by the first 3 pops, and lastly, the selected port state is restored.



The second example puts the CTRL register on the stack after the three address selection registers because that way, when it restores, it will first select the previously-not-selected port, restore it, then swap back to the previously-selected port and restore that, leaving it in place as the active port just as it was when your routine was called.

Obviously, if you intend to use the DCSEL registers, you'd want to tuck those into your backup/restore logic.

EDIT: This is incorrect. If you write to those registers, it's because you WANT those changed, so why would you back them up and restore them?

It's completely fine to manipulate DCSEL during these two examples so long as in example 1, the ADDRSEL bit is correct before doing the restore.

User avatar
svenvandevelde
Posts: 488
Joined: Wed Dec 23, 2020 6:30 am
Location: Belgium, Antwerpen

IRQ

Post by svenvandevelde »


Great @ZeroByte. Thank you for this. I'll try it. You just solved my issue how to decently stack the vera. Thank you also for the detailed explanation!

KICKC home page by Jesper Gravgaard.
My KICKC alpha with Commander X16 extensions.
Ed Minchau
Posts: 503
Joined: Sat Jul 11, 2020 3:30 pm

IRQ

Post by Ed Minchau »


I'd avoid doing anything with VERA or the RAM/ROM bank pointers during IRQ. Instead I have IRQ set a flag bit, and have the main program do regular checks of such flag bits. Whenever MAIN detects the flag bit set it resets it and handles whatever I need to do in VERA or the RAM banks. It might take a millisecond longer to get around to doing whatever subroutine, but it makes the custom IRQ much faster and there's no danger of messing up VERA addresses or RAM bank pointers this way.

User avatar
svenvandevelde
Posts: 488
Joined: Wed Dec 23, 2020 6:30 am
Location: Belgium, Antwerpen

IRQ

Post by svenvandevelde »



On 5/26/2022 at 9:32 PM, Ed Minchau said:




Instead I have IRQ set a flag bit, and have the main program do regular checks of such flag bits. Whenever MAIN detects the flag bit set it resets it and handles whatever I need to do in VERA or the RAM banks.



So ... You would put all the video painting in the main loop while in the IRQ routine, you would put the coordinates calculations and game AI logic?

Concerning the banking it really depends what memory layout you have... I mean if you have moved half of your game engine memory into a bank, then during IRQ banking is required in bram. 

 

KICKC home page by Jesper Gravgaard.
My KICKC alpha with Commander X16 extensions.
Ed Minchau
Posts: 503
Joined: Sat Jul 11, 2020 3:30 pm

IRQ

Post by Ed Minchau »



On 5/26/2022 at 2:46 PM, svenvandevelde said:




So ... You would put all the video painting in the main loop while in the IRQ routine, you would put the coordinates calculations and game AI logic?



Concerning the banking it really depends what memory layout you have... I mean if you have moved half of your game engine memory into a bank, then during IRQ banking is required in bram. 



 



I'd have the IRQ do as little as possible. For Asteroid Commander it just updates a couple of bytes set aside for flag bits. The main program then checks those bits a couple hundred times a second, each time a major subroutine is finished and at least once for every trip through the main loop.

ZeroByte
Posts: 714
Joined: Wed Feb 10, 2021 2:40 pm

IRQ

Post by ZeroByte »



On 5/26/2022 at 2:32 PM, Ed Minchau said:




I'd avoid doing anything with VERA or the RAM/ROM bank pointers during IRQ. Instead I have IRQ set a flag bit, and have the main program do regular checks of such flag bits. Whenever MAIN detects the flag bit set it resets it and handles whatever I need to do in VERA or the RAM banks. It might take a millisecond longer to get around to doing whatever subroutine, but it makes the custom IRQ much faster and there's no danger of messing up VERA addresses or RAM bank pointers this way.



I have to disagree.

VBLANK is exactly what this kind of thing is for, and waiting for the Kernal to finish polling the keyboard before you get another crack at doing anything is very risky of bleeding out into the visible area and getting tearing artifacts and such.

The VBLANK IRQ can look for a signal value from the main loop that a frame is done, and only do no-op if that flag is not set. If the flag is set, then you know the program is sitting in an idle loop and you won't break anything by manipulating VERA.

Main loop code should always assume dirty state for the data ports anyway, (i.e. always start by configuring the port address) and since you know IRQ won't manipulate the port unless you signaled "frame done", it's safe to have a long loop for loading into VRAM, etc. because you haven't written the "update frame" flag.

I've seen the VBLANK IRQ take up to several characters' (not pixels, but characters) worth of visible raster time to complete and return to the main loop, depending on what the keyboard is doing.

Yes, it takes precious cycles away to do all this pushing VERA onto the stack, but that's why you try to avoid things like using both ports, etc. Flappy Bird is written in C, and it doesn't even push VERA onto the stack during IRQ, and it writes the sprite shadows into VERA during VBLANK and it never crashes.

I am not a super-coder or anything, and don't claim to know it all, and I'm sure there's all kinds of ways to get things done, but my understanding has been what I've just stated here.

Post Reply