How to setup and use IRQs on the TRS-80 Color Computer – Part 5 – Advanced interrupt settings and using CoCo 3 specific interrupts

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

In this section I will be going over some pretty advanced techniques to speed up your interrupt request routine so it has minimal effect on the rest of your program.

First trick is to setup an FIRQ that is in Direct Page space.  This will save a couple CPU Cycles every time the FIRQ is triggered.

Example:

The DP is set to $3E and the FIRQ routine starts in RAM at address $3ECA

 LDA    #$0E  * Write the direct page JMP instruction
 LDB    #$CA  * Will jump to address DP + $CA = $3ECA in our example
 STD    $010F * CoCo 1 & 2 FIRQ Jump pointer

Simon Jonassen told me about trick that you can use with the jump location in a CoCo 1 & 2.  You can use the  FIRQ jump Vector location as the actual interrupt routine so there is no jump required the code is executed at the FIRQ vector.  To use this you would just put your entire FIRQ routine starting at $010F.  That way you save the few extra cycles that would normally be used with the JMP $xxxx instruction.  If your program doesn’t use the FIRQ you can do the same thing with the IRQ but your interrupt routine would start at $010c.  – Thanks Simon


I’m going to show you some very optimized 6809 code that will be used for a CoCo 3 only audio sample player.  This code is not CoCo 1 or 2 compatible.

In this example the audio data has been loaded in RAM starting at address $3D59 and ending at address $7FFF and this code will playback the sample looping it over and over.

To minimize the CPU cycles this interrupt routine uses some self modifying code.

        ORG     $C000           * Address of the Direct Page and FIRQ
DirectPage:
        SETDP   DirectPage/256  * Setup the DP addressing for the assembler
*****************************************
* Play Sample in the background in mem $3D59-$7FFF, Loop it
Sample1Start:
        FDB     $3D59            * location where sample starts counting down from, used for looping
FIRQ_Interrupt1:
        STA     FIRQRestoreA+1   * Save A before we exit the routine (self modifying code)
        LDA     $FF93            * Re enable the FIRQ
LoadAudio1:
        LDA     $3D59            * This value gets changed with some self modifying code below
        STA     $FF20            * OUTPUT sound to DAC
        INC     LoadAudio1+2     * Increment the LSB of Sample pointer
        BNE     >                * check if we hit 00, if not exit
        INC     LoadAudio1+1     * Increment the MSB of Sample pointer
        BPL     >                * If positive then we are good still under $8000 otherwise (end of sample, reset pointer)
Hit8000:                         * Restore the pointer to the start location of the sample
        LDA     Sample1Start     * Point to the MSB of the sample Start location
        STA     LoadAudio1+1     * Store the MSB of Sample pointer
        LDA     Sample1Start+1   * Point to the LSB of the sample Start location
        STA     LoadAudio1+2     * Store the LSB of Sample pointer
        
FIRQRestoreA:
!       LDA     #$00             * STA at the start of the FIRQ stores A's value here and gets loaded before the RTI saves a cycle and a byte of RAM
        RTI                      * Return from the FIRQ

The program starts here and sets up the hardware on the CoCo 3 to use the GIME chip’s interrupts.  This is different from the previous methods shown in this series of posts.

START
***********************************************************
        ORCC    #$50            * Disable interrupts
        CLR     $FFD9           * High Speed mode enabled

        SETDP   DirectPage/256  * Set the direct Page so the assembler will use Direct Page addressing when assembling
        LDA     #DirectPage/256 * Set the Direct Page value
        TFR     A,DP            * to where our FIRQ is located

        LDA     #%00110100      * Setup the CoCo 3's hardware
        STA     $FF01           * HSYNC IRQ Disabled, IRQ Polarity Flag falling Edge, Data Direction Normal, Select Line LSB = 0, HSYNC Flag = 0
        STA     $FF03           * VSYNC IRQ Disabled, IRQ Polarity Flag falling Edge, Data Direction Normal, Select Line MSB = 0, VSYNC Flag = 0
        STA     $FF21           * CONTROL OF CD FIRQ* TO CPU DISABLED, IRQ Polarity Falling Edge of CD, CD Flag off
        STA     $FF23           * CONTROL OF Cart FIRQ* TO CPU DISABLED, IRQ Polarity Falling Edge of Cart, Cart Flag off
        LDA     #%01111100      *
        STA     $FF90           * CoCo 3 Mode, MMU Enabled, GIME IRQ Enabled, GIME FIRQ Enabled, Vector RAM at FEXX enabled, Standard SCS Normal, ROM Map 16k Int, 16k Ext
        LDA     #%00100000      *
        STA     $FF91           * Mem Type 64k chips, 279.365 nsec timer, MMU Task 0 - $FFA0-$FFA7
        LDA     #%10000000      *
        STA     $FF98           * Graphics mode, Colour output, 60 hz, max vertical res

* Setup the Timer FIRQ
        LDA     #$0E            * Write the JMP instruction to a direct page JMP location
        LDB     #FIRQ_Interrupt1-DirectPage  * B now is the address of the FIRQ routine
        STD     $FEF4           * Save it to the CoCo 3 FIRQ vector address 
        LDD     #$0280          *
        STD     $FF94           * Set countdown Timer to $0280 - This is the speed the samples will playback at
        LDA     #%00100000      * $20
        STA     $FF93           * Enable TIMER FIRQ Interrupt

        ANDCC   #$AF            * = %10101111 this will Enable the IRQ and the FIRQ to start

