Initial revision
[ssr] / StraySrc / Libraries / Sapphire / s / coRoutine
1 ;
2 ; coRoutine.s
3 ;
4 ; Basic coroutine handling (MDW)
5 ;
6 ; © 1994-1998 Straylight
7 ;
8
9 ;----- Licensing note -------------------------------------------------------
10 ;
11 ; This file is part of Straylight's Sapphire library.
12 ;
13 ; Sapphire is free software; you can redistribute it and/or modify
14 ; it under the terms of the GNU General Public License as published by
15 ; the Free Software Foundation; either version 2, or (at your option)
16 ; any later version.
17 ;
18 ; Sapphire is distributed in the hope that it will be useful,
19 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ; GNU General Public License for more details.
22 ;
23 ; You should have received a copy of the GNU General Public License
24 ; along with Sapphire. If not, write to the Free Software Foundation,
25 ; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26
27 ;----- Standard header ------------------------------------------------------
28
29 GET libs:header
30 GET libs:swis
31
32 ;----- External dependencies ------------------------------------------------
33
34 GET sapphire:alloc
35 GET sapphire:sapphire
36 GET sapphire:seh
37 GET sapphire:suballoc
38
39 ;----- Main code ------------------------------------------------------------
40
41 AREA |Sapphire$$Code|,CODE,READONLY
42
43 coRout__stkSize EQU 1024
44
45 ; --- coRout_create ---
46 ;
47 ; On entry: R0 == pointer to coroutine
48 ; R1 == R10 value to pass to coroutine
49 ; R2 == R12 value to pass to coroutine
50 ; R3 == size of stack to pass (0 for default)
51 ;
52 ; On exit: R0 == coroutine handle
53 ; May return an error
54 ;
55 ; Use: Creates a new coroutine. It may be given control using
56 ; coRout_switch. Its registers are on entry:
57 ;
58 ; R0 == its coroutine handle
59 ; R10 == value passed to coRout_create in R1
60 ; R12 == value passed to coRout_create in R2
61 ; R13 == pointer to the stack created for it
62
63 EXPORT coRout_create
64 coRout_create ROUT
65
66 STMFD R13!,{R0-R4,R14} ;Save some registers away
67
68 ; --- Allocate a coroutine block ---
69
70 MOV R0,#coRout__blkSize ;Get the block size ready
71 BL sub_alloc ;Allocate the block
72 BVS %99coRout_create ;Quit if it failed
73 MOV R4,R0 ;Keep the block pointer
74
75 ; --- Allocate a stack ---
76
77 CMP R3,#0 ;Does he want default stack?
78 MOVEQ R3,#coRout__stkSize ;Yes -- give it to him
79 MOV R0,R3 ;Get the size in R0
80 BL alloc ;Try to allocate the stack
81 BLCS alloc_error ;If it failed, get an error
82 BCS %98coRout_create ;And quit if it didn't work
83 STR R0,[R4,#coRout__stack] ;Save the stack pointer away
84
85 ; --- Set up the try list ---
86
87 MOV R14,#0 ;Must clear the list head
88 STR R14,[R4,#coRout__tryList] ;Save that away for later
89
90 ; --- Fix everything up right ---
91
92 ADD R0,R0,R3 ;Point to the stack top
93 ADR R3,coRout__start ;Point to the start routine
94 MOV R14,PC ;Get the processor flags
95 AND R14,R14,#&FC000003 ;Leave only the flags
96 ORR R14,R3,R14 ;And add them on to it
97 MOV R3,R2 ;Get coroutine's R12 in R3
98 MOV R2,R11 ;Point to scratchpad in R2
99 STMFD R0!,{R1-R3,R14} ;And save all that lot away
100 SUB R0,R0,#32 ;Don't care about other regs
101 LDR R14,[R13,#0] ;Load the coroutine address
102 STMFD R0!,{R4,R14} ;Save them in R0 and R1
103 STR R0,[R4,#coRout__sp] ;And save its stack pointer
104
105 ; --- Return to caller ---
106
107 MOV R0,R4 ;Point to the coroutine block
108 ADD R13,R13,#4 ;Don't restore caller's R0
109 LDMFD R13!,{R1-R4,R14} ;And return to caller
110 BICS PC,R14,#V_flag
111
112 ; --- Handle errors ---
113
114 98coRout_create MOV R3,R0 ;Save the error pointer
115 MOV R0,R4 ;Point to the coroutine block
116 MOV R1,#coRout__blkSize ;Get the block's size
117 BL sub_free ;Free the block nicely
118 MOV R0,R3 ;Restore the error pointer
119
120 99coRout_create ADD R13,R13,#4 ;Don't restore caller's R0
121 LDMFD R13!,{R1-R4,R14} ;And return to caller
122 ORRS PC,R14,#V_flag
123
124 LTORG
125
126 ; --- coRout_switch ---
127 ;
128 ; On entry: R0 == coroutine to switch to, or 0 for main
129 ;
130 ; On exit: --
131 ;
132 ; Use: Switches context to another coroutine.
133
134 EXPORT coRout_switch
135 coRout_switch ROUT
136
137 STMFD R13!,{R12,R14} ;Save some registers
138 WSPACE coRout__wSpace ;Find my workspace address
139 LDR R14,coRout__current ;Get the current coroutine
140 CMP R14,R0 ;Switch to current one?
141 LDMEQFD R13!,{R12,PC}^ ;Silly sausage!
142
143 ; --- Perform a context switch ---
144
145 STMFD R13!,{R0-R11} ;Save all the other registers
146 MOV R1,R14 ;Move this to another reg
147 STR R0,coRout__current ;Save the new coroutine
148 BL seh_setListBase ;Use this try list now
149 CMP R1,#0 ;Are we running main routine?
150 ADREQ R1,coRout__main ;Yes -- point to dummy block
151 CMP R0,#0 ;Do we switch to main routine
152 ADREQ R0,coRout__main ;Yes -- point to dummy block
153 STR R13,[R1,#coRout__sp] ;Save the new stack pointer
154 LDR R13,[R0,#coRout__sp] ;Get a new stack pointer
155 LDMFD R13!,{R0-R12,PC}^ ;And switch context to it
156
157 LTORG
158
159 ; --- coRout_destroy ---
160 ;
161 ; On entry: R0 == coroutine handle to destroy
162 ;
163 ; On exit: --
164 ;
165 ; Use: Destroys a coroutine.
166
167 EXPORT coRout_destroy
168 coRout_destroy ROUT
169
170 STMFD R13!,{R12,R14} ;Save some registers away
171 WSPACE coRout__wSpace ;Find my workspace address
172 LDR R14,coRout__current ;Get the current coroutine
173 CMP R14,R0 ;Terminate the current one?
174 LDMEQFD R13!,{R12,R14} ;Yes -- unstack registers
175 BEQ coRout_end ;And end it normally
176
177 ; --- Now just destroy a coroutine ---
178
179 STMFD R13!,{R0,R1} ;Save some registers
180 MOV R1,R0 ;Keep the coroutine handle
181 LDR R0,[R1,#coRout__stack] ;Find the stack's address
182 BL free ;Free up the space it took
183 MOV R0,R1 ;Point at the coroutine blk
184 MOV R1,#coRout__blkSize ;Get the coroutine block size
185 BL sub_free ;And dispose of that too
186 LDMFD R13!,{R0,R1,R12,PC}^ ;And return to caller
187
188 LTORG
189
190 ; --- coRout__start ---
191 ;
192 ; On entry: R0 == coroutine handle
193 ; R1 == pointer to coroutine code
194 ;
195 ; On exit: R14 == pointer to coRout_end
196 ;
197 ; Use: Sets up a coroutine so that when it returns, it calls
198 ; coRout_end and dies properly.
199
200 coRout__start ROUT
201
202 ; --- Set up a handler to kill the coroutine ---
203
204 ADR R0,coRout__catch ;Point to our catch handler
205 BL seh_try ;Register that nicely
206
207 ; --- Now start up the coroutine proper ---
208
209 MOV R14,PC ;Set up the return address
210 MOV PC,R1 ;And call the main code
211
212 ; --- Fall through into coRout_end when done ---
213
214 ; --- coRout_end ---
215 ;
216 ; On entry: --
217 ;
218 ; On exit: Doesn't.
219 ;
220 ; Use: Terminates the current coroutine.
221
222 EXPORT coRout_end
223 coRout_end ROUT
224
225 ; --- Destroy the current coroutine ---
226
227 WSPACE coRout__wSpace ;Find my workspace
228 LDR R13,coRout__main+coRout__sp ;Return to main stack
229 LDR R1,coRout__current ;Get the current coroutine
230 LDR R0,[R1,#coRout__stack] ;Find the stack's address
231 BL free ;Free up the space it took
232 MOV R0,R1 ;Point at the coroutine blk
233 MOV R1,#coRout__blkSize ;Get the coroutine block size
234 BL sub_free ;And dispose of that too
235
236 ; --- Now rejoin the main routine ---
237
238 MOV R0,#0 ;Going back to main routine
239 STR R0,coRout__current ;So remember that
240 BL seh_setListBase ;Use main exception list
241 LDMFD R13!,{R0-R12,PC}^ ;And rejoin everything nicely
242
243 LTORG
244
245 ; --- Exception handling ---
246
247 coRout__catch MOVS PC,R14 ;No need for tidy-up
248 DCD -1 ;Catch all exceptions
249 B coRout__throw ;And throw them on again
250 DCD 0
251
252 coRout__throw WSPACE coRout__wSpace ;Find my workspace
253 LDR R13,coRout__main+coRout__sp ;Return to main stack
254 STMFD R13!,{R0-R3} ;Remember the exception
255 LDR R1,coRout__current ;Load the old coroutine hnd
256 LDR R0,[R1,#coRout__stack] ;Find the stack's address
257 BL free ;Free up the space it took
258 MOV R0,R1 ;Point at the coroutine blk
259 MOV R1,#coRout__blkSize ;Get the coroutine block size
260 BL sub_free ;And dispose of that too
261
262 MOV R0,#0 ;Going back to main routine
263 STR R0,coRout__current ;So remember that
264 BL seh_setListBase ;Use main exception list
265 LDMFD R13!,{R0-R3} ;Restore exception
266 B seh_throw ;And raise it again
267
268 coRout__wSpace DCD 0
269
270 ;----- Coroutine blocks -----------------------------------------------------
271
272 ^ 0
273 coRout__tryList # 4 ;SEH try list head for stack
274 coRout__stack # 4 ;Pointer to the stack
275 coRout__sp # 4 ;Coroutine's current R13
276 coRout__blkSize # 0 ;Size of a coroutine block
277
278 ;----- Workspace ------------------------------------------------------------
279
280 ^ 0,R12
281 coRout__wStart # 0
282
283 coRout__current # 4 ;The current coroutine or 0
284 coRout__main # coRout__blkSize ;Dummy coroutine block
285
286 coRout__wSize EQU {VAR}-coRout__wStart
287
288 AREA |Sapphire$$LibData|,CODE,READONLY
289
290 DCD coRout__wSize
291 DCD coRout__wSpace
292 DCD 0
293 DCD 0
294
295 ;----- That's all, folks ----------------------------------------------------
296
297 END