Ok, I'll test that maybe later in the weekend.
Tonight I wanted to test the "TI" variable for the MO5 and compiled and ran contrib_timers.bas. Unfortunately it seem the timer remains at 0. Is there any reason for that ?
[EDIT] inspecting src/hw/mo5/startup.asm I found the answer
On the MO5 the "TIMEPT" is not only the address of the ISR (interrupt service routine), but also a flag to enable it at $2063. When 0 (default value) the TIMEPT is not enabled and the MO5IRQ routine is not called. Of course if you want to chain, you need to check this flag to be sure that the previous pointer in $2061 was enabled or not. At the end of the ISR, you should do a RTI to finish the interrupt and continue the normal execution flow.
Last, as these two are IRQ and not FIRQ (F standing for Fast), you don't need to protect the registers (there is no need to PSHS/PULS D,X). The mc6809 will save them all on the stack for you.
Here is a version of startup.asm that makes the timer work on the MO5 (and possibly on the pc128 as well)
Code : Tout sélectionner
MO5TIMER fdb $0
MO5IRQO fdb MO5IRQ3
MO5IRQN fdb $0
; TIMER service routine
MO5IRQ
LDD MO5TIMER
ADDD #1
STD MO5TIMER
LDX MO5IRQN
BEQ MO5IRQ2
JSR ,X
MO5IRQ2
JMP [MO5IRQO]
MO5IRQ3
RTI
MO5STARTUP
LDX #$2061
LDA 2,X
BEQ MO5STARTUP2
LDD ,X
STD MO5IRQO
MO5STARTUP2
LDD #MO5IRQ
STD ,X
; LDA #1 <== any non zero will do, but since $00xx point to video ram, we know for sure that A=highbyte(MO5IRQ) will never be 0 ;)
STA 2,X
LDA #$20
TFR A,DP
RTS
And if the memory footprint is an issue (in my experience, it is), we can use a technique that I (ab)use in most all of
1kb intro: use immediate values as variables. This reduces the memory footprint a lot and makes the program faster as well (but somehow is not ROMable (not an issue here), and difficult to analyze for a beginner, hence considered as bad programming by academics). In that case that would give:
Code : Tout sélectionner
; TIMER service routine
MO5IRQ
LDD #1 ; increment
ADDD #0 ; add value of TI variable
MO5TIMER set *-2 ; (variable within code)
STD MO5TIMER ; write result to TI variable
LDX #0 ; get next ISR
MO5IRQN set *-2 ; (variable within code)
BEQ MO5IRQ2 ; any defined ?
JSR ,X ; yes ==> call it
MO5IRQ2
JMP >MO5IRQEND ; no ==> jmp to the old one
MO5IRQO set *-2 ; (variable within code)
MO5IRQEND
RTI ; by defaut do RTI
MO5STARTUP
LDX #$2061
LDA 2,X ; Is previous TIMERPT enable ?
BEQ MO5STARTUP2 ; no ==> keep default return code (RTI)
LDD ,X ; yes ==> backup previous ISR
STD MO5IRQO ; and chain it at the end of our own
MO5STARTUP2
LDD #MO5IRQ ; install our own ISR
STD ,X
LDA #$20 ; any non-zero value will do, let's use the one that'll go to DP
STA 2,X ; enable the ISR
TFR A,DP
RTS
Note this ISR is running at 50hz, and it seem a 60hz interrupt is assumed (TI/60 in the basic source). It is possible to emulate this by incrementing MO5TIMER by 1.2 instead of 1 (*), but may be you plan to introduce some kind of global system-constant (say TI_HZ) indicating the frequency of change of the TI variable on the machine ?
sam.
_____
(*) this will require a bit of extra code to deal with the fractional part. The idea is to make the increment = 2 one time out of 4, and 1 otherwise, resulting in a 1.2 increment value on average.
Code : Tout sélectionner
MO5IRQ
LDD #$0501 ; A=fractionnal part increment, B=increment
MO5TIMERFRAC SET *-2 ; (internal variable within code)
DECA
BNE MO5IRQ0
ADDD #$0501 ; reset A to 5, and sets B to 2
MO5IRQ0
STA MO5TIMERFRAC
CLRA ; make increment 16 bit.
ADDD #0 ; adds the TI variable
MO5TIMER set *-2 ; (variable within code)
STD MO5TIMER ; write result back to TI variable
LDX #0 ; get next ISR
MO5IRQN set *-2 ; (variable within code)
(... remaining code unchanged...)