optimized coroutines library

This commit is contained in:
Irmen de Jong
2025-06-04 20:53:21 +02:00
parent d067fa4b73
commit ddceec364e
3 changed files with 38 additions and 37 deletions

View File

@@ -28,6 +28,8 @@
; that routine can for instance call current() (or just look at the active_task variable) to get the id of the next task to execute.
; It has then to return a boolean: true=next task is to be executed, false=skip the task this time.
; - in tasks: call yield() to pass control to the next task. Use the returned userdata value to do different things.
; For now, you MUST call yield() only from the actual subroutine that has been registered as a task!
; (this is because otherwise the cpu call stack gets messed up and an RTS in task1 could suddenly pop a return address belonging to another tasks' call frame)
; - call current() to get the current task id.
; - call kill(taskid) to kill a task by id.
; - call killall() to kill all tasks.
@@ -41,19 +43,17 @@ coroutines {
const ubyte MAX_TASKS = 64
uword[MAX_TASKS] tasklist
uword[MAX_TASKS] userdatas
uword[MAX_TASKS] returnaddresses
ubyte active_task
uword supervisor
sub add(uword taskaddress, uword userdata) -> ubyte {
sub add(uword @nozp taskaddress, uword @nozp userdata) -> ubyte {
; find the next empty slot in the tasklist and stick it there
; returns the task id of the new task, or 255 if there was no space for more tasks. 0 is a valid task id!
; also returns the success in the Carry flag (carry set=success, carry clear = task was not added)
for cx16.r0L in 0 to len(tasklist)-1 {
if tasklist[cx16.r0L] == 0 {
tasklist[cx16.r0L] = taskaddress
tasklist[cx16.r0L] = sys.get_as_returnaddress(taskaddress)
userdatas[cx16.r0L] = userdata
returnaddresses[cx16.r0L] = 0
sys.set_carry()
return cx16.r0L
}
@@ -70,57 +70,48 @@ coroutines {
}
}
sub run(uword supervisor_routine) {
sub run(uword @nozp supervisor_routine) {
supervisor = supervisor_routine
for active_task in 0 to len(tasklist)-1 {
if tasklist[active_task]!=0 {
; activate the termination handler and start the first task
; note: cannot use pushw() because JSR doesn't push the return address in the same way
sys.push_returnaddress(&termination)
goto tasklist[active_task]
sys.pushw(tasklist[active_task])
return
}
}
}
sub yield() -> uword {
; Store the return address of the yielding task,
; and continue with the next one instead (round-robin)
; Returns the associated userdata value
uword task_start, task_continue
returnaddresses[active_task] = sys.popw()
; Store the return address of the yielding task, and continue with the next one instead (round-robin)
; Returns the associated userdata value.
; NOTE: CAN ONLY BE CALLED FROM THE SCOPE OF THE SUBROUTINE THAT HAS BEEN REGISTERED AS THE TASK!
uword task_return_address
tasklist[active_task] = sys.popw()
resume_with_next_task:
skip_task:
if not next_task() {
void sys.popw() ; remove return to the termination handler
return 0 ; exiting here will now actually return from the start() call back to the calling program :)
return 0 ; exiting here will now actually return back to the calling program that called run()
}
if supervisor!=0 {
if supervisor!=0
if lsb(call(supervisor))==0
goto resume_with_next_task
}
goto skip_task
if task_continue==0 {
; fetch start address of next task.
; address on the stack must be pushed in reverse byte order
; also, subtract 1 from the start address because JSR pushes returnaddress minus 1
; note: cannot use pushw() because JSR doesn't push the return address in the same way
sys.push_returnaddress(task_start)
} else
sys.pushw(task_continue)
; returning from yield then continues with the next coroutine
; returning from yield then continues with the next coroutine:
sys.pushw(task_return_address)
return userdatas[active_task]
sub next_task() -> bool {
; search through the task list for the next active task
repeat len(tasklist) {
active_task++
if active_task==len(returnaddresses)
if active_task==len(tasklist)
active_task=0
task_start = tasklist[active_task]
if task_start!=0 {
task_continue = returnaddresses[active_task]
task_return_address = tasklist[active_task]
if task_return_address!=0 {
return true
}
}
@@ -128,9 +119,8 @@ resume_with_next_task:
}
}
sub kill(ubyte taskid) {
sub kill(ubyte @nozp taskid) {
tasklist[taskid] = 0
returnaddresses[taskid] = 0
}
sub current() -> ubyte {
@@ -138,12 +128,10 @@ resume_with_next_task:
}
sub termination() {
; a task has terminated. wipe it from the list.
; this is an internal routine
; internal routine: a task has terminated. wipe it from the list.
kill(active_task)
; reactivate this termination handler
; note: cannot use pushw() because JSR doesn't push the return address in the same way
; reactivate this termination handler and go to the next task
sys.push_returnaddress(&termination)
goto coroutines.yield.resume_with_next_task
goto coroutines.yield.skip_task
}
}