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
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… 🙂
What value is FIRQENR supposed to be in the above example? I get an undefined error.
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