; Copyright (c) 2012 Teresa Bradbury
; Execution starts here
; In this example there are 3 threads
; Thread 2 prints twice then kills itself (by returning)
; Thread 1 loops until the main thread kills it
0x0100:
call threadinit
load #thread1 R1
load #thread1stack R2
call threadcreate
store R1 thread1id
load #thread2 R1
load #thread2stack R2
call threadcreate
store R1 thread2id
mainloop:
load #mainstr R1
call printstring
call threadyield
;jump mainloop ; uncomment this for inf-loop
load #mainstr R1
call printstring
call threadyield
load #mainstr R1
call printstring
call threadyield
load #mainstr R1
call printstring
call threadyield
load thread1id R1
call threaddestroy
load #mainstr R1
call printstring
call threadyield
load #mainstr R1
call printstring
call threadselfdestruct
thread1id : block 1
thread2id : block 1
mainstr : block #"Main Thread\n"
thread1 :
load #thread1str R1
call printstring
call threadyield
jump thread1
;return
thread2 :
load #thread2str R1
call printstring
call threadyield
;jump thread2
load #thread2str R1
call printstring
call threadyield
return
thread1stack : block 8
thread2stack : block 8
thread1str : block #"Thread 1\n"
thread2str : block #"Thread 2\n"
; R1 : string pointer (changed)
printstring :
load R1 R2
jumpz R2 printstring_end
store R2 0xfff0
add R1 ONE R1
jump printstring
printstring_end :
return
; -------------------------------------
; --------- Concurrency Data ----------
; -------------------------------------
maxthreads : block #3
; For each thread, the first word is one if the thread exists
; and zero otherwise. The second word is the stack pointer.
threaddata : block 6
currentthreadid : block 1
; -------------------------------------
; ------- Concurrency Functions -------
; -------------------------------------
; must be called at start of the program
; it initializes the data structures
; No parameters
threadinit :
load maxthreads R0
threadinit_threaddatainit :
; R0 is the thread that is being initialised. It counts down.
add R0 MONE R0
; thread id
move R0 R1
; structure offset
move ZERO R2
; no flags
move ZERO R3
call threaddatastore
jumpnz R0 threadinit_threaddatainit
; set thread zero to active
move ZERO R1
move ZERO R2
move ONE R3
call threaddatastore
store ZERO currentthreadid
return
; R1 : in thread id
; R2 : in structure offset
; R1 : out data
; preserves R0
threaddataload :
; structure has size 2
load #2 R3
mult R1 R3 R3
add R3 R2 R3
load R3 #threaddata R1
return
; R1 : in thread id
; R2 : in structure offset
; R3 : in data
; preserves R0
threaddatastore :
; structure has size 2
load #2 R4
mult R1 R4 R4
add R4 R2 R4
store R3 #threaddata R4
return
; R1 : in function pointer
; R2 : in stack pointer
; R1 : out thread id (-1 for error)
; Stack needs to be big enough for thread's workings,
; plus at least 3
threadcreate :
push R1
push R2
; find a free thread id (in R0)
move ZERO R0
threadcreate_findid :
load maxthreads R1
sub R1 R0 R2
; if R2 == 0 then R0 == maxthreads
; so return -1 as no available threads
jumpnz R2 threadcreate_no_error
move MONE R1
return
threadcreate_no_error :
move R0 R1
move ZERO R2
call threaddataload
jumpz R1 threadcreate_foundid
add ONE R0 R0
jump threadcreate_findid
threadcreate_foundid :
; created thread is inactive (not current) and
; the stack contains a pointer to selfdestruct then
; the provided stack pointer so first it will
; return to the given function and if it returns
; again it will be destroyed
pop R2 ; stack pointer
pop R1 ; function pointer
store R1 #1 R2
load #threadselfdestruct R1
store R1 #0 R2
; Store the stack pointer
move R0 R1
add R2 ONE R3
move ONE R2
call threaddatastore
; Mark the thread as existant
move R0 R1
move ZERO R2
move ONE R3
call threaddatastore
; Return the created thread id
move R0 R1
return
; Pass thread id in R1
threaddestroy :
; If thread id is not current, we just mark nonexistant
; If thread id *is* current, we mark nonexistant and then call
; threadyield to switch to another thread
move R1 R0 ; because R0 survives the function calls
; Turn off the thread
move R0 R1
move ZERO R2
move ZERO R3
call threaddatastore
; Is this the current thread?
load currentthreadid R1
sub R0 R1 R2
jumpnz R2 threaddestroy_notcurrentthread
call threadyield
threaddestroy_notcurrentthread :
return
; No parameters - destroys current thread
; this is called on returning from a thread function
threadselfdestruct :
load currentthreadid R1
jump threaddestroy
; No parameters
threadyield :
; store the stackpointer
load currentthreadid R1
move ONE R2
move SP R3
call threaddatastore
; round robin scheduling
; we will eventually choose the thread id in R0
load currentthreadid R0
threadyield_findnext :
load maxthreads R1
add R0 ONE R0
sub R1 R0 R2
; if R2 == 0 then R0 == maxthreads
; so R0 := 0
jumpnz R2 threadyield_mod
move ZERO R0
threadyield_mod :
move R0 R1
move ZERO R2
call threaddataload
jumpnz R1 threadyield_foundnext
; if none of the threads exist, halt
load currentthreadid R4
sub R4 R0 R4
jumpnz R4 threadyield_findnext
halt
threadyield_foundnext :
store R0 currentthreadid
; load the new stack pointer
load currentthreadid R1
move ONE R2
call threaddataload
move R1 SP
return