gint/src/kernel/inth.S

290 lines
7.7 KiB
ArmAsm
Raw Normal View History

/*
** gint:core:inth - Interrupt handlers
** This file only contains the entry points because the gates are managed
** by device drivers. Each driver will supply its own interrupt handler
** blocks depending on its configuration.
*/
#define CPP_ASM
#include <gint/hardware.h>
.global _gint_inth_7305
#ifdef FX9860G
.global _gint_inth_7705
#endif
.global _gint_inth_callback
.section .gint.blocks, "ax"
.align 4
/* Interrupt handlers
The .gint.blocks section consists of blocks of 32 bytes intended for mapping
into the VBR space (exception, TLB miss, and interrupt handlers). Each event
gate is linearly associated with a block number on SH4:
block_id = (event_code - 0x3c0) / 0x20
and associated with a compact block number on SH3 through a table (to ensure
that blocks are consecutive in memory).
This file provides entry points; drivers may provide their own interrupt
handlers, and store them in the .gint.blocks section for consistency. They
should be aware of the consequences of reordering the blocks into the VBR
space:
- It is possible to map MPU-specific blocks at runtime, to avoid checking
the MPU each time an interrupt is handled;
- However, the blocks can only rely on relative displacements or cross-
references if their relative order is known and heeded for both on SH4 and
SH3. A good example of this is the timer driver. Please be careful. */
/* SH7305-TYPE DEBUG INTERRUPT HANDLER - 26 BYTES */
#if 0
_gint_inth_7305:
mov.l 1f, r0
mov.l @r0, r4
sts.l pr, @-r15
mov.l 2f, r0
jsr @r0
nop
lds.l @r15+, pr
rte
nop
.zero 6
2: .long _debug
1: .long 0xff000028
#endif
/* SH7305-TYPE INTERRUPT HANDLER ENTRY - 40 BYTES */
_gint_inth_7305:
/* Save caller-saved registers which might currently be in use by the
interrupted function, as we don't know what the callback will do */
sts.l pr, @-r15
stc.l gbr, @-r15
sts.l mach, @-r15
sts.l macl, @-r15
/* Get the event code from the INTEVT register */
mov.l 1f, r0
mov.l @r0, r0
/* Interrupt codes start at 0x400 */
mov #4, r1
shll8 r1
sub r1, r0
/* Add the distance to the first entry and jump as a subroutine */
add #(.first_entry - .jump_over), r0
bsrf r0
nop
.jump_over:
/* Restore caller-saved registers */
lds.l @r15+, macl
lds.l @r15+, mach
ldc.l @r15+, gbr
lds.l @r15+, pr
rte
nop
.zero 24
1: .long 0xff000028
.first_entry:
#ifdef FX9860G
2022-09-25 04:48:07 +02:00
/* SH7705-TYPE INTERRUPT HANDLER ENTRY - 56 BYTES */
_gint_inth_7705:
/* Save caller-saved registers as before */
sts.l pr, @-r15
stc.l gbr, @-r15
sts.l mach, @-r15
sts.l macl, @-r15
/* Get the event code from the INTEVT2 register */
mov.l 1f, r0
mov.l @r0, r0 /* r0 = old_code */
/* Translate the event code to compact format. The compact format is
laid out in a way that leaves no gap in the VBR space. Additionally,
gates are installed starting at VBR + 0x200 to save space. */
mov.l 2f, r2
mov #-5, r3
shld r3, r0 /* r0 = old_code >> 5 */
add #-32, r0 /* r0 = (old_code - 0x400) >> 5 */
mov.b @(r0, r2), r0 /* r0 = gate_number */
add #16, r0 /* r0 = (0x200 + gate_number * 0x20) >> 5 */
mov #5, r3
shld r3, r0 /* r0 = 0x200 + gate_number * 0x20 */
/* Start at VBR + 0x200 and jump! */
stc vbr, r1
add r1, r0
jsr @r0
nop
/* Restore saved registers */
lds.l @r15+, macl
lds.l @r15+, mach
ldc.l @r15+, gbr
lds.l @r15+, pr
rte
nop
.zero 8
1: .long 0xa4000000 /* INTEVT2 register */
2: .long _inth_remap
.data
.align 4
/* EVENT CODE TRANSLATION TABLE - 96 BYTES
This table was originally used to translate SH3 event codes to corresponding
SH4 events codes so that the VBR can be laid out in the SH4 model on every
platform. However, due to memory size limitations on SH3, it now also
translates SH4 event codes to adjacent gates numbers to reduce the size of
the VBR section.
The VBR interrupt space on SH3 is laid out as follows:
VBR offset SH3 events Description
-------------------------------------------------------------------
0x200 400 420 440 TMU0, TMU1, TMU2
0x260 f00 --- --- --- ETMU0, 3-gate logic at ETMU4
0x2e0 4a0 RTC Periodic Interrupt
-------------------------------------------------------------------
0x600 --- --- Entry gate
-------------------------------------------------------------------
There is space for 16 gates at VBR + 0x200 so the VBR currently ends after
the interrupt entry gate at VBR + 0x640. */
_inth_remap:
.byte 0, 1, 2, 0xff, 0xff, 7, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
#endif
.section .gint.mapped, "ax"
/* CALLBACK HELPER
This function implements the callback with context saving. It is a general
function and does not need to reside in VBR space which is block-structured.
This function saves registers r0_bank...r7_bank, enables interrupts,
switches back to user bank and executes the callback. It does not save other
registers (pr/mach/macl/gbr) which are managed by the handler entry. */
/* gint_inth_callback: Indirect call from kernel space to userland
@r4 Address of callback function
-> Returns the return value of the callback (int). */
kernel: dynamic loading of GMAPPED functions to user RAM This commit introduces a large architectural change. Unlike previous models of the fx-9860G series, the G-III models have a new user RAM address different from 8801c000. The purpose of this change is to dynamically load GMAPPED functions to this address by querying the TLB, and call them through a function pointer whose address is determined when loading. Because of the overhead of using a function pointer in both assembly and C code, changes have been made to avoid GMAPPED functions altogether. Current, only cpu_setVBR() and gint_inth_callback() are left, the second being used specifically to enable TLB misses when needed. * Add a .gint.mappedrel section for the function pointers holding addresses to GMAPPED functions; add function pointers for cpu_setVBR() and gint_inth_callback() * Move rram to address 0 instead of the hardcoded 0x8801c000 * Load GMAPPED functions at their linked address + the physical address user RAM is mapped, to and compute their function pointers * Remove the GMAPPED macro since no user function needs it anymore * Add section flags "ax" (code) or "aw" (data) to every custom .section in assembler code, as they default to unpredictable values that can cause the section to be marked NOLOAD by the linker * Update the main kernel, TMU, ETMU and RTC interrupt handlers to use the new indirect calling method This is made possible by new MMU functions giving direct access to the physical area behind any virtualized page. * Add an mmu_translate() function to query the TLB * Add an mmu_uram() function to access user RAM from P1 The exception catching mechanism has been modified to avoid the use of GMAPPED functions altogether. * Set SR.BL=0 and SR.IMASK=15 before calling exception catchers * Move gint_exc_skip() to normal text ROM * Also fix registers not being popped off the stack before a panic The timer drivers have also been modified to avoid GMAPPED functions. * Invoke timer_stop() through gint_inth_callback() and move it to ROM * Move and expand the ETMU driver to span 3 blocks at 0xd00 (ETMU4) * Remove the timer_clear() function by inlining it into the ETMU handler (TCR is provided within the storage block of each timer) * Also split src/timer/inth.s into src/timer/inth-{tmu,etmu}.s Additionally, VBR addresses are now determined at runtime to further reduce hardcoded memory layout addresses in the linker script. * Determine fx-9860G VBR addresses dynamically from mmu_uram() * Determine fx-CG 50 VBR addresses dynamically from mmu_uram() * Remove linker symbols for VBR addresses Comments and documentation have been updated throughout the code to reflect the changes.
2020-09-17 14:48:54 +02:00
_gint_inth_callback_reloc:
stc.l r0_bank, @-r15
stc.l r1_bank, @-r15
stc.l r2_bank, @-r15
stc.l r3_bank, @-r15
stc.l r4_bank, @-r15
stc.l r5_bank, @-r15
stc.l r6_bank, @-r15
stc.l r7_bank, @-r15
stc.l spc, @-r15
stc.l ssr, @-r15
stc.l sr, @-r15
/* Save some values to user bank; once we enable interrupts, the kernel
bank might be overwritten at any moment. */
ldc r4, r0_bank
/* Enable interrupts and go back to user bank. On SH4, SR.IMASK is set
to the level of the current interrupt, which makes sure we can only
be re-interrupted by something with a higher priority. */
stc sr, r1
mov.l .SR_clear_RB_BL, r0
and r0, r1
/* On SH3 the CPUOPM.INTMU bit is not supported, and on the fx-CG
emulator, it is outright ignored. In these situations, set IMASK to
15 to block interrupts while allowing TLB misses to be handled. */
mov.l .gint, r2
mov.l @r2, r0
tst #1, r0
bf .set_imask
mov.l @(4*HWCALC,r2), r0
cmp/eq #HWCALC_FXCG_MANAGER, r0
bt .set_imask
bra .load_sr
nop
.set_imask:
mov.l .SR_set_IMASK, r0
or r0, r1
.load_sr:
ldc r1, sr
/* We are now in the user bank with r0 set. Perform the call. We want
to forward the return value to kernel bank, but this bank can be
changed at any moment since interrupts are enabled. */
sts.l pr, @-r15
mov.l @(4, r0), r4
mov.l @(8, r0), r5
mov.l @(12, r0), r6
mov.l @(16, r0), r7
mov.l @r0, r0
jsr @r0
nop
lds.l @r15+, pr
/* Restore the previous status register and the registers of the
interrupted procedure. Restoring sr gets us back to system bank with
interrupts disabled. */
ldc.l @r15+, sr
/* We can now pull the return value since interrupts are disabled */
stc r0_bank, r0
ldc.l @r15+, ssr
ldc.l @r15+, spc
ldc.l @r15+, r7_bank
ldc.l @r15+, r6_bank
ldc.l @r15+, r5_bank
ldc.l @r15+, r4_bank
ldc.l @r15+, r3_bank
ldc.l @r15+, r2_bank
ldc.l @r15+, r1_bank
rts
ldc.l @r15+, r0_bank
.align 4
.SR_clear_RB_BL:
.long ~((1 << 29) | (1 << 28))
.SR_set_IMASK:
.long (0xf << 4)
.gint:
.long _gint
kernel: dynamic loading of GMAPPED functions to user RAM This commit introduces a large architectural change. Unlike previous models of the fx-9860G series, the G-III models have a new user RAM address different from 8801c000. The purpose of this change is to dynamically load GMAPPED functions to this address by querying the TLB, and call them through a function pointer whose address is determined when loading. Because of the overhead of using a function pointer in both assembly and C code, changes have been made to avoid GMAPPED functions altogether. Current, only cpu_setVBR() and gint_inth_callback() are left, the second being used specifically to enable TLB misses when needed. * Add a .gint.mappedrel section for the function pointers holding addresses to GMAPPED functions; add function pointers for cpu_setVBR() and gint_inth_callback() * Move rram to address 0 instead of the hardcoded 0x8801c000 * Load GMAPPED functions at their linked address + the physical address user RAM is mapped, to and compute their function pointers * Remove the GMAPPED macro since no user function needs it anymore * Add section flags "ax" (code) or "aw" (data) to every custom .section in assembler code, as they default to unpredictable values that can cause the section to be marked NOLOAD by the linker * Update the main kernel, TMU, ETMU and RTC interrupt handlers to use the new indirect calling method This is made possible by new MMU functions giving direct access to the physical area behind any virtualized page. * Add an mmu_translate() function to query the TLB * Add an mmu_uram() function to access user RAM from P1 The exception catching mechanism has been modified to avoid the use of GMAPPED functions altogether. * Set SR.BL=0 and SR.IMASK=15 before calling exception catchers * Move gint_exc_skip() to normal text ROM * Also fix registers not being popped off the stack before a panic The timer drivers have also been modified to avoid GMAPPED functions. * Invoke timer_stop() through gint_inth_callback() and move it to ROM * Move and expand the ETMU driver to span 3 blocks at 0xd00 (ETMU4) * Remove the timer_clear() function by inlining it into the ETMU handler (TCR is provided within the storage block of each timer) * Also split src/timer/inth.s into src/timer/inth-{tmu,etmu}.s Additionally, VBR addresses are now determined at runtime to further reduce hardcoded memory layout addresses in the linker script. * Determine fx-9860G VBR addresses dynamically from mmu_uram() * Determine fx-CG 50 VBR addresses dynamically from mmu_uram() * Remove linker symbols for VBR addresses Comments and documentation have been updated throughout the code to reflect the changes.
2020-09-17 14:48:54 +02:00
.section .gint.mappedrel, "aw"
_gint_inth_callback:
.long _gint_inth_callback_reloc