; ; coRoutine.s ; ; Basic coroutine handling (MDW) ; ; © 1994-1998 Straylight ; ;----- Licensing note ------------------------------------------------------- ; ; This file is part of Straylight's Sapphire library. ; ; Sapphire is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation; either version 2, or (at your option) ; any later version. ; ; Sapphire is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with Sapphire. If not, write to the Free Software Foundation, ; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ;----- Standard header ------------------------------------------------------ GET libs:header GET libs:swis ;----- External dependencies ------------------------------------------------ GET sapphire:alloc GET sapphire:sapphire GET sapphire:seh GET sapphire:suballoc ;----- Main code ------------------------------------------------------------ AREA |Sapphire$$Code|,CODE,READONLY coRout__stkSize EQU 1024 ; --- coRout_create --- ; ; On entry: R0 == pointer to coroutine ; R1 == R10 value to pass to coroutine ; R2 == R12 value to pass to coroutine ; R3 == size of stack to pass (0 for default) ; ; On exit: R0 == coroutine handle ; May return an error ; ; Use: Creates a new coroutine. It may be given control using ; coRout_switch. Its registers are on entry: ; ; R0 == its coroutine handle ; R10 == value passed to coRout_create in R1 ; R12 == value passed to coRout_create in R2 ; R13 == pointer to the stack created for it EXPORT coRout_create coRout_create ROUT STMFD R13!,{R0-R4,R14} ;Save some registers away ; --- Allocate a coroutine block --- MOV R0,#coRout__blkSize ;Get the block size ready BL sub_alloc ;Allocate the block BVS %99coRout_create ;Quit if it failed MOV R4,R0 ;Keep the block pointer ; --- Allocate a stack --- CMP R3,#0 ;Does he want default stack? MOVEQ R3,#coRout__stkSize ;Yes -- give it to him MOV R0,R3 ;Get the size in R0 BL alloc ;Try to allocate the stack BLCS alloc_error ;If it failed, get an error BCS %98coRout_create ;And quit if it didn't work STR R0,[R4,#coRout__stack] ;Save the stack pointer away ; --- Set up the try list --- MOV R14,#0 ;Must clear the list head STR R14,[R4,#coRout__tryList] ;Save that away for later ; --- Fix everything up right --- ADD R0,R0,R3 ;Point to the stack top ADR R3,coRout__start ;Point to the start routine MOV R14,PC ;Get the processor flags AND R14,R14,#&FC000003 ;Leave only the flags ORR R14,R3,R14 ;And add them on to it MOV R3,R2 ;Get coroutine's R12 in R3 MOV R2,R11 ;Point to scratchpad in R2 STMFD R0!,{R1-R3,R14} ;And save all that lot away SUB R0,R0,#32 ;Don't care about other regs LDR R14,[R13,#0] ;Load the coroutine address STMFD R0!,{R4,R14} ;Save them in R0 and R1 STR R0,[R4,#coRout__sp] ;And save its stack pointer ; --- Return to caller --- MOV R0,R4 ;Point to the coroutine block ADD R13,R13,#4 ;Don't restore caller's R0 LDMFD R13!,{R1-R4,R14} ;And return to caller BICS PC,R14,#V_flag ; --- Handle errors --- 98coRout_create MOV R3,R0 ;Save the error pointer MOV R0,R4 ;Point to the coroutine block MOV R1,#coRout__blkSize ;Get the block's size BL sub_free ;Free the block nicely MOV R0,R3 ;Restore the error pointer 99coRout_create ADD R13,R13,#4 ;Don't restore caller's R0 LDMFD R13!,{R1-R4,R14} ;And return to caller ORRS PC,R14,#V_flag LTORG ; --- coRout_switch --- ; ; On entry: R0 == coroutine to switch to, or 0 for main ; ; On exit: -- ; ; Use: Switches context to another coroutine. EXPORT coRout_switch coRout_switch ROUT STMFD R13!,{R12,R14} ;Save some registers WSPACE coRout__wSpace ;Find my workspace address LDR R14,coRout__current ;Get the current coroutine CMP R14,R0 ;Switch to current one? LDMEQFD R13!,{R12,PC}^ ;Silly sausage! ; --- Perform a context switch --- STMFD R13!,{R0-R11} ;Save all the other registers MOV R1,R14 ;Move this to another reg STR R0,coRout__current ;Save the new coroutine BL seh_setListBase ;Use this try list now CMP R1,#0 ;Are we running main routine? ADREQ R1,coRout__main ;Yes -- point to dummy block CMP R0,#0 ;Do we switch to main routine ADREQ R0,coRout__main ;Yes -- point to dummy block STR R13,[R1,#coRout__sp] ;Save the new stack pointer LDR R13,[R0,#coRout__sp] ;Get a new stack pointer LDMFD R13!,{R0-R12,PC}^ ;And switch context to it LTORG ; --- coRout_destroy --- ; ; On entry: R0 == coroutine handle to destroy ; ; On exit: -- ; ; Use: Destroys a coroutine. EXPORT coRout_destroy coRout_destroy ROUT STMFD R13!,{R12,R14} ;Save some registers away WSPACE coRout__wSpace ;Find my workspace address LDR R14,coRout__current ;Get the current coroutine CMP R14,R0 ;Terminate the current one? LDMEQFD R13!,{R12,R14} ;Yes -- unstack registers BEQ coRout_end ;And end it normally ; --- Now just destroy a coroutine --- STMFD R13!,{R0,R1} ;Save some registers MOV R1,R0 ;Keep the coroutine handle LDR R0,[R1,#coRout__stack] ;Find the stack's address BL free ;Free up the space it took MOV R0,R1 ;Point at the coroutine blk MOV R1,#coRout__blkSize ;Get the coroutine block size BL sub_free ;And dispose of that too LDMFD R13!,{R0,R1,R12,PC}^ ;And return to caller LTORG ; --- coRout__start --- ; ; On entry: R0 == coroutine handle ; R1 == pointer to coroutine code ; ; On exit: R14 == pointer to coRout_end ; ; Use: Sets up a coroutine so that when it returns, it calls ; coRout_end and dies properly. coRout__start ROUT ; --- Set up a handler to kill the coroutine --- ADR R0,coRout__catch ;Point to our catch handler BL seh_try ;Register that nicely ; --- Now start up the coroutine proper --- MOV R14,PC ;Set up the return address MOV PC,R1 ;And call the main code ; --- Fall through into coRout_end when done --- ; --- coRout_end --- ; ; On entry: -- ; ; On exit: Doesn't. ; ; Use: Terminates the current coroutine. EXPORT coRout_end coRout_end ROUT ; --- Destroy the current coroutine --- WSPACE coRout__wSpace ;Find my workspace LDR R13,coRout__main+coRout__sp ;Return to main stack LDR R1,coRout__current ;Get the current coroutine LDR R0,[R1,#coRout__stack] ;Find the stack's address BL free ;Free up the space it took MOV R0,R1 ;Point at the coroutine blk MOV R1,#coRout__blkSize ;Get the coroutine block size BL sub_free ;And dispose of that too ; --- Now rejoin the main routine --- MOV R0,#0 ;Going back to main routine STR R0,coRout__current ;So remember that BL seh_setListBase ;Use main exception list LDMFD R13!,{R0-R12,PC}^ ;And rejoin everything nicely LTORG ; --- Exception handling --- coRout__catch MOVS PC,R14 ;No need for tidy-up DCD -1 ;Catch all exceptions B coRout__throw ;And throw them on again DCD 0 coRout__throw WSPACE coRout__wSpace ;Find my workspace LDR R13,coRout__main+coRout__sp ;Return to main stack STMFD R13!,{R0-R3} ;Remember the exception LDR R1,coRout__current ;Load the old coroutine hnd LDR R0,[R1,#coRout__stack] ;Find the stack's address BL free ;Free up the space it took MOV R0,R1 ;Point at the coroutine blk MOV R1,#coRout__blkSize ;Get the coroutine block size BL sub_free ;And dispose of that too MOV R0,#0 ;Going back to main routine STR R0,coRout__current ;So remember that BL seh_setListBase ;Use main exception list LDMFD R13!,{R0-R3} ;Restore exception B seh_throw ;And raise it again coRout__wSpace DCD 0 ;----- Coroutine blocks ----------------------------------------------------- ^ 0 coRout__tryList # 4 ;SEH try list head for stack coRout__stack # 4 ;Pointer to the stack coRout__sp # 4 ;Coroutine's current R13 coRout__blkSize # 0 ;Size of a coroutine block ;----- Workspace ------------------------------------------------------------ ^ 0,R12 coRout__wStart # 0 coRout__current # 4 ;The current coroutine or 0 coRout__main # coRout__blkSize ;Dummy coroutine block coRout__wSize EQU {VAR}-coRout__wStart AREA |Sapphire$$LibData|,CODE,READONLY DCD coRout__wSize DCD coRout__wSpace DCD 0 DCD 0 ;----- That's all, folks ---------------------------------------------------- END