How to setup and use IRQs on the TRS-80 Color Computer – Part 4 – Simpler ways to use interrupts on the CoCo

Table of Contents (click on a link to jump to that web page)

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.

This entry was posted in CoCo Programming. Bookmark the permalink.

2 Responses to How to setup and use IRQs on the TRS-80 Color Computer – Part 4 – Simpler ways to use interrupts on the CoCo

  1. 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?

    • nowhereman999 says:

      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

Leave a comment