chiark / gitweb /
JPEG support and other fixes from Nick Clark
[ssr] / StraySrc / Libraries / Sapphire / s / thread
1 ;
2 ; thread.s
3 ;
4 ; Preemptive multitasking of idle threads (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:suballoc
36                 GET     sapphire:idle
37                 GET     sapphire:msgs
38                 GET     sapphire:sapphire
39
40 ;----- Main code ------------------------------------------------------------
41
42                 AREA    |Sapphire$$Code|,CODE,READONLY
43
44 thr__stkSize    EQU     1024
45 thr__idleFreq   EQU     1
46
47 ; --- thread_create ---
48 ;
49 ; On entry:     R0 == size of stack to allocate, or 0 for a default
50 ;               R1 == pointer to thread routine
51 ;               R2 == workspace pointer to pass in R10
52 ;               R3 == workspace pointer to pass in R12
53 ;
54 ; On exit:      R0 == thread handle for the thread
55 ;               May return an error
56 ;
57 ; Use:          Creates a new thread running `in the background' (i.e. over
58 ;               idle events).
59 ;
60 ;               The thread is passed control with the registers R10 and R12
61 ;               set up from R1 and R2 passed to this routine and R13 pointing
62 ;               to the top of a stack chunk.  R0 on entry contains the
63 ;               thread's handle.  The thread is passed the scratchpad
64 ;               address (in R11).  The values of other registers are
65 ;               indeterminate and must not be relied upon.
66 ;
67 ;               The default stack size for a new thread is 1K, although this
68 ;               may change in future.
69 ;
70 ;               The thread may exit by calling thread_destroy or by
71 ;               returning in the normal way.
72
73                 EXPORT  thread_create
74 thread_create   ROUT
75
76                 STMFD   R13!,{R1-R8,R12,R14}    ;Save some registers
77                 WSPACE  thr__wSpace             ;Load my workspace pointer
78
79                 ; --- Try to allocate the stack ---
80
81                 CMP     R0,#0                   ;Does he want the default?
82                 MOVEQ   R0,#thr__stkSize        ;Yes -- give it to him
83                 CMP     R0,#256                 ;Make sure it's big enough
84                 MOVLT   R0,#256                 ;If not, make it bigger
85                 MOV     R5,R0                   ;Keep a copy of the size
86                 BL      alloc                   ;Try to allocate it nicely
87                 BLCS    alloc_error             ;It failed, so get an error
88                 BVS     %99thread_create        ;And skip to the end
89                 MOV     R6,R0                   ;Keep the stack block ptr
90
91                 ; --- Now allocate a thread block ---
92
93                 MOV     R0,#thr__size           ;The size of the block
94                 BL      alloc                   ;Allocate the block for me
95                 BLCS    alloc_error             ;It failed, so get an error
96                 BVS     %98thread_create        ;If it failed, skip onward
97
98                 ; --- Set up the initial context ---
99
100                 ADD     R5,R6,R5                ;R5 is the inital R13 to give
101                 MOV     R14,R1                  ;Get start address in R14
102                 MOV     R1,R2                   ;Move `R10' down a register
103                 MOV     R2,R11                  ;Give scratchpad in R11
104                 ADR     R4,thread_destroy       ;Returning destroys thread
105                 STMFD   R5!,{R1-R4,R14}         ;Save these on the stack
106                 STMFD   R5!,{R0-R9}             ;Fill the rest with rubbish
107
108                 ; --- Fill in the block ---
109
110                 ADD     R14,R0,#thr__suspend    ;Don't fill in the links
111                 STMIA   R14,{R1-R8}             ;Store information in block
112                 MOV     R1,#0                   ;Thread is not suspended yet
113                 MOV     R2,#0                   ;Thread not blocked on sem
114                 MOV     R3,#0                   ;Start at standard priority
115                 MOV     R4,#thr__idleFreq       ;Set standard timeslice
116                 STMIA   R14!,{R1-R4}            ;Save these in the block
117                 MOV     R4,#0                   ;Not in critical section
118                 MOV     R7,#0                   ;No flags to speak of yet
119                 MOV     R8,#0                   ;No error handler defined
120                 STMIA   R14!,{R4-R8}            ;Fill the rest in too
121                 BL      thr__insert             ;Insert it into the list
122
123                 ; --- Bump the active counter ---
124
125                 BL      thr__incCount           ;Bump the active counter
126                 LDMFD   R13!,{R1-R8,R12,R14}    ;Restore all the registers
127                 BICS    PC,R14,#V_flag          ;Return without V set
128
129                 ; --- We stumbled across a mishap ---
130
131 98thread_create MOV     R4,R0                   ;Keep the error pointer
132                 MOV     R0,R6                   ;Point to the stack block
133                 BL      free                    ;Deallocate it -- don't want
134                 MOV     R0,R4                   ;Point to the error again
135
136 99thread_create ADD     R2,R0,#4                ;Point to the error text
137                 ADR     R0,thr__noCreate        ;Point to the error block
138                 BL      msgs_error              ;Set up the error nicely
139                 LDMFD   R13!,{R1-R8,R12,R14}    ;Restore all the registers
140                 ORRS    PC,R14,#V_flag          ;Return with V set
141
142 thr__noCreate   DCD     1
143                 DCB     "thrNOCRT",0
144
145                 LTORG
146
147 ; --- thread_setPriority ---
148 ;
149 ; On entry:     R0 == thread handle
150 ;               R1 == new priority to set
151 ;
152 ; On exit:      --
153 ;
154 ; Use:          Changes the priority of a thread.  The priority if a thread
155 ;               is a signed integer.  The highest priority thread is the one
156 ;               which runs.  If more than one thread has maximum priority,
157 ;               they are run in a cyclical order.
158
159                 EXPORT  thread_setPriority
160 thread_setPriority ROUT
161
162                 STMFD   R13!,{R2,R3,R12,R14}    ;Save some registers here
163                 WSPACE  thr__wSpace             ;Load my workspace pointer
164                 LDR     R14,[R0,#thr__priority] ;Get the current priority
165                 CMP     R1,R14                  ;Are we changing anything?
166                 LDMEQFD R13!,{R2,R3,R12,PC}^    ;No -- return right now
167
168                 ; --- Unlink the thread ---
169
170                 LDMIA   R0,{R2,R3}              ;Get the next and previous
171                 CMP     R2,#0                   ;Is there a next pointer?
172                 STRNE   R3,[R2,#thr__prev]      ;Yes -- fill in its prev
173                 CMP     R3,#0                   ;Is there a prev pointer?
174                 STRNE   R2,[R3,#thr__next]      ;Yes -- fill in its next
175                 STREQ   R2,wsp__threads         ;No -- make next first thread
176
177                 ; --- Change priority and insert the thread again ---
178
179                 STR     R1,[R0,#thr__priority]  ;Save the new priority
180                 BL      thr__insert             ;Insert in the right place
181                 LDMFD   R13!,{R2,R3,R12,PC}^    ;Return to caller
182
183                 LTORG
184
185 ; --- thread_setTimeSlice ---
186 ;
187 ; On entry:     R0 == thread handle
188 ;               R1 == new timeslice size, in centiseconds
189 ;
190 ; On exit:      --
191 ;
192 ; Use:          Changes a thread's timeslice size.  Specify 0 to indicate
193 ;               that thread shouldn't be pre-empted.
194
195                 EXPORT  thread_setTimeSlice
196 thread_setTimeSlice ROUT
197
198                 STR     R1,[R0,#thr__timeSlice] ;Save the new timeslice
199                 MOVS    PC,R14                  ;Return to caller
200
201                 LTORG
202
203
204 ; --- thread_destroy ---
205 ;
206 ; On entry:     R0 == thread handle to destroy, if not executing a thread
207 ;
208 ; On exit:      --
209 ;
210 ; Use:          Destroys either the current thread or a thread with the
211 ;               the given handle if no thread is executing currently.  You
212 ;               can't destroy an arbitrary thread while running in one.
213 ;
214 ;               If a thread is waiting for a semaphore, it is removed from
215 ;               the waiting list.
216
217                 EXPORT  thread_destroy
218 thread_destroy  ROUT
219
220                 STMFD   R13!,{R0-R5,R12,R14}    ;Save some registers
221                 WSPACE  thr__wSpace             ;Find my workspace
222                 MOV     R5,R0                   ;Keep the thread pointer
223
224                 ; --- Find out which thread to destroy ---
225
226                 LDR     R4,wsp__current         ;Get the current thread
227                 CMP     R4,#0                   ;Is there one running?
228                 MOVNE   R5,R4                   ;Yes -- destroy it then
229
230                 ; --- If the thread was active, decrement active count ---
231
232                 LDR     R14,[R5,#thr__suspend]  ;Get the suspension counter
233                 CMP     R14,#0                  ;Is it currently active?
234                 BLEQ    thr__decCount           ;Yes -- decrement actives
235
236                 ; --- Remove thread from semaphore waiting lists ---
237
238                 LDR     R3,[R5,#thr__semaphore] ;Get the blocking semaphore
239                 CMP     R3,#0                   ;Is there one?
240                 BEQ     %10thread_destroy       ;No -- skip this part
241
242                 MOV     R2,#0                   ;Previous item in the list
243                 LDR     R0,[R3,#sem__blocked]   ;Get the blocked list
244 00              CMP     R0,#0                   ;Have we reached the end?
245                 BEQ     %10thread_destroy       ;Yes -- skip onwards
246                 LDR     R14,[R0,#sml__thread]   ;Get the waiting thread hnd
247                 CMP     R14,R5                  ;Is it this thread?
248                 MOVNE   R2,R0                   ;No -- update previous ptr
249                 LDRNE   R0,[R0,#sml__next]      ;Find the next link block
250                 BNE     %00thread_destroy       ;And move to next block
251
252                 ; --- Delete this thread waiting block ---
253
254                 LDR     R14,[R0,#sml__next]     ;Find the next link block
255                 CMP     R2,#0                   ;Is there a previous block?
256                 STRNE   R14,[R2,#sml__next]     ;Yes -- store it as next
257                 STREQ   R14,[R3,#sem__blocked]  ;No -- next is new first item
258                 CMP     R14,#0                  ;Is there a next block?
259                 STREQ   R2,[R3,#sem__blockEnd]  ;No -- previous one is last
260
261                 BL      free                    ;Destroy the link block
262
263                 ; --- Delink the thread then ---
264
265 10              LDMIA   R5,{R1,R2}              ;R1 == next, R2 == prev
266                 CMP     R1,#0                   ;Is there a next?
267                 STRNE   R2,[R1,#thr__prev]      ;Yes -- store prev away
268                 CMP     R2,#0                   ;Is there a prev?
269                 STRNE   R1,[R2,#thr__next]      ;Yes -- store next pointer
270                 STREQ   R1,wsp__threads         ;No -- store as list head
271
272                 ; --- Destroy the thread's memory ---
273
274                 LDR     R0,[R5,#thr__stack]     ;Find the thread's stack
275                 BL      free                    ;Get rid of it -- it's no use
276                 MOV     R0,R5                   ;Get the pointer back again
277                 MOV     R1,#thr__size           ;The size of the block
278                 BL      sub_free                ;Destroy the thread block
279
280                 ; --- Now we must return, but where to? ---
281                 ;
282                 ; If the caller is not the thread itself, this is easy.
283                 ; If it *was* the thread, we've just killed its stack, so
284                 ; we can't return to it.  Instead, we do the same job as
285                 ; thread_yield.
286
287                 CMP     R4,#0                   ;Was there a current thread?
288                 LDMEQFD R13!,{R0-R5,R12,PC}^    ;No -- return normally
289
290                 MOV     R0,#0                   ;We're stopping current thrd
291                 STR     R0,wsp__current         ;So clear current thread
292
293                 BL      thr__end                ;Kill off any handlers
294                 LDR     R13,wsp__stackPtr       ;Get the system stack pointer
295                 LDMFD   R13!,{R0-R12,PC}^       ;Return to thread dispatcher
296
297                 LTORG
298
299 ; --- thr__decCount ---
300 ;
301 ; On entry:     --
302 ;
303 ; On exit:      --
304 ;
305 ; Use:          Decrements the active threads counter, and disables the idle
306 ;               claimer if there aren't any left.
307
308 thr__decCount   ROUT
309
310                 STMFD   R13!,{R0-R3,R14}        ;Save registers
311                 LDR     R14,wsp__active         ;Get the active counter
312                 SUBS    R14,R14,#1              ;Decrement the counter
313                 STR     R14,wsp__active         ;Store it back again
314                 LDMGTFD R13!,{R0-R3,PC}^        ;If some still active, return
315
316                 ; --- Remove the idle claiming routine ---
317
318                 MOV     R0,#thr__idleFreq       ;How often I should be called
319                 ADR     R1,thr__idles           ;Point to the idle handler
320                 MOV     R2,#0                   ;Don't care about R10
321                 MOV     R3,R12                  ;Pass workspace in R12
322                 BL      idle_removeHandler      ;Stop it from being called
323
324                 LDMFD   R13!,{R0-R3,PC}^        ;Return to caller
325
326                 LTORG
327
328 ; --- thr__incCount ---
329 ;
330 ; On entry:     --
331 ;
332 ; On exit:      --
333 ;
334 ; Use:          Increments the active threads counter, and adds in the idle
335 ;               claimer if it was previously disabled.
336
337 thr__incCount   ROUT
338
339                 STMFD   R13!,{R0-R3,R14}        ;Save registers
340                 LDR     R14,wsp__active         ;Get the active counter
341                 ADD     R14,R14,#1              ;Increment the counter
342                 STR     R14,wsp__active         ;Store it back again
343                 CMP     R14,#1                  ;Was it previously off?
344                 LDMGTFD R13!,{R0-R3,PC}^        ;No -- return right now
345
346                 ; --- Add in the idle claiming routine ---
347
348                 MOV     R0,#thr__idleFreq       ;How often I should be called
349                 ADR     R1,thr__idles           ;Point to the idle handler
350                 MOV     R2,#0                   ;Don't care about R10
351                 MOV     R3,R12                  ;Pass workspace in R12
352                 BL      idle_handler    ;Get it called nicely
353                 LDMFD   R13!,{R0-R3,PC}^        ;Return to caller
354
355                 LTORG
356
357 ; --- thr__idles ---
358 ;
359 ; On entry:     --
360 ;
361 ; On exit:      --
362 ;
363 ; Use:          Handles idle events by passing control to each thread in
364 ;               turn.  This is the main scheduler for the threads.
365 ;
366 ;               We use a simple but well-respected algorithm.  We find the
367 ;               first active thread in the list, move it to the end of its
368 ;               priority group and run it.
369
370 thr__idles      ROUT
371
372                 ; --- Save current context away ---
373                 ;
374                 ; We don't actually return from this routine -- actually
375                 ; we restore from thread_destroy, thread_suspend or
376                 ; thread_yield, or on the CallBack from a timer interrupt.
377
378                 STMFD   R13!,{R0-R12,R14}       ;Save context on the stack
379                 STR     R13,wsp__stackPtr       ;Save the stack pointer
380
381                 ; --- Now we need to find an unsuspended thread ---
382                 ;
383                 ; There must be an active thread, because we don't get called
384                 ; unless the count is non-0
385
386                 LDR     R10,wsp__threads        ;Find the first thread
387 10thr__idles    CMP     R10,#0                  ;Is this the end?
388                 LDMEQFD R13!,{R0-R12,PC}^       ;Yes -- nothing to do then
389                 LDR     R0,[R10,#thr__suspend]  ;Get the thread's suspend
390                 CMP     R0,#0                   ;Is this one active?
391                 LDRNE   R10,[R10,#thr__next]    ;No -- get the next one out
392                 BNE     %10thr__idles           ;And go round again
393
394                 ; --- We have an active thread ---
395                 ;
396                 ; We now unlink the thread and move it to the end of its
397                 ; priority group in the list, so next time the next one in
398                 ; the group gets a chance.
399
400                 LDR     R9,[R10,#thr__priority] ;Get the thread's priority
401                 LDMIA   R10,{R0,R1}             ;Get next and prev pointers
402                 CMP     R0,#0                   ;Is there a next pointer?
403                 STRNE   R1,[R0,#thr__prev]      ;Yes -- fill in its prev
404                 CMP     R1,#0                   ;Is there a prev pointer?
405                 STRNE   R0,[R1,#thr__next]      ;Yes -- fill in its next
406                 STREQ   R0,wsp__threads         ;No -- make it the new first
407
408                 ; --- Now search forwards for the end of the group ---
409
410 20thr__idles    CMP     R0,#0                   ;Is this the list end?
411                 BEQ     %25thr__idles           ;Yes -- skip out of loop
412                 LDR     R8,[R0,#thr__priority]  ;Get the next one's priority
413                 CMP     R8,R9                   ;Do they match up nicely?
414                 MOVGE   R1,R0                   ;This is the previous one
415                 LDRGE   R0,[R0,#thr__next]      ;Get the next one out
416                 BGE     %20thr__idles           ;And loop round again
417
418                 ; --- Insert our thread between R1 and R0 ---
419
420 25thr__idles    CMP     R1,#0                   ;Is the previous one OK?
421                 STRNE   R10,[R1,#thr__next]     ;Yes -- fill in its next
422                 STREQ   R10,wsp__threads        ;Otherwise make it first one
423                 CMP     R0,#0                   ;Is there a next one?
424                 STRNE   R10,[R0,#thr__prev]     ;Yes -- fill in its previous
425                 STMIA   R10,{R0,R1}             ;Save next and prev back
426
427                 ; --- Now we can run the thread at last ---
428
429                 LDR     R0,[R10,#thr__timeSlice] ;Load the timeslice we want
430                 BL      thr__start              ;Set up timer interrupt
431                 STR     R10,wsp__current        ;Save the current thread
432                 LDR     R13,[R10,#thr__stackPtr] ;Find its stack pointer
433                 LDMFD   R13!,{R0-R12,R14,PC}^   ;Start it up again
434
435                 LTORG
436
437 ; --- thr__insert ---
438 ;
439 ; On entry:     R0 == pointer to thread block
440 ;
441 ; On exit:      --
442 ;
443 ; Use:          Inserts a thread into the thread list in the right place for
444 ;               its priority.
445
446 thr__insert     ROUT
447
448                 STMFD   R13!,{R1-R4,R14}        ;Save some registers
449
450                 LDR     R4,[R0,#thr__priority]  ;Get the thread;s priority
451                 LDR     R1,wsp__threads         ;Find the first item
452                 MOV     R2,#0                   ;No previous list item yet
453 10thr__insert   CMP     R1,#0                   ;Is this the end of the list?
454                 BEQ     %20thr__insert          ;Yes -- skip forwards
455                 LDR     R3,[R1,#thr__priority]  ;Get its priority ready
456                 CMP     R3,R4                   ;How do they compare?
457                 MOVGT   R2,R1                   ;Too high -- now previous
458                 LDRGT   R1,[R1,#thr__next]      ;Find the next thread handle
459                 BGT     %10thr__insert          ;And loop round for another
460
461                 ; --- Insert the thread between R2 and R1 ---
462
463 20thr__insert   CMP     R2,#0                   ;Is there a previous one?
464                 STRNE   R0,[R2,#thr__next]      ;Yes -- fill in its next
465                 STREQ   R0,wsp__threads         ;No -- this is the first one
466                 CMP     R1,#0                   ;Is there a next one?
467                 STRNE   R0,[R1,#thr__prev]      ;Yes -- fill in its prev
468                 STMIA   R0,{R1,R2}              ;Store next and prev in block
469                 LDMFD   R13!,{R1-R4,PC}^        ;Return to caller then
470
471                 LTORG
472
473 ; --- thread_suspend ---
474 ;
475 ; On entry:     R0 == thread handle, or 0 for the current thread
476 ;
477 ; On exit:      --
478 ;
479 ; Use:          Suspends a thread's execution.  If a thread is currently
480 ;               running, that thread is suspended.  Otherwise, any thread
481 ;               may be suspended.
482 ;
483 ;               If the thread is currently claiming semaphores, the
484 ;               semaphores are not released, because we don't whether the
485 ;               system is in a fit state for this.
486 ;
487 ;               Thread suspensions are counted.  i.e. if you suspend a thread
488 ;               5 times, you have to resume it 5 times for it to become
489 ;               active again.
490
491                 EXPORT  thread_suspend
492 thread_suspend  ROUT
493
494                 STMFD   R13!,{R12,R14}          ;Save some registers away
495                 WSPACE  thr__wSpace             ;Locate my workspace
496                 LDR     R14,wsp__current        ;Get the current thread
497                 CMP     R0,#0                   ;Is current one wanted?
498                 CMPNE   R0,R14                  ;Or is it just coincidence?
499                 BNE     %10thread_suspend       ;No -- deal with that case
500
501                 ; --- We want to suspend the current thread ---
502                 ;
503                 ; To suspend, we need to save the current thread context,
504                 ; and resume from the system stack.  We know the thread must
505                 ; be active currently, otherwise we wouldn't be executing
506                 ; it!
507
508                 STMFD   R13!,{R0-R12}           ;Save entire context on stack
509                 STR     R13,[R14,#thr__stackPtr] ;Save the thread's stack ptr
510                 MOV     R0,#1                   ;Thread must be active
511                 STR     R0,[R14,#thr__suspend]  ;So store 1 as suspend count
512                 BL      thr__decCount           ;There's one less active now
513
514                 ; --- Now restore context to dispatcher ---
515
516                 BL      thr__end                ;Stop all the handlers
517                 MOV     R0,#0                   ;There is no current thread
518                 STR     R0,wsp__current         ;So clear current pointer
519                 LDR     R13,wsp__stackPtr       ;Get the system stack pointer
520                 LDMFD   R13!,{R0-R12,PC}^       ;Restore context again
521
522                 ; --- We're just meant to suspend any old thread ---
523                 ;
524                 ; We just need to bump its counter -- this is easy ---
525
526 10              LDR     R14,[R0,#thr__suspend]  ;Get current suspend count
527                 ADD     R14,R14,#1              ;Bump it up one
528                 STR     R14,[R0,#thr__suspend]  ;Store it back again
529                 CMP     R14,#1                  ;Was it previously active?
530                 BLEQ    thr__decCount           ;Yes -- decrement actives
531                 LDMFD   R13!,{R12,PC}^          ;Return to caller happy
532
533                 LTORG
534
535 ; --- thread_resume ---
536 ;
537 ; On entry:     R0 == thread handle
538 ;
539 ; On exit:      --
540 ;
541 ; Use:          Allows a suspended thread to continue operations.  If you
542 ;               resume a thread more times than it has been suspended,
543 ;               any excess resumes are ignored.  You can't resume a thread
544 ;               to stop it being blocked by a semaphore.
545
546                 EXPORT  thread_resume
547 thread_resume   ROUT
548
549                 STMFD   R13!,{R10,R12,R14}      ;Save some registers
550                 WSPACE  thr__wSpace             ;Find my workspace address
551
552                 ; --- Make sure the thread isn't running now ---
553
554                 LDR     R14,[R0,#thr__suspend]  ;Get the suspension counter
555                 CMP     R14,#0                  ;Is it zero already?
556                 LDMEQFD R13!,{R10,R12,PC}^      ;Yes -- return right now
557
558                 ; --- Decrement the counter ---
559
560                 LDR     R10,[R0,#thr__semaphore] ;Get the blocking semaphore
561                 CMP     R10,#0                  ;Is there a blocking sem?
562                 MOVEQ   R10,#0                  ;No -- minimum count is 0
563                 MOVNE   R10,#1                  ;Yes -- minimum count is 1
564                 SUB     R14,R14,#1              ;Decrement the counter
565                 CMP     R14,R10                 ;Is it too low?
566                 MOVLT   R14,R10                 ;Yes -- bring it up again
567                 STR     R14,[R0,#thr__suspend]  ;Store the counter back again
568
569                 ; --- Bump the active count if thread now active ---
570
571                 CMP     R14,#0                  ;Is the thread active now?
572                 BLEQ    thr__incCount           ;Yes -- increment the count
573                 CMP     R14,#1                  ;Is the thread almost active?
574                 LDMNEFD R13!,{R10,R12,PC}^      ;No -- return right now
575
576                 ; --- Check if the thread can start from a semaphore ---
577
578                 STMFD   R13!,{R0-R2}            ;Save some registers
579                 MOV     R10,R0                  ;Look after the thread handle
580                 LDR     R14,[R10,#thr__semaphore] ;Is it waiting for a sem?
581                 CMP     R14,#0                  ;Is the pointer null?
582                 BEQ     %20thread_resume        ;Yes -- skip to end
583
584                 LDR     R0,[R14,#sem__counter]  ;Is it signalled enough?
585                 CMP     R0,#0                   ;If so, it isn't 0
586                 BEQ     %20thread_resume        ;If not, skip to the end
587
588                 ; --- Go through the waiting list ---
589                 ;
590                 ; We want to find the entry in the list, but we also want
591                 ; to avoid `jumping the queue' -- i.e. getting the semaphore
592                 ; before an unsuspended thread.  With the algorithms used
593                 ; in thread_signal, I don't think this can happen, but it's
594                 ; as well to make sure.  We have to go through the list
595                 ; anyway.
596
597                 LDR     R0,[R14,#sem__blocked]  ;Find the blocked list
598                 MOV     R2,#0                   ;No previous block found
599 10              CMP     R0,#0                   ;Is it the end of the list?
600                 BEQ     %20thread_resume        ;Yes -- it's all over then
601                 LDR     R1,[R0,#sml__thread]    ;Get the thread's handle
602                 CMP     R1,R10                  ;Do they match up?
603                 BEQ     %11thread_resume        ;Yes -- skip onwards
604                 LDR     R1,[R1,#thr__suspend]   ;Get the suspended count
605                 CMP     R1,#0                   ;Is this thread active?
606                 BEQ     %20thread_resume        ;Yes -- skip to end
607                 MOV     R2,R0                   ;Set up the previous pointer
608                 LDR     R0,[R0,#sml__next]      ;Get the next block
609                 B       %10thread_resume        ;And go back round the loop
610
611                 ; --- Remove it from the waiting list ---
612
613 11thread_resume LDR     R1,[R0,#sml__next]      ;Get the next list item
614                 CMP     R2,#0                   ;Is there a previous one?
615                 STRNE   R1,[R2,#sml__next]      ;Yes -- fill in its next ptr
616                 STREQ   R1,[R14,#sem__blocked]  ;No -- it's the new list head
617                 CMP     R1,#0                   ;Is there a next block?
618                 STREQ   R2,[R14,#sem__blockEnd] ;No -- fill in the last ptr
619
620                 MOV     R2,R14                  ;Keep the semaphore pointer
621                 MOV     R1,#sml__size           ;Size of the link blocks
622                 BL      sub_free                ;Destroy the link block
623
624                 ; --- Now decrement the semaphore counter ---
625
626                 LDR     R0,[R2,#sem__counter]   ;Get the current counter
627                 SUB     R0,R0,#1                ;Decrement it nicely
628                 STR     R0,[R2,#sem__counter]   ;Store it back again
629                 MOV     R0,#0                   ;Thread not suspended at all
630                 STR     R0,[R10,#thr__suspend]  ;Store 0 as suspend count
631                 BL      thr__incCount           ;Increment active threads
632
633 20thread_resume LDMFD   R13!,{R0-R2,R10,R12,PC}^ ;Return to caller
634
635                 LTORG
636
637 ; --- thread_yield ---
638 ;
639 ; On entry:     --
640 ;
641 ; On exit:      --
642 ;
643 ; Use:          Pauses the thread for a while.  You only need to use this
644 ;               call if you have stopped the current thread from being
645 ;               timesliced.
646
647                 EXPORT  thread_yield
648 thread_yield    ROUT
649
650                 STMFD   R13!,{R0-R14}           ;Save the old context
651                 WSPACE  thr__wSpace             ;Find my workspace pointer
652                 LDR     R0,wsp__current         ;Get the current thread ptr
653                 BL      thr__end                ;Kill off the handlers
654                 STR     R13,[R0,#thr__stackPtr] ;Save the thread's stack ptr
655                 MOV     R0,#0                   ;Current thread is stopped
656                 STR     R0,wsp__current         ;So clear the pointer
657                 LDR     R13,wsp__stackPtr       ;Get the system stack pointer
658                 LDMFD   R13!,{R0-R12,PC}^       ;Restore main system context
659
660                 LTORG
661
662 ; --- thread_createSem ---
663 ;
664 ; On entry:     R0 == initial value for semaphore (0 for counter, 1 for
665 ;                     mutex)
666 ;
667 ; On exit:      R0 == semaphore handle and V clear if all went well
668 ;               R0 == pointer to error and V set if something went wrong
669 ;
670 ; Use:          Creates a semaphore with the given initial counter value.
671 ;
672 ;               The semaphore can be used to provide serialised access to
673 ;               a resource by initialising its value to 1 and performing the
674 ;               following:
675 ;
676 ;               thread_wait(mySemaphore)
677 ;               //
678 ;               // Do things with the resource
679 ;               //
680 ;               thread_signal(mySemaphore)
681 ;
682 ;               Or you can inform a thread that it has items in its input
683 ;               queue by having the following in the thread code:
684 ;
685 ;               while true
686 ;                 thread_wait(theSemaphore)
687 ;                 getFromQueue(myQueue,item)
688 ;                 process(item)
689 ;               endWhile
690 ;
691 ;               and when inserting queue items:
692 ;
693 ;               addToQueue(item)
694 ;               thread_signal(theSemaphore)
695 ;
696 ;               It is distinctly possible that input queue management will
697 ;               be introduced in a separate Sapphire module.
698
699                 EXPORT  thread_createSem
700 thread_createSem ROUT
701
702                 STMFD   R13!,{R0-R3,R14}        ;Save some registers
703                 MOV     R0,#sem__size           ;The size of a semaphore
704                 BL      sub_alloc               ;Try to create the semaphore
705                 BVS     %99thread_createSem     ;If it failed, tidy up
706
707                 LDR     R1,[R13],#4             ;Get the initial sem value
708                 MOV     R2,#0                   ;No threads waiting either
709                 MOV     R3,#0                   ;So no last link block
710                 STMIA   R0,{R1-R3}              ;Save in semaphore block
711                 LDMFD   R13!,{R1-R3,PC}         ;Return to caller
712
713                 ; --- Error occurred ---
714
715 99              ADD     R2,R0,#4                ;Point to the error text
716                 ADR     R0,thr__noSemCrt        ;Point to error block
717                 BL      msgs_error              ;Translate the error message
718                 ADD     R13,R13,#4              ;Don't restore R0 from stack
719                 LDMFD   R13!,{R1-R3,PC}         ;Return with V set on exit
720
721 thr__noSemCrt   DCD     1
722                 DCB     "thrNOSEMCRT",0
723
724                 LTORG
725
726 ; --- thread_destroySem ---
727 ;
728 ; On entry:     R0 == semaphore handle
729 ;
730 ; On exit:      --
731 ;
732 ; Use:          Destroys a semaphore when it's no use any more.  If threads
733 ;               are waiting for it, an error is generated.
734
735                 EXPORT  thread_destroySem
736 thread_destroySem ROUT
737
738                 STMFD   R13!,{R1,R14}           ;Save some registers away
739                 LDR     R14,[R0,#sem__blocked]  ;Get the waiting list head
740                 CMPEQ   R14,#0                  ;Is there a waiting list?
741                 BNE     %90thread_destroySem    ;If so, complain
742
743                 MOV     R1,#sem__size           ;The size of a semaphore
744                 BL      sub_free                ;Free up the block
745                 LDMFD   R13!,{R1,PC}^           ;Return to caller
746
747                 ; --- The semaphore is still in use ---
748
749 90              ADR     R0,thr__semInUse        ;Point to error message
750                 BL      msgs_error              ;Translate the message
751                 SWI     OS_GenerateError        ;It's a serious problem
752
753 thr__semInUse   DCD     1
754                 DCB     "thrSEMINUSE",0
755
756                 LTORG
757
758 ; --- thread_threaded ---
759 ;
760 ; On entry:     --
761 ;
762 ; On exit:      CS if currently running a thread, CC otherwise
763 ;
764 ; Use:          Informs the caller whether a thread is currently executing.
765
766                 EXPORT  thread_threaded
767 thread_threaded ROUT
768
769                 STMFD   R13!,{R12}              ;Save a register
770                 WSPACE  thr__wSpace             ;Load my workspace address
771                 LDR     R12,wsp__current        ;Load current thread handle
772                 CMP     R12,#0                  ;Is there a thread running?
773                 LDMFD   R13!,{R12}              ;Restore the register
774                 ORRNES  PC,R14,#C_flag          ;Return CS if thread running
775                 BICEQS  PC,R14,#C_flag          ;Otherwise return CC
776
777                 LTORG
778
779 ; --- thread_wait ---
780 ;
781 ; On entry:     R0 == semaphore handle
782 ;
783 ; On exit:      If successful, R0 preserved and V clear.
784 ;               If failed, R0 == pointer to error block and V set
785 ;
786 ; Use:          Waits on a sempahore.  The algorithm actually is as follows:
787 ;
788 ;               if semaphore.counter == 0 then
789 ;                 addToWaitingList(semaphore,currentThread)
790 ;                 suspend(currentThread)
791 ;               else
792 ;                 semaphore.counter -= 1
793 ;               endIf
794 ;
795 ;               See thread_createSem for suggestions on how to make use of
796 ;               semaphores.
797
798                 EXPORT  thread_wait
799 thread_wait     ROUT
800
801                 BIC     R14,R14,#V_flag         ;Assume that all is well
802                 STMFD   R13!,{R10,R12-R14}      ;Save some registers
803                 WSPACE  thr__wSpace             ;Locate my workspace
804                 LDR     R10,wsp__current        ;Find the current thread
805                 CMP     R10,#0                  ;Is it nonexistant?
806                 BEQ     %99thread_wait          ;Yes -- this is an error
807
808                 ; --- Do the messing about with the counter ---
809
810                 LDR     R14,[R0,#sem__counter]  ;Get the semaphore counter
811                 SUBS    R14,R14,#1              ;Decrement the counter
812                 STRGE   R14,[R0,#sem__counter]  ;If it was nonzero, store
813                 LDMEQFD R13!,{R10,R12,R14,PC}^  ;And return to caller
814
815                 ; --- Add the thread to the waiting list ---
816
817                 STMFD   R13!,{R0-R9}            ;Save rest of the context
818                 MOV     R9,R0                   ;Keep the semaphore handle
819                 MOV     R0,#sml__size           ;Size of a link block
820                 BL      sub_alloc               ;Allocate a link block
821                 BVS     %90thread_wait          ;If it failed, return error
822
823                 MOV     R1,#0                   ;No next waiting thread
824                 STMIA   R0,{R1,R10}             ;Save information in block
825                 LDR     R1,[R9,#sem__blockEnd]  ;Get the last waiting block
826                 CMP     R1,#0                   ;Is there one at all?
827                 STRNE   R0,[R1,#sml__next]      ;Yes -- store in its next
828                 STREQ   R0,[R9,#sem__blocked]   ;No -- it's the first one
829                 STR     R0,[R9,#sem__blockEnd]  ;It's certainly the last one
830
831                 ; --- Now suspend the thread for a while ---
832
833                 MOV     R1,#1                   ;Thread is currently active
834                 STR     R1,[R10,#thr__suspend]  ;Not any more it isn't
835                 STR     R9,[R10,#thr__semaphore] ;Remember the blocking sem
836
837                 ; --- Now switch back to main context ---
838
839                 BL      thr__end                ;Remove all the handlers
840                 STR     R13,[R10,#thr__stackPtr] ;Save the current stackptr
841                 LDR     R13,wsp__stackPtr       ;Find the system stack ptr
842                 LDMFD   R13!,{R0-R12,PC}^       ;And return to normality
843
844                 ; --- An error occurred while suspending ---
845
846 90thread_wait   ADD     R2,R0,#4                ;Point to the error text
847                 ADR     R0,thr__noWaitSem       ;Point to the error message
848                 BL      msgs_error              ;Translate the error
849                 ADD     R13,R13,#4              ;Don't restore R0 on exit
850                 LDMFD   R13!,{R1-R12,R14}       ;Restore all the registers
851                 ORRS    PC,R14,#V_flag          ;Return the error back
852
853 thr__noWaitSem  DCD     1
854                 DCB     "thrNOWAITSEM",0
855
856                 ; --- The caller isn't a thread ---
857
858 99thread_wait   ADR     R0,thr__notAThrd        ;Point to the error
859                 BL      msgs_error              ;Translate the message
860                 SWI     OS_GenerateError        ;And generate the error
861
862 thr__notAThrd   DCD     1
863                 DCB     "thrNOTATHRD",0
864
865                 LTORG
866
867 ; --- thread_signal ---
868 ;
869 ; On entry:     R0 == semaphore handle
870 ;
871 ; On exit:      --
872 ;
873 ; Use:          Increments a semaphore's counter if no threads are waiting
874 ;               for it, or releases a thread waiting for the semaphore.
875 ;
876 ;               The actual algorithm is shown below:
877 ;
878 ;               if semaphore.waitingList != 0 then
879 ;                 thread = removeFromWaitingList(semaphore)
880 ;                 unSuspend(thread)
881 ;               else
882 ;                 semaphore.counter += 1;
883 ;               endif
884 ;
885 ;               See thread_createSem for suggestions on how to make use of
886 ;               semaphores.
887
888                 EXPORT  thread_signal
889 thread_signal   ROUT
890
891                 STMFD   R13!,{R0-R3,R10,R12,R14} ;Save a load of registers
892                 WSPACE  thr__wSpace             ;Locate my workspace
893
894                 ; --- Find a thread to restore control to ---
895
896                 MOV     R3,R0                   ;Look after the sem handle
897                 MOV     R2,#0                   ;No previous block yet
898                 LDR     R0,[R3,#sem__blocked]   ;Find blocked list head
899
900 10thread_signal CMP     R0,#0                   ;Is this the end?
901                 BEQ     %30thread_signal        ;Yes -- increment counter
902                 LDR     R10,[R0,#sml__thread]   ;Get the thread handle out
903                 LDR     R14,[R10,#thr__suspend] ;Find its suspended count
904                 CMP     R14,#1                  ;Is it only blocked by sem?
905                 MOVGT   R2,R0                   ;No -- this is now prev
906                 LDRGT   R0,[R0,#sml__next]      ;Find the next one
907                 BGT     %10thread_signal        ;And check that one out
908
909                 ; --- Found a suitable thread ---
910
911                 LDR     R14,[R0,#sml__next]     ;Get the next pointer out
912                 CMP     R2,#0                   ;Is there a previous block?
913                 STRNE   R14,[R2,#sml__next]     ;Yes -- fix its next ptr
914                 STREQ   R14,[R3,#sem__blocked]  ;No -- this is now first one
915                 CMP     R14,#0                  ;Is there a next block?
916                 STREQ   R2,[R3,#sem__blockEnd]  ;Yes -- previous is now last
917
918                 MOV     R1,#sml__size           ;The size of a link block
919                 BL      sub_free                ;Don't need it any more
920
921                 ; --- Let the new thread go ---
922
923                 MOV     R0,#0                   ;Thread is no longer blocked
924                 STR     R0,[R10,#thr__suspend]  ;Unblock the thread nicely
925                 STR     R0,[R10,#thr__semaphore] ;No blocking semaphore now
926                 LDMFD   R13!,{R0-R3,R10,R12,PC}^ ;Return to caller happily
927
928                 ; --- No threads found -- increment the counter ---
929
930 30thread_signal LDR     R14,[R3,#sem__counter]  ;Get the current counter
931                 ADD     R14,R14,#1              ;Increment it a little bit
932                 STR     R14,[R3,#sem__counter]  ;Store new counter back
933                 LDMFD   R13!,{R0-R3,R10,R12,PC}^ ;Return to caller happily
934
935                 LTORG
936
937 ; --- thread_init ---
938 ;
939 ; On entry:     --
940 ;
941 ; On exit:      --
942 ;
943 ; Use:          Initialises the thread system for use.
944
945                 EXPORT  thread_init
946 thread_init     ROUT
947
948                 STMFD   R13!,{R12,R14}          ;Save some registers
949
950                 ; --- Make sure we're not initialised ---
951
952                 WSPACE  thr__wSpace             ;Find my workspace pointer
953                 LDR     R14,wsp__flags          ;Load my flags word
954                 TST     R14,#thrFlag__inited    ;Am I initialised yet?
955                 LDMNEFD R13!,{R12,PC}^          ;Yes -- return right now
956                 ORR     R14,R14,#thrFlag__inited ;Set the flag
957                 STR     R14,wsp__flags          ;Save the flags word back
958
959                 ; --- Initialise the rest of the workspace ---
960
961                 STMFD   R13!,{R0-R4}            ;Save some more registers
962                 MOV     R0,#0                   ;No threads registered yet
963                 MOV     R1,#0                   ;No active threads, then
964                 MOV     R2,#0                   ;No thread running now
965                 MOV     R3,#0                   ;No system stack pointer
966                 STMIB   R12,{R0-R3,R11}         ;Save them in the workspace
967
968                 ; --- Start up other things we need ---
969
970                 BL      alloc_init              ;We need redirectble alloc
971                 BL      sub_init                ;And allocating small blocks
972
973                 LDMFD   R13!,{R0-R4,R12,PC}^    ;Return to caller
974
975                 LTORG
976
977 thr__wSpace     DCD     0
978
979 ;----- Handling pre-emption of threads --------------------------------------
980
981 ; --- thr__start ---
982 ;
983 ; On entry:     R0 == timeslice size in centiseconds
984 ;
985 ; On exit:      --
986 ;
987 ; Use:          Sets up handlers and a timer interrupt for starting a
988 ;               thread.
989
990 thr__start      ROUT
991
992                 STMFD   R13!,{R0-R4,R14}        ;Save some registers away
993                 ADR     R4,wsp__handlers        ;Point to handlers save area
994
995                 ; --- Set up the handlers properly ---
996
997                 MOV     R0,#7                   ;Install CallBack handler
998                 ADR     R1,thr__callBack        ;Point to handler routine
999                 MOV     R2,R12                  ;Point to my workspace
1000                 ADR     R3,wsp__regBuff         ;Point to the register block
1001                 SWI     OS_ChangeEnvironment    ;Install the handler
1002                 STMIA   R4!,{R1-R3}             ;Save the old handler
1003
1004                 MOV     R0,#10                  ;Install event handler
1005                 ADR     R1,thr__events          ;Point to handler routine
1006                 MOV     R2,R12                  ;Point to my workspace
1007                 SWI     OS_ChangeEnvironment    ;Install the handler
1008                 STMIA   R4!,{R1-R3}             ;Save the old handler
1009
1010                 MOV     R0,#6                   ;Install error handler
1011                 ADR     R1,thr__errors          ;Point to handler routine
1012                 MOV     R2,R12                  ;Point to my workspace
1013                 MOV     R3,R11                  ;Use Scratchpad for error
1014                 SWI     OS_ChangeEnvironment    ;Install the handler
1015                 STMIA   R4!,{R1-R3}             ;Save the old handler
1016
1017                 MOV     R0,#11                  ;Install exit handler
1018                 ADR     R1,thr__exit            ;Point to handler routine
1019                 MOV     R2,R12                  ;Point to my workspace
1020                 SWI     OS_ChangeEnvironment    ;Install the handler
1021                 STMIA   R4!,{R1-R3}             ;Save the old handler
1022
1023                 ; --- Enable my event ---
1024
1025                 MOV     R0,#14                  ;Enable an event
1026                 MOV     R1,#9                   ;I'll use the user event
1027                 SWI     OS_Byte                 ;Enable the event for me
1028
1029                 ; --- Set up the timer for me ---
1030
1031                 LDR     R0,[R13,#0]             ;Load the timeslice value
1032                 CMP     R0,#0                   ;Do we set the timer?
1033                 ADRNE   R1,thr__timer           ;Point to timer routine
1034                 MOVNE   R2,R12                  ;Point to my workspace
1035                 SWINE   OS_CallAfter            ;Install the handler then
1036
1037                 LDMFD   R13!,{R0-R4,PC}^        ;Return to caller
1038
1039                 LTORG
1040
1041 ; --- thr__end ---
1042 ;
1043 ; On entry:     --
1044 ;
1045 ; On exit:      --
1046 ;
1047 ; Use:          Removes all the handlers set up when a thread starts
1048
1049 thr__end        ROUT
1050
1051                 STMFD   R13!,{R0-R4,R14}        ;Save some registers
1052
1053                 ; --- Restore handlers to their old values ---
1054
1055                 ADR     R4,wsp__handlers        ;Point to saved handlers
1056                 MOV     R0,#7                   ;Restore CallBack handler
1057                 LDMIA   R4!,{R1-R3}             ;Restore handler registers
1058                 SWI     OS_ChangeEnvironment    ;Put them all back then
1059                 MOV     R0,#10                  ;Restore Event handler
1060                 LDMIA   R4!,{R1-R3}             ;Restore handler registers
1061                 SWI     OS_ChangeEnvironment    ;Put them all back then
1062                 MOV     R0,#6                   ;Restore Error handler
1063                 LDMIA   R4!,{R1-R3}             ;Restore handler registers
1064                 SWI     OS_ChangeEnvironment    ;Put them all back then
1065                 MOV     R0,#11                  ;Restore Exit handler
1066                 LDMIA   R4!,{R1-R3}             ;Restore handler registers
1067                 SWI     OS_ChangeEnvironment    ;Put them all back then
1068
1069                 MOV     R0,#13                  ;Disable an event
1070                 MOV     R1,#9                   ;I'm using the user event
1071                 SWI     OS_Byte                 ;Disable it nicely
1072
1073                 ; --- Get rid of my ticker event ---
1074                 ;
1075                 ; We try and remove it -- if it was never there anyway,
1076                 ; or it's happened, we don't care.
1077
1078                 ADR     R0,thr__timer           ;Point to my timer handler
1079                 MOV     R1,R12                  ;Get my workspace pointer
1080                 SWI     XOS_RemoveTickerEvent   ;Remove the ticker event
1081
1082                 ; --- Kill off the `In error' flag ---
1083
1084                 LDR     R14,wsp__flags          ;Load my flags word
1085                 BIC     R14,R14,#thrFlag__inErr ;Clear the flag bit
1086                 STR     R14,wsp__flags          ;Store it back again then
1087
1088                 LDMFD   R13!,{R0-R4,PC}^        ;Return to caller
1089
1090                 LTORG
1091
1092 ; --- thr__errors ---
1093 ;
1094 ; On entry:     R0 == pointer to my workspace
1095 ;
1096 ; On exit:      Doesn't
1097 ;
1098 ; Use:          Handles an error in a thread.
1099
1100 thr__errors     ROUT
1101
1102                 SWI     OS_IntOff               ;Disable interrupts a while
1103                 MOV     R12,R0                  ;Point to my workspace
1104                 LDR     R11,wsp__R11            ;Load scratchpad address
1105                 LDR     R10,wsp__current        ;Find the current thread
1106                 MOV     R1,#1                   ;Enter a critical section
1107                 STR     R1,[R10,#thr__critCount] ;Save critical counter
1108                 SWI     OS_IntOn                ;Safe to interrupt again
1109                 ADD     R9,R10,#thr__errorHnd   ;Locate the error handler
1110                 LDR     R5,[R9],#4              ;Get the handler address
1111                 CMP     R5,#0                   ;Is there one set up?
1112                 BEQ     %50thr__errors          ;No -- skip round next bit
1113
1114                 ; --- Am I already doing this? ---
1115
1116                 LDR     R14,wsp__flags          ;Load my flags word
1117                 TST     R14,#thrFlag__inErr     ;Am I already going?
1118                 BNE     %50thr__errors          ;Yes -- skip round next bit
1119
1120                 ; --- Handle the error and find resume point ---
1121
1122                 ORR     R14,R14,#thrFlag__inErr ;Remember I'm doing this
1123                 STR     R14,wsp__flags          ;Save it back again
1124                 ADD     R0,R11,#4               ;Point to error block
1125                 LDMIA   R9,{R1,R13}             ;Get registers to pass out
1126                 STMFD   R13!,{R12}              ;Save the old R12 away
1127                 MOV     R12,R1                  ;Pass its workspace pointer
1128                 MOV     R14,PC                  ;Set up return address
1129                 MOV     PC,R5                   ;Call the handler
1130
1131                 ; --- Call the resume point ---
1132
1133                 LDMFD   R13!,{R12}              ;Restore my workspace
1134                 LDR     R14,wsp__flags          ;Find my flags again
1135                 BIC     R14,R14,#thrFlag__inErr ;We're coming out again
1136                 STR     R14,wsp__flags          ;Store the flags back nicely
1137                 BL      thread_leaveCrit        ;Leave the critical section
1138                 MOV     R12,R1                  ;Set up resumer's workspace
1139                 MOV     PC,R0                   ;Call the resumer
1140
1141                 ; --- It all went wrong ---
1142
1143 50thr__errors   LDR     R13,wsp__stackPtr       ;Get the system stack pointer
1144                 MOV     R0,#0                   ;No current thread
1145                 STR     R0,wsp__current         ;So clear the pointer
1146                 BL      thr__end                ;Remove all its handlers
1147                 MOV     R0,R10                  ;Point to the thread
1148                 BL      thread_destroy          ;Kill off the thread
1149                 ADD     R0,R11,#4               ;Point to error block
1150                 SWI     OS_GenerateError        ;Pass to main handler
1151
1152                 LTORG
1153
1154 ; --- thr__exit ---
1155 ;
1156 ; On entry:     R12 == my workspace address
1157 ;
1158 ; On exit:      --
1159 ;
1160 ; Use:          Handles OS_Exit calls in a thread.  Closes down the thread
1161 ;               and propagates the exit
1162
1163 thr__exit       ROUT
1164
1165                 LDR     R11,wsp__R11            ;Load scratchpad pointer
1166                 SWI     OS_IntOff               ;Disable interrupts off
1167                 LDR     R13,wsp__stackPtr       ;Get the system stack pointer
1168                 BL      thr__end                ;Remove all its handlers
1169                 SWI     OS_IntOn                ;Save to interrupt again
1170                 LDR     R0,wsp__current         ;Get the current thread
1171                 MOV     R1,#0                   ;No current thread
1172                 STR     R1,wsp__current         ;So clear the pointer
1173                 BL      thread_destroy          ;Destroy the thread
1174                 SWI     OS_Exit
1175
1176                 LTORG
1177
1178 ; --- thr__callBack ---
1179 ;
1180 ; On entry:     R12 == pointer to my workspace
1181 ;
1182 ; On exit:      --
1183 ;
1184 ; Use:          Performs appropriate operations on receipt of a timer
1185 ;               interrupt.  If the thread is in a critical section, it is
1186 ;               marked `should be stopped'.  Otherwise, the context is
1187 ;               saved on the thread's stack and control returned to the
1188 ;               main program
1189
1190 thr__callBack   ROUT
1191
1192                 LDR     R10,wsp__current        ;Get the current thread
1193                 LDR     R14,[R10,#thr__critCount] ;Is it in a critical bit?
1194                 CMP     R14,#0                  ;If so, this is <>0
1195                 BEQ     %10thr__callBack        ;Otherwise skip forwards
1196
1197                 ; --- Set the thread's flag and continue ---
1198
1199                 LDR     R14,[R10,#thr__flags]   ;Get the thread's flags word
1200                 ORR     R14,R14,#tFlag__timeUp  ;Kill it on exit from crit
1201                 STR     R14,[R10,#thr__flags]   ;Store flags word back again
1202                 ADR     R14,wsp__regBuff        ;Point to save buffer
1203                 LDMIA   R14,{R0-R14}^           ;Get the user registers out
1204                 MOV     R0,R0                   ;Otherwise ARM complains
1205                 LDR     R14,[R14,#15*4]         ;Load the saved PC
1206                 MOVS    PC,R14                  ;Return to the thread
1207
1208                 ; --- Save the thread's context ---
1209
1210 10thr__callBack ADR     R14,wsp__regBuff        ;Point to save buffer
1211                 LDR     R11,[R14,#13*4]         ;Find the thread's stack ptr
1212                 SUB     R11,R11,#15*4           ;Make way for the context
1213                 STR     R11,[R10,#thr__stackPtr] ;Save the thread's stack ptr
1214                 LDMIA   R14!,{R0-R7}
1215                 STMIA   R11!,{R0-R7}
1216                 LDMIA   R14!,{R0-R7}
1217                 STMIA   R11!,{R0-R4,R6,R7}      ;Don't transfer R13
1218
1219                 ; --- Go back to the main program ---
1220
1221                 LDR     R11,wsp__stackPtr       ;Find the system stack ptr
1222                 LDR     R14,[R11,#13*4]         ;Get the saved link register
1223                 TEQP    R14,#0                  ;Set this as the current mode
1224                 MOV     R0,R0                   ;Stop ARM from being odd
1225                 MOV     R13,R11                 ;Point to the stack properly
1226                 BL      thr__end                ;Stop all the handlers
1227                 LDMFD   R13!,{R0-R12,PC}^       ;Rejoin the main thread
1228
1229                 LTORG
1230
1231 ; --- thr__events ---
1232 ;
1233 ; On entry:     R0 == event code (ideally this is 9)
1234 ;               R1 == my magic identifier number (just this thing, y'know)
1235 ;
1236 ; On exit:      R12 may contain 1
1237 ;
1238 ; Use:          Handles events.  If it's the magic timer event, then I get
1239 ;               myself a CallBack.
1240
1241 thr__events     ROUT
1242
1243                 CMP     R0,#9                   ;Is it a user event?
1244                 MOVNES  PC,R14                  ;No -- return right away
1245                 STMFD   R13!,{R14}              ;Save the link away
1246                 LDR     R14,thr__myMagic        ;Get my magic number
1247                 CMP     R1,R14                  ;Does it match up?
1248                 MOVEQ   R12,#1                  ;Yes -- request a CallBack
1249                 LDMFD   R13!,{PC}^              ;Return to caller
1250
1251                 LTORG
1252
1253 thr__myMagic    DCB     "MDW!"
1254
1255 ; --- thr__timer ---
1256 ;
1257 ; On entry:     --
1258 ;
1259 ; On exit:      --
1260 ;
1261 ; Use:          Generates an event that can eventually wend its way to my
1262 ;               CallBack handler
1263
1264 thr__timer      ROUT
1265
1266                 STMFD   R13!,{R0,R1,R14}        ;Save some registers
1267                 MOV     R0,#9                   ;Make a user event
1268                 LDR     R1,thr__myMagic         ;My special identifier
1269                 SWI     OS_GenerateEvent        ;Make the event happen
1270                 LDMFD   R13!,{R0,R1,PC}^        ;Return to caller
1271
1272                 LTORG
1273
1274 ; --- thread_enterCrit ---
1275 ;
1276 ; On entry:     --
1277 ;
1278 ; On exit:      --
1279 ;
1280 ; Use:          Declares that the current thread is about to enter a
1281 ;               critical section and must not be interrupted.
1282
1283                 EXPORT  thread_enterCrit
1284 thread_enterCrit ROUT
1285
1286                 STMFD   R13!,{R10,R12,R14}      ;Save some registers away
1287                 WSPACE  thr__wSpace             ;Find my workspace
1288                 LDR     R10,wsp__current        ;Get the current thread
1289                 CMP     R10,#0                  ;Is there one?
1290                 LDMEQFD R13!,{R10,R12,PC}^      ;No -- this is a no-op then
1291                 LDR     R14,[R10,#thr__critCount] ;Get the current counter
1292                 ADD     R14,R14,#1              ;Bump it along one
1293                 STR     R14,[R10,#thr__critCount] ;Save the counter back
1294                 LDMFD   R13!,{R10,R12,PC}^      ;Return to caller
1295
1296                 LTORG
1297
1298 ; --- thread_leaveCrit ---
1299 ;
1300 ; On entry:     --
1301 ;
1302 ; On exit:      --
1303 ;
1304 ; Use:          Declares that the current thread has left the critical
1305 ;               section and can be interrupted again.
1306
1307                 EXPORT  thread_leaveCrit
1308 thread_leaveCrit ROUT
1309
1310                 STMFD   R13!,{R10,R12-R14}      ;Save some registers away
1311                 WSPACE  thr__wSpace             ;Find my workspace
1312                 LDR     R10,wsp__current        ;Get the current thread
1313                 CMP     R10,#0                  ;Is there one?
1314                 LDMEQFD R13!,{R10,R12,R14,PC}^  ;No -- this is a no-op then
1315                 LDR     R14,[R10,#thr__critCount] ;Get the current counter
1316                 SUBS    R14,R14,#1              ;Chop one off it
1317                 MOVLT   R14,#0                  ;Stop it going negative
1318                 STR     R14,[R10,#thr__critCount] ;Save the counter back
1319                 LDMLEFD R13!,{R10,R12,R14,PC}^  ;If still critical, leave it
1320
1321                 ; --- Check to see if it should stop now ---
1322
1323                 LDR     R14,[R10,#thr__flags]   ;Get the thread's flags
1324                 TST     R14,#tFlag__timeUp      ;Has the timer happened?
1325                 LDMEQFD R13!,{R10,R12,R14,PC}^  ;No -- leave it to go then
1326                 BL      thr__end                ;Stop the handlers and things
1327                 STMFD   R13!,{R0-R9}            ;Save rest of the context
1328                 STR     R13,[R10,#thr__stackPtr] ;Save the stack pointer
1329                 LDR     R13,wsp__stackPtr       ;Load the system stack ptr
1330                 STMFD   R13!,{R0-R12,PC}^       ;Return to main thread
1331
1332                 LTORG
1333
1334 ; --- thread_errorHandler ---
1335 ;
1336 ; On entry:     R0 == pointer to routine to call
1337 ;               R1 == R12 value to call with
1338 ;               R2 == R13 value to call with
1339 ;
1340 ; On exit:      --
1341 ;
1342 ; Use:          Sets up the error handler for a thread.
1343
1344                 EXPORT  thread_errorHandler
1345 thread_errorHandler
1346                 STMFD   R13!,{R12,R14}          ;Save some registers
1347                 WSPACE  thr__wSpace             ;Locate my workspace
1348                 LDR     R14,wsp__current        ;Get the current thread
1349                 ADD     R14,R14,#thr__errorHnd  ;Point to handler block
1350                 STMIA   R14,{R0-R2}             ;Save the information away
1351                 LDMFD   R13!,{R12,PC}^          ;Return to caller
1352
1353                 LTORG
1354
1355 ;----- Data structures ------------------------------------------------------
1356
1357 ; --- The thread structure ---
1358
1359                 ^       0
1360 thr__next       #       4                       ;Pointer to next thread
1361 thr__prev       #       4                       ;Pointer to previous thread
1362 thr__suspend    #       4                       ;Thread's suspension counter
1363 thr__semaphore  #       4                       ;Pointer to blocking sem
1364 thr__priority   #       4                       ;Priority of this thread
1365 thr__timeSlice  #       4                       ;Size of timeslice for thread
1366 thr__critCount  #       4                       ;Critical section counter
1367 thr__stackPtr   #       4                       ;Thread's stack pointer
1368 thr__stack      #       4                       ;Pointer to thread's stack
1369 thr__flags      #       4                       ;Various flags for the thread
1370 thr__errorHnd   #       4*3                     ;The error handler for it
1371 thr__size       #       0                       ;Size of this block
1372
1373 tFlag__timeUp   EQU     (1<<0)                  ;Pre-empt after critical sect
1374
1375 ; --- The semaphore link structure ---
1376
1377                 ^       0
1378 sml__next       #       4                       ;Pointer to next link block
1379 sml__thread     #       4                       ;Pointer to suspended thread
1380 sml__size       #       0                       ;Size of this block
1381
1382 ; --- The main semaphore block ---
1383
1384                 ^       0
1385 sem__counter    #       4                       ;Semaphore counter
1386 sem__blocked    #       4                       ;List of blocked threads
1387 sem__blockEnd   #       4                       ;Pointer to last link in list
1388 sem__size       #       0                       ;Size of this block
1389
1390 ;----- Workspace ------------------------------------------------------------
1391
1392                 ^       0,R12
1393 thr__wStart     #       0
1394
1395 wsp__flags      #       4                       ;Various useful flags
1396 wsp__threads    #       4                       ;Pointer to list of threads
1397 wsp__active     #       4                       ;Counter of active threads
1398 wsp__current    #       4                       ;Pointer to current thread
1399 wsp__stackPtr   #       4                       ;The system stack pointer
1400 wsp__R11        #       4                       ;Sapphire's R11 pointer
1401 wsp__handlers   #       12*4                    ;Old handlers to restore
1402 wsp__regBuff    #       16*4                    ;Register save buffer
1403
1404 thr__wSize      EQU     {VAR}-thr__wStart
1405
1406 thrFlag__inited EQU     (1<<0)                  ;Are we initialised yet?
1407 thrFlag__inErr  EQU     (1<<1)                  ;We are handling an error
1408
1409                 AREA    |Sapphire$$LibData|,CODE,READONLY
1410
1411                 DCD     thr__wSize
1412                 DCD     thr__wSpace
1413                 DCD     256
1414                 DCD     thread_init
1415
1416 ;----- That's all, folks ----------------------------------------------------
1417
1418                 END