This document describes th and how to use it in an assembly-language program.
This is my latest attempt at cooperative multithreading for the Z80 processor. The goals of this version are:
Cooperative multithreading works best with I/O-bound threads. It can also work with compute-bound threads if they yield frequently enough to allow other threads to run.
With preemptive multithreading, a thread can run as long as it does not block and still has time left in its time-slice. With cooperative multithreading, a thread can run as long as it does not block. A cooperative thread, therefore, could run indefinitely.
Cooperative multithreading has both advantages and disadvantages over preemptive. Here are a few:
To use th, include the file "th.asm" at the entry point of your program—typically at address 0xD748 on the TI-86—and define a label named "main
" where the main program starts (like in a C program). Also, define THREAD_MAX
to the maximum number of threads that will run simultaneously in the program.
To create a thread, call th_create
with the address of the thread's code in HL
, its stack pointer in DE
, and its argument in BC
. If the call is successful, th_create
returns a pointer to the thread in HL
. Otherwise, it sets the carry flag and a thread is not created.
A thread's stack is a block of memory large enough for the deepest call sequence and variable usage within the thread; it must account for calls to TI-OS routines as well as interrupt handlers that might occur during a thread. This is generally 50 bytes or more. The stack pointer (as one of the arguments to th_create
) is the address above the top (highest address) of the stack. It is the top of the stack because the stack grows downward on the Z80 (as well as in most other processors). For example, if I have a stack at address mystack
and is MYSTACK_SIZE
bytes large, then the stack pointer is mystack
+MYSTACK_SIZE
. That is the value I would load into DE
before I call th_create
.
The new thread will be started with its argument at address SP
+2; that is, the argument is on the stack above the return address, and you can use the following code at the start of a thread's code to get its argument:
thread_code: POP BC ; get the return address POP HL ; get the argument PUSH HL ; put the argument back PUSH BC ; put the return address back ; more thread code... RET
HL
and call th_cancel
.HL
and call th_exit
. Alternatively, you can simply return from the thread (except from the "main" thread) and th_exit
will be called automagically. When the last thread exits, the program terminates.HL
and call th_join
. On success, it will return the thread's exit status. Otherwise, it sets the carry flag.HL
and call th_sleep
. The thread will run again when another thread wakes it up.HL
and call th_wake
. This will wake every thread that is sleeping on that event.th_yield
. This saves all registers, so it's safe to call at any time.exit
. All threads will terminate, and the program will exit immediately to the hosting environment (e.g., TI-OS). Note: returning from the main thread is equivalent to calling exit
. On the other hand, calling th_exit
from the main thread exits only that thread and allows other threads to run.You can find two example programs that use multithreading in the files testth.asm and produce.asm.
th_cancel
.send
and receive
routines use th_sleep
to block the thread when the pipe is full or empty, respectively. send
and receive
also use th_wake
to notify the other thread(s) that the pipe is not empty or not full, respectively. This demonstrates one of the applications of th_sleep
and th_wake
.Another application of th_sleep
and th_wake
is for implementing timers: a thread sleeps on a timer until it's expired, and a timer thread (or an interrupt handler) wakes threads when a timer expires.
While I have tested th for bugs and performance, there may be some bugs lurking in the code.
If you find a bug, try to make a small test case that triggers it (the smaller the test case, the better). Make sure it's actually a bug in the library and not in your own code—parallel computing can be tricky sometimes, after all. Then contact the author with the test case along with any details you know about the bug (such as symptoms). Better yet, fix the bug and send it back to the author.
You can contact the author of this library at abbrev@gmail.com.
Copyright © 2007 Christopher Williams
This program 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 of the License, or (at your option) any later version.
This program 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 this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA