2018-08-01 20:41:36 +02:00
|
|
|
/*
|
|
|
|
** gint:tmu:inth - Interrupt handlers for the timer units
|
|
|
|
** Perhaps the most technical of my interrupt handlers. They implement a
|
|
|
|
** simple kind of interrupt handler communication by letting the control flow
|
|
|
|
** from each interrupt handler to the next.
|
|
|
|
*/
|
|
|
|
|
2019-03-10 15:45:34 +01:00
|
|
|
/* Gates for the standard Timer Unit (TMU) */
|
2019-07-18 21:18:36 +02:00
|
|
|
.global _inth_tmu /* 128 bytes */
|
2018-08-01 20:41:36 +02:00
|
|
|
|
2019-03-10 15:45:34 +01:00
|
|
|
/* Gates for the extra timers (informally called ETMU) */
|
2019-07-18 21:18:36 +02:00
|
|
|
.global _inth_etmu2 /* 32 bytes */
|
|
|
|
.global _inth_etmu_help /* 32 bytes */
|
|
|
|
.global _inth_etmux /* 32 bytes */
|
2018-08-01 20:41:36 +02:00
|
|
|
|
2019-03-10 15:45:34 +01:00
|
|
|
.section .gint.blocks, "ax"
|
|
|
|
.align 4
|
2018-08-01 20:41:36 +02:00
|
|
|
|
|
|
|
/* TMU INTERRUPT HANDLERS - 128 BYTES
|
|
|
|
Unfortunately I did not manage to write a handler that cleared the interrupt
|
|
|
|
flag and invoked a callback in less than 34 bytes data included. So I
|
|
|
|
decided to make several gates operate as a whole and add a bit more features
|
|
|
|
in them. Basically, these handlers:
|
|
|
|
- Clear the interrupt flag
|
|
|
|
- Invoke a callback function and pass it a user-provided argument
|
|
|
|
- Stop the timer if the callback returns non-zero
|
2019-02-21 20:58:38 +01:00
|
|
|
- Host their own callback pointers and arguments
|
2018-08-01 20:41:36 +02:00
|
|
|
|
|
|
|
It is important to notice that the code of the following gates looks like
|
|
|
|
they are contiguous in memory. The assembler will make that assumption, and
|
|
|
|
turn any address reference between two gates into a *relative displacement*.
|
|
|
|
If the gates don't have the same relative location at runtime, the code will
|
|
|
|
crash because we will have broken the references. This is why we can only do
|
|
|
|
it with handlers that are mapped to consecutive event codes. */
|
|
|
|
|
2019-07-18 21:18:36 +02:00
|
|
|
_inth_tmu:
|
|
|
|
|
2018-08-01 20:41:36 +02:00
|
|
|
/* FIRST GATE - TMU0 entry, clear underflow flag and call back */
|
|
|
|
_inth_tmu_0:
|
|
|
|
mova .storage0, r0
|
2019-07-16 21:32:20 +02:00
|
|
|
mov #0, r1
|
2018-08-01 20:41:36 +02:00
|
|
|
|
|
|
|
/*** This is the first shared section ***/
|
|
|
|
|
|
|
|
.clearflag:
|
2019-07-16 21:32:20 +02:00
|
|
|
sts.l pr, @-r15
|
2018-08-01 20:41:36 +02:00
|
|
|
mov.l r1, @-r15
|
|
|
|
|
|
|
|
/* Load the TCR address and clear the interrupt flag */
|
|
|
|
mov.l .mask, r3
|
|
|
|
mov.l @(8, r0), r1
|
|
|
|
mov.w @r1, r2
|
|
|
|
and r3, r2
|
|
|
|
mov.w r2, @r1
|
|
|
|
|
|
|
|
/* Invoke the callback function and pass the argument */
|
|
|
|
mov.l @r0, r1
|
|
|
|
jsr @r1
|
|
|
|
mov.l @(4, r0), r4
|
|
|
|
|
2019-02-21 20:58:38 +01:00
|
|
|
/* Prepare stopping the timer and jump to second section */
|
2019-07-16 21:32:20 +02:00
|
|
|
mov.l @r15+, r4
|
|
|
|
mov.l .timer_stop, r1
|
2018-08-01 20:41:36 +02:00
|
|
|
bra .stoptimer
|
2019-07-16 21:32:20 +02:00
|
|
|
nop
|
2018-08-01 20:41:36 +02:00
|
|
|
|
|
|
|
/* SECOND GATE - TMU1 entry and stop timer */
|
|
|
|
_inth_tmu_1:
|
|
|
|
mova .storage1, r0
|
|
|
|
bra .clearflag
|
2019-07-16 21:32:20 +02:00
|
|
|
mov #1, r1
|
2018-08-01 20:41:36 +02:00
|
|
|
|
|
|
|
/*** This is the second shared section ***/
|
|
|
|
|
|
|
|
.stoptimer:
|
|
|
|
/* Stop the timer if the return value is not zero */
|
|
|
|
tst r0, r0
|
|
|
|
bt .end
|
2019-07-16 21:32:20 +02:00
|
|
|
jsr @r1
|
|
|
|
nop
|
2018-08-01 20:41:36 +02:00
|
|
|
|
|
|
|
.end:
|
2019-07-16 21:32:20 +02:00
|
|
|
lds.l @r15+, pr
|
2020-05-06 16:37:44 +02:00
|
|
|
rts
|
2018-08-01 20:41:36 +02:00
|
|
|
nop
|
|
|
|
|
|
|
|
.zero 12
|
|
|
|
|
|
|
|
/* THIRD GATE - TMU2 entry and storage for TMU0 */
|
|
|
|
_inth_tmu_2:
|
|
|
|
mova .storage2, r0
|
|
|
|
bra .clearflag
|
2019-07-16 21:32:20 +02:00
|
|
|
mov #2, r1
|
2018-08-01 20:41:36 +02:00
|
|
|
|
|
|
|
.zero 14
|
|
|
|
|
|
|
|
.storage0:
|
|
|
|
.long 0 /* Callback: Configured dynamically */
|
|
|
|
.long 0 /* Argument: Configured dynamically */
|
|
|
|
.long 0xa4490010 /* TCR0: Overridden at startup on SH3 */
|
|
|
|
|
|
|
|
/* FOURTH GATE - Storage for TMU1, TMU2 and other values */
|
|
|
|
_inth_tmu_storage:
|
|
|
|
|
2019-07-16 21:32:20 +02:00
|
|
|
.mask:
|
|
|
|
.long 0x0000feff
|
|
|
|
.timer_stop:
|
|
|
|
.long _timer_stop /* gint's function from <gint/timer.h> */
|
2018-08-01 20:41:36 +02:00
|
|
|
|
|
|
|
.storage1:
|
|
|
|
.long 0 /* Callback: Configured dynamically */
|
|
|
|
.long 0 /* Argument: Configured dynamically */
|
|
|
|
.long 0xa449001c /* TCR1: Overridden at startup on SH3 */
|
|
|
|
|
|
|
|
.storage2:
|
|
|
|
.long 0 /* Callback: Configured dynamically */
|
|
|
|
.long 0 /* Argument: Configured dynamically */
|
|
|
|
.long 0xa4490028 /* TCR2: Overridden at startup on SH3 */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* EXTRA TMU INTERRUPT HANDLERS - 96 BYTES
|
|
|
|
To implement the same functionalities as the standard timers, several blocks
|
|
|
|
are once again needed. But the handlers for the extra timers are not located
|
|
|
|
in adjacent gates, except for ETMU1 and ETMU2 which have event codes 0xc20
|
2019-02-21 20:58:38 +01:00
|
|
|
and 0xc40. Since handler 0xc60 is free on SH4 and not redirected to on SH3,
|
|
|
|
I use it to build a three-handler block similar to that of the TMU above.
|
2018-08-01 20:41:36 +02:00
|
|
|
|
|
|
|
On SH4 this means that an extra gate has to be installed, but no interrupt
|
2019-02-21 20:58:38 +01:00
|
|
|
point here. On SH3 this means that four gates are used for the only extra
|
2018-08-01 20:41:36 +02:00
|
|
|
timer, but the incurred cost is minimal (96 bytes on the binary file)
|
|
|
|
because the size of the VBR area can hardly be shrunk anyway.
|
|
|
|
|
|
|
|
It *is* possible to do generalized communication between interrupt handlers
|
|
|
|
that do not reside in consecutive gates. The general way of performing a
|
|
|
|
jump or data access between two interrupt handlers would be to store at
|
|
|
|
runtime the address of the target resource in a reserved longword in the
|
|
|
|
source handler. But longwords are costly in 32-byte areas. Even if the event
|
|
|
|
codes of the interrupt handlers are known at development time, the best I
|
|
|
|
can think of is hardcoding the relative displacements, and one would need to
|
|
|
|
use the unnatural and unmaintainable @(disp, pc) addressing modes. */
|
|
|
|
|
2019-07-16 21:32:20 +02:00
|
|
|
/* FIRST GATE - ETMU2 entry, invoke callback and prepare clear flag */
|
|
|
|
_inth_etmu2:
|
|
|
|
/* Warning: the size of the following section (4 bytes) is hardcoded in
|
|
|
|
the jump in _inth_etmux */
|
|
|
|
mova .storage_etmu2, r0
|
|
|
|
mov #5, r1
|
2018-08-01 20:41:36 +02:00
|
|
|
|
|
|
|
.extra_callback:
|
2019-07-16 21:32:20 +02:00
|
|
|
sts.l pr, @-r15
|
|
|
|
mov.l r1, @-r15
|
2018-08-01 20:41:36 +02:00
|
|
|
|
|
|
|
/* Invoke the callback function */
|
2019-07-16 21:32:20 +02:00
|
|
|
mov.l @r0, r1
|
2018-08-01 20:41:36 +02:00
|
|
|
jsr @r1
|
2019-07-16 21:32:20 +02:00
|
|
|
mov.l @(4, r0), r4
|
2018-08-01 20:41:36 +02:00
|
|
|
|
2019-07-16 21:32:20 +02:00
|
|
|
/* Load timer ID and forward the callback's return value */
|
|
|
|
mov.l .timer_clear, r1
|
|
|
|
mov.l @r15+, r4
|
|
|
|
bra _inth_etmu_help
|
|
|
|
mov r0, r5
|
|
|
|
|
|
|
|
nop
|
2018-08-01 20:41:36 +02:00
|
|
|
|
2019-07-16 21:32:20 +02:00
|
|
|
.storage_etmu2:
|
2018-08-01 20:41:36 +02:00
|
|
|
.long 0 /* Callback: Configured dynamically */
|
|
|
|
.long 0 /* Argument: Configured dynamically */
|
|
|
|
|
|
|
|
/* SECOND GATE - Helper entry, invoke callback and stop timer if requested */
|
2019-07-16 21:32:20 +02:00
|
|
|
_inth_etmu_help:
|
2018-08-01 20:41:36 +02:00
|
|
|
|
2019-07-16 21:32:20 +02:00
|
|
|
/* Clear the flag and possibly stop the timer */
|
|
|
|
jsr @r1
|
|
|
|
nop
|
2018-08-01 20:41:36 +02:00
|
|
|
|
2019-07-16 21:32:20 +02:00
|
|
|
lds.l @r15+, pr
|
2020-05-06 16:37:44 +02:00
|
|
|
rts
|
2018-08-01 20:41:36 +02:00
|
|
|
nop
|
|
|
|
|
2019-07-16 21:32:20 +02:00
|
|
|
.zero 18
|
|
|
|
|
|
|
|
.timer_clear:
|
|
|
|
.long _timer_clear /* gint's function from src/tmu/tmu.c */
|
|
|
|
|
|
|
|
/* THIRD GATE - All other ETMU entries, deferred to the previous ones */
|
|
|
|
_inth_etmux:
|
2018-08-01 20:41:36 +02:00
|
|
|
/* Dynamically compute the target of the jump */
|
2019-07-16 21:32:20 +02:00
|
|
|
stc vbr, r3
|
2018-08-01 20:41:36 +02:00
|
|
|
mov.l 1f, r2
|
2019-07-16 21:32:20 +02:00
|
|
|
add r2, r3
|
2018-08-01 20:41:36 +02:00
|
|
|
|
2019-07-16 21:32:20 +02:00
|
|
|
mova .storage_etmux, r0
|
|
|
|
mov.l .id_etmux, r1
|
|
|
|
jmp @r3
|
|
|
|
nop
|
2018-08-01 20:41:36 +02:00
|
|
|
nop
|
|
|
|
|
|
|
|
/* Offset from VBR where extra timer 2 is located:
|
|
|
|
- 0x600 to reach the interrupt handlers
|
2020-05-06 16:37:44 +02:00
|
|
|
- 0x040 to jump over the entry gate
|
2019-07-16 21:32:20 +02:00
|
|
|
- 0x840 to reach the handler of ETMU2
|
|
|
|
- 0x004 to skip its first instructions (the size is hardcoded) */
|
2020-05-06 16:37:44 +02:00
|
|
|
1: .long 0xe84
|
2018-08-01 20:41:36 +02:00
|
|
|
|
2019-07-18 21:18:36 +02:00
|
|
|
.id_etmux:
|
|
|
|
.long 0 /* Timer ID */
|
2019-07-16 21:32:20 +02:00
|
|
|
.storage_etmux:
|
2018-08-01 20:41:36 +02:00
|
|
|
.long 0 /* Callback: Configured dynamically */
|
|
|
|
.long 0 /* Argument: Configured dynamically */
|