Table of Contents (click on a link to jump to that web page)
- Part 1 – What is an IRQ and when would I use it?
- Part 2 – Overview with an example program
- Part 3 – Can my IRQ run without effecting BASIC?
- Part 4 – Simpler ways to use interrupts on the CoCo
- Part 5 – Advanced interrupt settings and using CoCo 3 specific interrupts
There is a simpler way to test for and react to the HSYNC and VSYNC interrupts. We do this by simply testing bit 7 of the IRQ we want to use. As per the information here:
HSYNC – $FF01 – PIA 0 side A control reg – PIA0AC CoCo 1/2/3
- Bit 7 HSYNC Flag 0=not Sync, 1=Hsync is triggered
- Bit 6 Unused
- Bit 5 Always a 1
- Bit 4 Always a 1
- Bit 3 Select Line LSB of MUX
- Bit 2 DATA DIRECTION TOGGLE 0 = $FF00 sets data direction 1 = normal
- Bit 1 IRQ POLARITY 0 = flag set on falling edge 1=set on rising edge
- Bit 0 HSYNC IRQ 0 = disabled 1 = enabled
VSYNC – $FF03 – PIA 0 side B control reg – PIA0BC CoCo 1/2/3
- Bit 7 VSYNC FLAG 0=not Sync, 1=Vsync is triggered
- Bit 6 N/A
- Bit 5 Always a 1
- Bit 4 Always a 1
- Bit 3 SELECT LINE MSB of MUX
- Bit 2 DATA DIRECTION TOGGLE 0 = $FF02 sets data direction 1=normal
- Bit 1 IRQ POLARITY 0=flag set on falling edge 1=set on rising edge
- Bit 0 VSYNC IRQ 0=disabled 1=enabled
If you have a program that needs to use the VSYNC such as the previous clock program or maybe a game that needs to know when to switch screens you can write your program so that it does what it needs to do and then waits for the VSYNC at address $FF03, bit 7 to be set. At the beginning of your program you would add the following few lines which will make sure the VSYNC IRQ is enabled by setting bit 0 of $F003 to a one.
LDA $FF03 * Load A with the value stored at $FF03 ORA #$01 * Set bit 1 high STA $FF03 * Store A at $FF03 - VSYNC is now enabled
Then you simply add these three lines of code to your program to make it wait and watch for a VSYNC to be triggered.
* Wait for VSYNC to occur VSYNC LDA $FF02 * Acknowledge the VSYNC interrupt VWAIT LDA $FF03 * Loop until Vsync is triggered BPL VWAIT * If bit 7 is low then keep looping
Once the VSYNC is triggered the program execution will continue to the rest of your program.
Unlike an actual interrupt request, the CPU doesn’t save any registers on the stack and it doesn’t have to jump to and from your interrupt request so this can save CPU cycles.
* Timer related pointers, Can be removed as you probably don't require a timer to be shown on screen in your game Timer EQU RepeatCount+5 * used for the Clock counter counts down from 30 every time play loop is entered. When it hit's zero a second has passed Minutes1 EQU $420-5 * Location to display timer on screen (minutes and seconds) Minutes2 EQU Minutes1+1 Seconds1 EQU Minutes1+3 Seconds2 EQU Minutes1+4 ORG $0E00 START ORCC #$50 * If you are going to use real IRQ's instead of polling VWAIT then change this section to enable the IRQs LDA $FF03 * PIA 0 side B control reg - PIA0BC ORA #$01 * VSync IRQ disabled STA $FF03 * Save Settings * This is Timer display related code LDD #$3030 * ascii '00' STD Minutes1 * Start showing 00 minutes STD Seconds1 * Start showing 00 seconds CLR Every2nd * Process sound every 2nd Vsync IRQ hit LDB #60 * Vsync is triggered 60 times a second STB Timer * store it LDB #': * Draw the colon on screen between the minutes and the seconds STB Minutes2+1 * store it * End of Time display related code that can be removed if you don't want to see a timer on the screen. Loop: VSYNC LDA $FF02 * Not sure if this is necessary??? VWAIT LDA $FF03 * Loop until Vsync is triggered BPL VWAIT * Show the running time on the screen DEC Timer * triggered 60 times every second BNE Loop * If we haven't reached zero yet then don't update the time LDB #60 * reset the timer back to 60 STB Timer * save the value LDA Seconds2 * check if the ones value of the # of seconds CMPA #$39 * if it is a nine then make it a zero BEQ > * make it a zero and add to the tens value of the # of seconds INCA * Otherwise update STA Seconds2 * Save BRA Playnotes * Go process music data ! LDA #$30 * set the ones value to zero STA Seconds2 * Save it LDA Seconds1 * Get the tens value of the seconds CMPA #$35 * check if it is a 5 BEQ > * If so then add one to minute value INCA * otherwise add 1 to the tens value of the seconds STA Seconds1 * save it BRA Playnotes * Go process music data ! LDA #$30 * Set the tens value of the seconds STA Seconds1 * to a zero LDA Minutes2 * Get the ones value of the minutes CMPA #$39 * check if it is a nine BEQ > * if so then go add one to the tens of the minute value INCA * otherwise increment the ones value of the seconds STA Minutes2 * update the value BRA Playnotes * Go process music data ! LDA #$30 * Set the ones value of the minutes STA Minutes2 * to zero INC Minutes1 * add one the tens value of the minutes LBRA Loop * Go wait for the next VSYNC IRQ * End of time on screen display code END START * Tell assembler when creating an ML program to set the 'EXEC' address to wherever the label START is in RAM (above)
That’s it for Part 4, in Part 5 I’ll go over how you would setup and use an interrupt specifically on a CoCo 3 and also some optimized ways accessing interrupt routines.
hello glen, in this last code I see that you are waiting for the sweep or the interruption both before starting and while you are waiting for a second to pass, but you must return from the interruption immediately, or am I wrong? Does this routine hijack the machine or not?
Hi Luis,
I’m sorry I didn’t see your comment until now.
This last routine hijacks the machine, it is only an example of having the computer run some code. Then it waits until the VSYNC is triggered then runs the code again. Not very useful but as I said just an example.
This is one way you can setup a game to run it’s code then wait until the Vsync for that frame on screen is finished drawing. Then you can work on the next frame in your game.
I hope that explains what you were asking.
Cheers,
Glen