* Your program can go here, for now it just loops forever as the sample plays in the background
!
        BRA    <
        END    START            * Tell the assembler when creating an DECB ML program to set the 'EXEC' address to wherever the label START is in RAM (above)

I hope you are now an interrupt expert and use them in your next awesome game…  🙂

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 5 – Advanced interrupt settings and using CoCo 3 specific interrupts

  1. Eric Canales says:

    What value is FIRQENR supposed to be in the above example? I get an undefined error.

  2. nowhereman999 says:

    Hi Eric,
    I’m sorry I used an include file that has some of the common CoCo addresses used as labels, FIRQENR’s value is $FF93 I’ll change the above code and remove the Label

    Here is the typical Labels I sometimes use, just in case I missed any others:
    *****************************************************
    ** Used Labels *
    *****************************************************

    TEXT_SCREEN_START EQU $0400
    TEXT_SCREEN_END EQU $0600

    FIRQENR EQU $FF93
    FIRQ_Jump_position EQU $FEF4 * Store $7E which is the JMP opcode
    FIRQ_Start_Address EQU $FEF5 * Store the address you want the IRQ to jumpt to at this address
    FIRQ_POINT EQU $FEF4 * $FEF4 is the usual CoCo3 SuperBasic FIRQ value

    IRQENR EQU $FF92
    IRQ_Jump_position EQU $FEF7 * Store $7E which is the JMP opcode
    IRQ_Start_Address EQU $FEF8 * Store the address you want the IRQ to jumpt to at this address
    IRQ_POINT EQU $FEF7 * $FEF7 is the usual CoCo3 SuperBasic IRQ value

    PIA0_Byte_0_KeyRow_Joy EQU $FF00
    PIA0_Byte_1_HSYNC EQU $FF01
    PIA0_Byte_2_KeyColumn_Joy EQU $FF02
    PIA0_Byte_3_VSYNC EQU $FF03

    PIA1_Byte_0_IRQ EQU $FF20
    PIA1_Byte_1_IRQ EQU $FF21
    PIA1_Byte_2 EQU $FF22
    PIA1_Byte_3_IRQ_Ct_Snd EQU $FF23

    Disk_Cont_Reg EQU $FF40
    Disk_Comnd_Stat_Reg EQU $FF48
    Disk_Track_Reg EQU $FF49
    Disk_Sector_Reg EQU $FF4A
    Disk_Data_Reg EQU $FF4B

    INIT0_Register0 EQU $FF90
    INIT1_Register1 EQU $FF91

    Timer_register_MSB EQU $FF94
    Timer_register_LSB EQU $FF95

    Video_Mode_Register EQU $FF98
    Vid_Res_Reg EQU $FF99
    Border_Register EQU $FF9A
    VidStart EQU $FF9D *
    Hor_Offset_Reg EQU $FF9F

    *Screen Colour Settings
    Palette_Start EQU $FFB0
    Palette_0 EQU $FFB0
    Palette_1 EQU $FFB1
    Palette_2 EQU $FFB2
    Palette_3 EQU $FFB3
    Palette_4 EQU $FFB4
    Palette_5 EQU $FFB5
    Palette_6 EQU $FFB6
    Palette_7 EQU $FFB7
    Palette_8 EQU $FFB8
    Palette_9 EQU $FFB9
    Palette_10 EQU $FFBA
    Palette_11 EQU $FFBB
    Palette_12 EQU $FFBC
    Palette_13 EQU $FFBD
    Palette_14 EQU $FFBE
    Palette_15 EQU $FFBF

    CoCo1_Regular_Speed EQU $FFD6
    CoCo1_High_Speed EQU $FFD7
    Regular_Speed EQU $FFD8
    High_Speed_Mode EQU $FFD9
    ROM_Mode_Enable EQU $FFDE
    RAM_Mode_Enable EQU $FFDF

    * Screen_Start EQU $7000
    Border_Colour EQU $00 * 00 – Black

    * MMU Related
    MMU_Reg_Bank0_0 EQU $FFA0 * Page $0000-$1FFF Block #0
    MMU_Reg_Bank0_1 EQU $FFA1 * Page $2000-$3FFF Block #1
    MMU_Reg_Bank0_2 EQU $FFA2 * Page $4000-$5FFF Block #2
    MMU_Reg_Bank0_3 EQU $FFA3 * Page $6000-$7FFF Block #3
    MMU_Reg_Bank0_4 EQU $FFA4 * Page $8000-$9FFF Block #4
    MMU_Reg_Bank0_5 EQU $FFA5 * Page $A000-$BFFF Block #5
    MMU_Reg_Bank0_6 EQU $FFA6 * Page $C000-$DFFF Block #6
    MMU_Reg_Bank0_7 EQU $FFA7 * Page $E000-$FFFF Block #7
    MMU_Reg_Bank1_0 EQU $FFA8 * Page $0000-$1FFF Block #0
    MMU_Reg_Bank1_1 EQU $FFA9 * Page $2000-$3FFF Block #1
    MMU_Reg_Bank1_2 EQU $FFAA * Page $4000-$5FFF Block #2
    MMU_Reg_Bank1_3 EQU $FFAB * Page $6000-$7FFF Block #3
    MMU_Reg_Bank1_4 EQU $FFAC * Page $8000-$9FFF Block #4
    MMU_Reg_Bank1_5 EQU $FFAD * Page $A000-$BFFF Block #5
    MMU_Reg_Bank1_6 EQU $FFAE * Page $C000-$DFFF Block #6
    MMU_Reg_Bank1_7 EQU $FFAF * Page $E000-$FFFF Block #7

Leave a comment