diff --git a/TODO b/TODO index f33b2e0..d46a7d7 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,4 @@ For the 2.1.0 release: -* core: use gint_switch() to handle TLB misses * core: the four basic memory functions (with automated tests) * bopti: remove the deprecated image_t definition * project: remove the compat branch @@ -12,6 +11,7 @@ Issues: * #10 support fx-CG 20 Extensions on existing code: +* bopti: try to display fullscreen images with TLB access + DMA on fxcg50 * gray: add gprint() * gray: double-buffer gray settings and unify d* with g* * topti: support unicode fonts diff --git a/include/gint/timer.h b/include/gint/timer.h index 90b6076..da0e986 100644 --- a/include/gint/timer.h +++ b/include/gint/timer.h @@ -141,6 +141,12 @@ void timer_pause(int timer); @timer Timer id, as returned by timer_setup() */ void timer_stop(int timer); +/* timer_wait() - wait for a timer to stop + Waits until the specified timer stops running. If the timer is not running, + returns immediately. The timer might not be free if it has just been paused + instead of stopped entirely. */ +void timer_wait(int timer); + //--- // Predefined timer callbacks //--- diff --git a/src/core/inth.S b/src/core/inth.S index 153781f..25f7f07 100644 --- a/src/core/inth.S +++ b/src/core/inth.S @@ -11,6 +11,8 @@ .global _gint_inth_7705 #endif +.global _gint_inth_callback + .section .gint.blocks, "ax" .align 4 @@ -152,3 +154,70 @@ _inth_remap: .byte 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff #endif + +/* 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. */ + +.section .gint.mapped + +/* gint_inth_callback() + Calls the specified function with the given argument after saving the user + context, enabling interrupts and going to user bank. + + @r4 Callback function (volatile void * -> int) + @r5 Argument (volatile void *) + Returns the return value of the callback (int). */ +_gint_inth_callback: + 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 + + /* Enable interrupts and go back to user bank. SR.IMASK is already set + to the level of the current interrupt, which makes sure we can only + be re-interrupted by something with a higher priority. */ + mov.l .SR_clear_RB_BL, r1 + stc sr, r0 + and r1, r0 + ldc r0, sr + + /* We are now in the user bank, but we can still use r0..r7 as the + important values have been saved on the stack. Call back. */ + stc r4_bank, r0 + sts.l pr, @-r15 + jsr @r0 + stc r5_bank, r4 + lds.l @r15+, pr + + /* We want to forward the return value to the system bank */ + ldc r0, r0_bank + + /* Restore the previous status register and the registers of the + interrupted procedure. */ + ldc.l @r15+, sr + 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)) diff --git a/src/core/syscalls.S b/src/core/syscalls.S index c16cab4..4867939 100644 --- a/src/core/syscalls.S +++ b/src/core/syscalls.S @@ -9,8 +9,7 @@ ** * File system, because it's a mess and we might ruin the ROM. */ -/* TLB management */ -.global ___TLB_LoadPTEH +.section .pretext /* Dynamic allocation */ .global _malloc @@ -51,26 +50,6 @@ #define syscall(id) syscall_(id, syscall_table) -/*** Special syscalls that must remain mapped ***/ - -.section .gint.mapped - -#ifdef FX9860G -___TLB_LoadPTEH: - syscall_(0x0003, 2f) -2: .long 0x80010070 -#endif - -#ifdef FXCG50 -___TLB_LoadPTEH: - syscall_(0x000c, 2f) -2: .long 0x80020070 -#endif - -/*** Normal syscalls in ROM ***/ - -.section .pretext - #ifdef FX9860G /* Dynamic allocation */ diff --git a/src/core/tlbh.S b/src/core/tlbh.S index a0f8b73..f0b325c 100644 --- a/src/core/tlbh.S +++ b/src/core/tlbh.S @@ -28,8 +28,15 @@ test_tea: map: /* If TEA is mappable, map a page and return */ - mov.l .TLB_LoadPTEH, r0 - jsr @r0 + #ifdef FX9860G + mov #3, r0 + #endif + #ifdef FXCG50 + mov #12, r0 + #endif + + mov.l .syscall, r2 + jsr @r2 nop lds.l @r15+, macl @@ -66,5 +73,12 @@ panic: .long 0x00300000 .max_mapped_rom: .long 0x00300000 + _srom -.TLB_LoadPTEH: - .long ___TLB_LoadPTEH + +#ifdef FX9860G +.syscall: + .long 0x80010070 +#endif +#ifdef FXCG50 +.syscall: + .long 0x80020070 +#endif diff --git a/src/core/vbr.s b/src/core/vbr.s index 8335e6e..5e33666 100644 --- a/src/core/vbr.s +++ b/src/core/vbr.s @@ -11,8 +11,8 @@ priority registers or the interrupt masks, and make sure that all the interrupts that it leaves enabled are handled by the new VBR handlers. - @arg vbr New VBR address (uint32_t) - @arg configure Configuration function (void -> void) + @r4 vbr New VBR address (uint32_t) + @r5 configure Configuration function (void -> void) Returns the previous VBR address. */ _gint_setvbr: mov.l r9, @-r15 diff --git a/src/tmu/inth.s b/src/tmu/inth.s index a7ad4bb..2a32128 100644 --- a/src/tmu/inth.s +++ b/src/tmu/inth.s @@ -40,9 +40,8 @@ _inth_tmu_0: mova .storage0, r0 mov #0, r1 - /*** This is the first shared section ***/ - -.clearflag: +/*** This is the first shared section ***/ +.shared1: sts.l pr, @-r15 mov.l r1, @-r15 @@ -54,32 +53,31 @@ _inth_tmu_0: mov.w r2, @r1 /* Invoke the callback function and pass the argument */ - mov.l @r0, r1 + mov.l .gint_inth_callback_1, r1 + mov.l @r0, r4 jsr @r1 - mov.l @(4, r0), r4 + mov.l @(4, r0), r5 - /* Prepare stopping the timer and jump to second section */ + /* Jump to second section */ + mov.l .timer_stop_1, r1 + bra .shared2 mov.l @r15+, r4 - mov.l .timer_stop, r1 - bra .stoptimer - nop /* SECOND GATE - TMU1 entry and stop timer */ _inth_tmu_1: mova .storage1, r0 - bra .clearflag + bra .shared1 mov #1, r1 - /*** This is the second shared section ***/ - -.stoptimer: +/*** This is the second shared section ***/ +.shared2: /* Stop the timer if the return value is not zero */ tst r0, r0 - bt .end + bt .shared3 jsr @r1 nop -.end: +.shared3: lds.l @r15+, pr rts nop @@ -89,10 +87,13 @@ _inth_tmu_1: /* THIRD GATE - TMU2 entry and storage for TMU0 */ _inth_tmu_2: mova .storage2, r0 - bra .clearflag + bra .shared1 mov #2, r1 - .zero 14 + .zero 10 + +.gint_inth_callback_1: + .long _gint_inth_callback .storage0: .long 0 /* Callback: Configured dynamically */ @@ -104,8 +105,8 @@ _inth_tmu_storage: .mask: .long 0x0000feff -.timer_stop: - .long _timer_stop /* gint's function from */ +.timer_stop_1: + .long _timer_stop .storage1: .long 0 /* Callback: Configured dynamically */ @@ -142,27 +143,24 @@ _inth_tmu_storage: /* 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 -.extra_callback: +.shared4: sts.l pr, @-r15 - mov.l r1, @-r15 + mov.l r0, @-r15 - /* Invoke the callback function */ - mov.l @r0, r1 - jsr @r1 - mov.l @(4, r0), r4 + /* Clear interrupt flag */ + mov.l .timer_clear, r2 + jsr @r2 + mov r1, r4 - /* Load timer ID and forward the callback's return value */ - mov.l .timer_clear, r1 - mov.l @r15+, r4 + /* Prepare invoking the callback function */ + mov.l @r15+, r0 + mov.l .gint_inth_callback_2, r1 + mov.l @r0, r4 bra _inth_etmu_help - mov r0, r5 - - nop + mov.l @(4, r0), r5 .storage_etmu2: .long 0 /* Callback: Configured dynamically */ @@ -171,18 +169,28 @@ _inth_etmu2: /* SECOND GATE - Helper entry, invoke callback and stop timer if requested */ _inth_etmu_help: - /* Clear the flag and possibly stop the timer */ + /* Invoke callback; if return value is non-zero, stop timer */ + jsr @r1 + nop + tst r0, r0 + bt .shared5 + mov.l .timer_stop_2, r1 jsr @r1 nop + /* Clear the flag and possibly stop the timer */ + +.shared5: lds.l @r15+, pr rts nop - .zero 18 - +.gint_inth_callback_2: + .long _gint_inth_callback .timer_clear: - .long _timer_clear /* gint's function from src/tmu/tmu.c */ + .long _timer_clear +.timer_stop_2: + .long _timer_stop /* THIRD GATE - All other ETMU entries, deferred to the previous ones */ _inth_etmux: @@ -198,11 +206,11 @@ _inth_etmux: nop /* Offset from VBR where extra timer 2 is located: - - 0x600 to reach the interrupt handlers - - 0x040 to jump over the entry gate - - 0x840 to reach the handler of ETMU2 - - 0x004 to skip its first instructions (the size is hardcoded) */ -1: .long 0xe84 + * 0x600 to reach the interrupt handlers + * 0x040 to jump over the entry gate + * 0x840 to reach the handler of ETMU2 + * Skip over the first instructions */ +1: .long 0xe80 + (.shared4 - _inth_etmu2) .id_etmux: .long 0 /* Timer ID */ diff --git a/src/tmu/tmu.c b/src/tmu/tmu.c index 3dd56b4..4b9a111 100644 --- a/src/tmu/tmu.c +++ b/src/tmu/tmu.c @@ -162,6 +162,22 @@ GMAPPED void timer_stop(int id) } } +/* timer_wait() - wait until a timer is stopped */ +void timer_wait(int id) +{ + if(id < 3) + { + tmu_t *T = &TMU[id]; + /* Sleep if an interruption will wake us up */ + while(*TSTR & (1 << id)) if(T->TCR.UNIE) sleep(); + } + else + { + etmu_t *T = &ETMU[id]; + while(T->TSTR) if(T->TCR.UNIE) sleep(); + } +} + //--- // Predefined timer callbacks //--- @@ -178,15 +194,12 @@ GMAPPED int timer_timeout(volatile void *arg) // Low-level functions //--- -/* timer_clear() - clear an ETMU flag and possibly stop the timer - @timer Timer ID, must be an ETMU - @stop Non-zero to stop the timer */ -GMAPPED void timer_clear(int id, int stop) +/* timer_clear() - clear an ETMU flag + @timer Timer ID, must be an ETMU */ +GMAPPED void timer_clear(int id) { do ETMU[id-3].TCR.UNF = 0; while(ETMU[id-3].TCR.UNF); - - if(stop) timer_stop(id); } //--- @@ -270,7 +283,7 @@ static void init(void) gint_inthandler(0xc60, inth_etmu_help, 32); /* Enable TMU0 at level 13, TMU1 at level 11, TMU2 at level 9 */ - gint_intlevel(0, 7); + gint_intlevel(0, 13); gint_intlevel(1, 11); gint_intlevel(2, 9);