mirror of
https://github.com/irmen/prog8.git
synced 2025-01-10 20:30:23 +00:00
added coroutines library and multitasking example. Added sys.push_returnaddress().
This commit is contained in:
parent
fe011de934
commit
529ea5bf58
@ -367,6 +367,20 @@ save_SCRATCH_ZPWORD2 .word 0
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push_returnaddress(uword address @XY) {
|
||||
%asm {{
|
||||
; push like JSR would: address-1, MSB first then LSB
|
||||
cpx #0
|
||||
bne +
|
||||
dey
|
||||
+ dex
|
||||
tya
|
||||
pha
|
||||
txa
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
|
@ -918,6 +918,20 @@ _no_msb_size
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push_returnaddress(uword address @XY) {
|
||||
%asm {{
|
||||
; push like JSR would: address-1, MSB first then LSB
|
||||
cpx #0
|
||||
bne +
|
||||
dey
|
||||
+ dex
|
||||
tya
|
||||
pha
|
||||
txa
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
|
@ -934,6 +934,20 @@ _no_msb_size
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push_returnaddress(uword address @XY) {
|
||||
%asm {{
|
||||
; push like JSR would: address-1, MSB first then LSB
|
||||
cpx #0
|
||||
bne +
|
||||
dey
|
||||
+ dex
|
||||
tya
|
||||
pha
|
||||
txa
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
|
134
compiler/res/prog8lib/coroutines.p8
Normal file
134
compiler/res/prog8lib/coroutines.p8
Normal file
@ -0,0 +1,134 @@
|
||||
; Cooperative multitasking / Coroutines
|
||||
; EXPERIMENTAL LIBRARY: Api may change or it may be removed completely in a future version!
|
||||
|
||||
; Achieves cooperative multitasking among a list of tasks each calling yield() to pass control to the next.
|
||||
; Uses cpu stack return address juggling to cycle between different tasks.
|
||||
;
|
||||
; Features:
|
||||
; - can have a dynamic number of tasks (max 64), when tasks end they're automaticall removed from the task list.
|
||||
; - you can add new tasks, even from IRQ handlers, while the rest is already running.
|
||||
; - tasks are regular subroutines but have to call yield() to pass control to the next task (round-robin)
|
||||
; - you can kill a task (if you know it's id...)
|
||||
; - when all tasks are finished the run() call will also return.
|
||||
; - tasks can't push anything on the cpu stack before calling yield() - that will cause chaos.
|
||||
;
|
||||
; Difference from IRQ handlers:
|
||||
; - you can have many tasks instead of only 2 (main program + irq handler)
|
||||
; - it's not tied to any IRQ setup, and will run as fast as the tasks themselves allow
|
||||
; - tasks fully control the switch to the next task; there is no preemptive switching
|
||||
;
|
||||
; TODO to make it actually even more useful, we probably have to:
|
||||
; - return a unique value (pointer that you had to provide when adding the task to the list?)
|
||||
; from yield() that the subroutine could use to access unique state,
|
||||
; because right now a single task == a single subroutine; right now you cannot re-use a subroutine to run
|
||||
; the same task multiple times for different things.
|
||||
;
|
||||
; USAGE:
|
||||
; - call add(taskaddress) to add a new task. It returns the task id.
|
||||
; - call run() to start executing all tasks until none are left.
|
||||
; - in tasks: call yield() to pass control to the next task.
|
||||
; - call kill(tasknumber) to kill a task by id.
|
||||
; - call killall() to kill all tasks.
|
||||
|
||||
coroutines {
|
||||
const ubyte MAX_TASKS = 64
|
||||
uword[MAX_TASKS] tasklist
|
||||
uword[MAX_TASKS] returnaddresses
|
||||
ubyte active_task
|
||||
|
||||
sub add(uword taskaddress) -> 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)
|
||||
sys.irqsafe_set_irqd()
|
||||
for cx16.r0L in 0 to len(tasklist)-1 {
|
||||
if tasklist[cx16.r0L] == 0 {
|
||||
tasklist[cx16.r0L] = taskaddress
|
||||
returnaddresses[cx16.r0L] = 0
|
||||
sys.irqsafe_clear_irqd()
|
||||
sys.set_carry()
|
||||
return cx16.r0L
|
||||
}
|
||||
}
|
||||
sys.irqsafe_clear_irqd()
|
||||
; no space for new task
|
||||
sys.clear_carry()
|
||||
return 255
|
||||
}
|
||||
|
||||
sub killall() {
|
||||
; kill all existing tasks
|
||||
sys.irqsafe_set_irqd()
|
||||
for cx16.r0L in 0 to len(tasklist)-1 {
|
||||
kill(cx16.r0L)
|
||||
}
|
||||
sys.irqsafe_clear_irqd()
|
||||
}
|
||||
|
||||
sub run() {
|
||||
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]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub yield() {
|
||||
; store the return address of the yielding task,
|
||||
; and continue with the next one instead (round-robin)
|
||||
uword task_start, task_continue
|
||||
returnaddresses[active_task] = sys.popw()
|
||||
|
||||
resume_with_next_task:
|
||||
if not next_task() {
|
||||
void sys.popw() ; remove return to the termination handler
|
||||
return ; exiting here will now actually return from the start() call back to the calling program :)
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
sub next_task() -> bool {
|
||||
; search through the task list for the next active task
|
||||
sys.irqsafe_set_irqd()
|
||||
repeat len(tasklist) {
|
||||
active_task++
|
||||
if active_task==len(returnaddresses)
|
||||
active_task=0
|
||||
task_start = tasklist[active_task]
|
||||
if task_start!=0 {
|
||||
task_continue = returnaddresses[active_task]
|
||||
sys.irqsafe_clear_irqd()
|
||||
return true
|
||||
}
|
||||
}
|
||||
sys.irqsafe_clear_irqd()
|
||||
return false ; no task
|
||||
}
|
||||
}
|
||||
|
||||
sub kill(ubyte tasknum) {
|
||||
tasklist[tasknum] = 0
|
||||
returnaddresses[tasknum] = 0
|
||||
}
|
||||
|
||||
sub termination() {
|
||||
; a task has terminated. wipe it from the list.
|
||||
; this is an internal routine
|
||||
kill(active_task)
|
||||
; reactivate this termination handler
|
||||
; note: cannot use pushw() because JSR doesn't push the return address in the same way
|
||||
sys.push_returnaddress(&termination)
|
||||
goto coroutines.yield.resume_with_next_task
|
||||
}
|
||||
}
|
@ -1848,6 +1848,18 @@ save_SCRATCH_ZPWORD2 .word 0
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push_returnaddress(uword address @XY) {
|
||||
%asm {{
|
||||
; push like JSR would: address-1, MSB first then LSB
|
||||
cpx #0
|
||||
bne +
|
||||
dey
|
||||
+ dex
|
||||
phy
|
||||
phx
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
|
@ -300,6 +300,20 @@ save_SCRATCH_ZPWORD2 .word 0
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push_returnaddress(uword address @XY) {
|
||||
%asm {{
|
||||
; push like JSR would: address-1, MSB first then LSB
|
||||
cpx #0
|
||||
bne +
|
||||
dey
|
||||
+ dex
|
||||
tya
|
||||
pha
|
||||
txa
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
|
@ -465,6 +465,20 @@ save_SCRATCH_ZPWORD2 .word 0
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push_returnaddress(uword address @XY) {
|
||||
%asm {{
|
||||
; push like JSR would: address-1, MSB first then LSB
|
||||
cpx #0
|
||||
bne +
|
||||
dey
|
||||
+ dex
|
||||
tya
|
||||
pha
|
||||
txa
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop() -> ubyte @A {
|
||||
%asm {{
|
||||
pla
|
||||
|
3
compiler/res/prog8lib/virtual/coroutines.p8
Normal file
3
compiler/res/prog8lib/virtual/coroutines.p8
Normal file
@ -0,0 +1,3 @@
|
||||
coroutines {
|
||||
; the VM doesn't support coroutines because the flow of control is radically different than how the 6502 CPU does it.
|
||||
}
|
@ -190,6 +190,14 @@ sys {
|
||||
}}
|
||||
}
|
||||
|
||||
sub push_returnaddress(uword w) {
|
||||
; note: this actually doesn't do anything useful on the VM because the code execution doesn't use the simulated cpu stack
|
||||
%ir {{
|
||||
loadm.w r65535,sys.pushw.w
|
||||
push.w r65535
|
||||
}}
|
||||
}
|
||||
|
||||
sub pop() -> ubyte {
|
||||
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
|
||||
%ir {{
|
||||
|
@ -172,7 +172,6 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
|
||||
listOf(
|
||||
"animals",
|
||||
"balls",
|
||||
"coroutines",
|
||||
"cube3d",
|
||||
"cube3d-float",
|
||||
"cube3d-gfx",
|
||||
@ -182,6 +181,7 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
|
||||
"maze",
|
||||
"mandelbrot",
|
||||
"mandelbrot-gfx",
|
||||
"multitasking",
|
||||
"numbergame",
|
||||
"primes",
|
||||
"queens",
|
||||
|
@ -353,6 +353,7 @@ main {
|
||||
sub start() {
|
||||
sys.push(11)
|
||||
sys.pushw(2222)
|
||||
sys.push_returnaddress(3333)
|
||||
cx16.r2++
|
||||
cx16.r1 = sys.popw()
|
||||
cx16.r0L = sys.pop()
|
||||
@ -363,6 +364,7 @@ main {
|
||||
val assembly = assemblyFile.readText()
|
||||
assembly shouldContain "inlined routine follows: push"
|
||||
assembly shouldContain "inlined routine follows: pushw"
|
||||
assembly shouldContain "inlined routine follows: push_returnaddress"
|
||||
assembly shouldContain "inlined routine follows: pop"
|
||||
assembly shouldContain "inlined routine follows: popw"
|
||||
}
|
||||
|
@ -369,6 +369,19 @@ Read the `conv source code <https://github.com/irmen/prog8/tree/master/compiler/
|
||||
to see what's in there.
|
||||
|
||||
|
||||
coroutines (experimental)
|
||||
-------------------------
|
||||
Provides a system to make cooperative multitasking programs via coroutines.
|
||||
A 'coroutine' is a subroutine whose execution you can pause and resume.
|
||||
This library handles the voodoo for you to switch between such coroutines transparently,
|
||||
so it can seem that your program is executing many subroutines at the same time.
|
||||
|
||||
API is experimental and may change or disappear in a future version.
|
||||
|
||||
Read the `coroutines source code <https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib/coroutines.p8>`_
|
||||
to see what's in there. And look at the ``multitasking`` example to see how it can be used.
|
||||
|
||||
|
||||
cx16
|
||||
----
|
||||
This is available on *all targets*, it is always imported as part of syslib.
|
||||
@ -1079,13 +1092,19 @@ sys (part of syslib)
|
||||
|
||||
``pushw (value)``
|
||||
pushes a 16-bit word value on the CPU hardware stack. Low-level function that should normally not be used.
|
||||
Don't assume anything about the order in which the bytes are pushed - popw will make sense of them again.
|
||||
|
||||
``push_returnaddress (address)``
|
||||
pushes a 16 bit memory address on the CPU hardware stack in the same byte order as a JSR instruction would,
|
||||
which means the next RTS instruction will jump to that address instead.you
|
||||
You cannot use pushw() for this because the bytes pushed by JSR are different
|
||||
|
||||
``pop ()``
|
||||
pops a byte value off the CPU hardware stack and returns it.
|
||||
Low-level function that should normally not be used.
|
||||
|
||||
``popw ()``
|
||||
pops a 16-bit word value off the CPU hardware stack and returns it.
|
||||
pops a 16-bit word value off the CPU hardware stack that was pushed before by pushw, and returns it.
|
||||
Low-level function that should normally not be used.
|
||||
|
||||
|
||||
|
@ -12,7 +12,7 @@ Future Things and Ideas
|
||||
|
||||
- support &, &< and &> on array elements from split word arrays too not just the array as a whole (to get rid of the error "&< is only valid on array variables"
|
||||
and "cannot take the adress of a word element that is in a split-word array" and the TODOS "address of element of a split word array")
|
||||
- fix leftover asmgen split word array todo's
|
||||
- after that: fix leftover asmgen split word array todo's
|
||||
- Kotlin: can we use inline value classes in certain spots?
|
||||
- Improve the SublimeText syntax file for prog8, you can also install this for 'bat': https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions
|
||||
|
||||
@ -72,6 +72,7 @@ IR/VM
|
||||
|
||||
Libraries
|
||||
---------
|
||||
- coroutines: make yield() return a configured uword so that a task subroutine can get reused for multiple different things
|
||||
- monogfx: flood fill should be able to fill stippled (it could do this in the past? vm version does it?)
|
||||
- Sorting module gnomesort_uw could be optimized more, rewrite in asm? Shellshort seems consistently faster even if most of the words are already sorted.
|
||||
- Add split-word array sorting routines to sorting module?
|
||||
|
@ -1,140 +0,0 @@
|
||||
|
||||
; Cooperative multitasking / Coroutines
|
||||
;
|
||||
; Uses cpu stack return address juggling to cycle between the different tasks when they call yield().
|
||||
; Super simplistic implementation here to just show the core idea.
|
||||
;
|
||||
; To make it actually useful, we probably have to:
|
||||
; - make the list of tasks dynamic;
|
||||
; - allow tasks to finish, allow new tasks to be added
|
||||
; - return a unique value (pointer that you had to provide when adding the task to the list?)
|
||||
; from yield() that the subroutine could use to access unique state,
|
||||
; because right now a single task == a single subroutine; right now you cannot re-use a subroutine to run
|
||||
; the same task multiple times for different things.
|
||||
|
||||
|
||||
%import textio
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.print("cooperative multitasking / coroutines\n\n")
|
||||
txt.print("here are 4 subroutines that each run\nan endless loop bouncing a digit around.")
|
||||
coroutines.start()
|
||||
}
|
||||
}
|
||||
|
||||
coroutines {
|
||||
uword[] tasklist = [&task1, &task2, &task3, &task4, &vsynctask]
|
||||
|
||||
sub start() {
|
||||
goto tasklist[0]
|
||||
}
|
||||
|
||||
uword[len(tasklist)] returnaddresses
|
||||
ubyte active_task
|
||||
|
||||
sub yield() {
|
||||
; store the return address of the yielding task,
|
||||
; and continue with the next one instead (round-robin)
|
||||
returnaddresses[active_task] = sys.popw()
|
||||
active_task++
|
||||
if active_task==len(returnaddresses)
|
||||
active_task=0
|
||||
cx16.r0 = returnaddresses[active_task]
|
||||
if cx16.r0==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
|
||||
cx16.r0 = tasklist[active_task]-1
|
||||
sys.push(cx16.r0H)
|
||||
sys.push(cx16.r0L)
|
||||
} else
|
||||
sys.pushw(cx16.r0)
|
||||
|
||||
; returning from yield then continues with the next coroutine
|
||||
}
|
||||
|
||||
sub task1() {
|
||||
const ubyte x = 5
|
||||
ubyte y
|
||||
repeat {
|
||||
for y in 10 to 24 {
|
||||
txt.setchr(x, y-1, sc:' ')
|
||||
txt.setchr(x, y, sc:'1')
|
||||
yield()
|
||||
}
|
||||
for y in 24 downto 10 {
|
||||
txt.setchr(x, y+1, sc:' ')
|
||||
txt.setchr(x, y, sc:'1')
|
||||
yield()
|
||||
}
|
||||
}
|
||||
; need infinite loop
|
||||
}
|
||||
|
||||
sub task2() {
|
||||
const ubyte x = 10
|
||||
ubyte y
|
||||
repeat {
|
||||
for y in 5 to 18 {
|
||||
txt.setchr(x, y-1, sc:' ')
|
||||
txt.setchr(x, y, sc:'2')
|
||||
yield()
|
||||
}
|
||||
for y in 18 downto 5 {
|
||||
txt.setchr(x, y+1, sc:' ')
|
||||
txt.setchr(x, y, sc:'2')
|
||||
yield()
|
||||
}
|
||||
}
|
||||
; need infinite loop
|
||||
}
|
||||
|
||||
sub task3() {
|
||||
ubyte x
|
||||
const ubyte y = 10
|
||||
repeat {
|
||||
for x in 14 to 38 {
|
||||
txt.setchr(x-1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'3')
|
||||
yield()
|
||||
}
|
||||
for x in 38 downto 14 {
|
||||
txt.setchr(x+1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'3')
|
||||
yield()
|
||||
}
|
||||
}
|
||||
; need infinite loop
|
||||
}
|
||||
|
||||
sub task4() {
|
||||
ubyte x
|
||||
const ubyte y = 14
|
||||
repeat {
|
||||
for x in 15 to 30 {
|
||||
txt.setchr(x-1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'4')
|
||||
yield()
|
||||
}
|
||||
for x in 30 downto 15 {
|
||||
txt.setchr(x+1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'4')
|
||||
yield()
|
||||
}
|
||||
}
|
||||
; need infinite loop
|
||||
}
|
||||
|
||||
sub vsynctask() {
|
||||
repeat {
|
||||
sys.waitvsync()
|
||||
sys.waitvsync()
|
||||
yield()
|
||||
}
|
||||
; need infinite loop
|
||||
}
|
||||
}
|
||||
|
||||
|
126
examples/multitasking.p8
Normal file
126
examples/multitasking.p8
Normal file
@ -0,0 +1,126 @@
|
||||
; Cooperative Multitasking example.
|
||||
; Can be compiled for different targets (except virtual).
|
||||
|
||||
%import coroutines
|
||||
%import textio
|
||||
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.print("cooperative multitasking / coroutines\n\n")
|
||||
txt.print("here are couple of routines that each\nrun a few loops bouncing a digit around.\n")
|
||||
|
||||
coroutines.killall()
|
||||
void coroutines.add(&task1)
|
||||
void coroutines.add(&task2)
|
||||
void coroutines.add(&task3)
|
||||
void coroutines.add(&task4)
|
||||
void coroutines.add(&delaytask)
|
||||
coroutines.run()
|
||||
|
||||
txt.print("we're all done!\n")
|
||||
}
|
||||
|
||||
sub task1() {
|
||||
const ubyte x = 5
|
||||
ubyte y
|
||||
repeat 3 {
|
||||
for y in 10 to 24 {
|
||||
txt.setchr(x, y-1, sc:' ')
|
||||
txt.setchr(x, y, sc:'1')
|
||||
coroutines.yield()
|
||||
}
|
||||
for y in 24 downto 10 {
|
||||
txt.setchr(x, y+1, sc:' ')
|
||||
txt.setchr(x, y, sc:'1')
|
||||
coroutines.yield()
|
||||
}
|
||||
}
|
||||
txt.setchr(x, 10, sc:' ')
|
||||
}
|
||||
|
||||
sub task2() {
|
||||
const ubyte x = 10
|
||||
ubyte y
|
||||
repeat 2 {
|
||||
for y in 5 to 18 {
|
||||
txt.setchr(x, y-1, sc:' ')
|
||||
txt.setchr(x, y, sc:'2')
|
||||
coroutines.yield()
|
||||
}
|
||||
for y in 18 downto 5 {
|
||||
txt.setchr(x, y+1, sc:' ')
|
||||
txt.setchr(x, y, sc:'2')
|
||||
coroutines.yield()
|
||||
}
|
||||
}
|
||||
txt.setchr(x, 5, sc:' ')
|
||||
|
||||
; add a new task dynamically
|
||||
void coroutines.add(&task5)
|
||||
}
|
||||
|
||||
sub task3() {
|
||||
ubyte x
|
||||
const ubyte y = 10
|
||||
repeat 4 {
|
||||
for x in 14 to 38 {
|
||||
txt.setchr(x-1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'3')
|
||||
coroutines.yield()
|
||||
}
|
||||
for x in 38 downto 14 {
|
||||
txt.setchr(x+1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'3')
|
||||
coroutines.yield()
|
||||
}
|
||||
}
|
||||
txt.setchr(14, y, sc:' ')
|
||||
}
|
||||
|
||||
sub task4() {
|
||||
ubyte x
|
||||
const ubyte y = 14
|
||||
repeat 4 {
|
||||
for x in 15 to 30 {
|
||||
txt.setchr(x-1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'4')
|
||||
coroutines.yield()
|
||||
}
|
||||
for x in 30 downto 15 {
|
||||
txt.setchr(x+1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'4')
|
||||
coroutines.yield()
|
||||
}
|
||||
}
|
||||
txt.setchr(15, y, sc:' ')
|
||||
}
|
||||
|
||||
sub task5() {
|
||||
ubyte x
|
||||
const ubyte y = 16
|
||||
repeat 4 {
|
||||
for x in 15 to 30 {
|
||||
txt.setchr(x-1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'5')
|
||||
coroutines.yield()
|
||||
}
|
||||
for x in 30 downto 15 {
|
||||
txt.setchr(x+1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'5')
|
||||
coroutines.yield()
|
||||
}
|
||||
}
|
||||
txt.setchr(15, y, sc:' ')
|
||||
}
|
||||
|
||||
sub delaytask() {
|
||||
repeat 200 {
|
||||
sys.waitvsync()
|
||||
sys.waitvsync()
|
||||
coroutines.yield()
|
||||
}
|
||||
}
|
||||
}
|
128
examples/test.p8
128
examples/test.p8
@ -1,21 +1,125 @@
|
||||
%import coroutines
|
||||
%import textio
|
||||
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
uword[2] array1
|
||||
cx16.r0--
|
||||
|
||||
array1[1] = $0122
|
||||
txt.print_uwhex(array1[1], true)
|
||||
txt.nl()
|
||||
rol(array1[1])
|
||||
txt.print_uwhex(array1[1], true)
|
||||
txt.nl()
|
||||
sys.set_carry()
|
||||
ror(array1[1])
|
||||
txt.print_uwhex(array1[1], true)
|
||||
txt.nl()
|
||||
txt.print("cooperative multitasking / coroutines\n\n")
|
||||
txt.print("here are couple of routines that each\nrun a few loops bouncing a digit around.\n")
|
||||
|
||||
void coroutines.add(&task1)
|
||||
void coroutines.add(&task2)
|
||||
void coroutines.add(&task3)
|
||||
void coroutines.add(&task4)
|
||||
void coroutines.add(&vsynctask)
|
||||
coroutines.run()
|
||||
|
||||
txt.print("we're all done!\n")
|
||||
}
|
||||
|
||||
sub task1() {
|
||||
const ubyte x = 5
|
||||
ubyte y
|
||||
repeat 3 {
|
||||
for y in 10 to 24 {
|
||||
txt.setchr(x, y-1, sc:' ')
|
||||
txt.setchr(x, y, sc:'1')
|
||||
coroutines.yield()
|
||||
}
|
||||
for y in 24 downto 10 {
|
||||
txt.setchr(x, y+1, sc:' ')
|
||||
txt.setchr(x, y, sc:'1')
|
||||
coroutines.yield()
|
||||
}
|
||||
}
|
||||
txt.setchr(x, 10, sc:' ')
|
||||
}
|
||||
|
||||
sub task2() {
|
||||
const ubyte x = 10
|
||||
ubyte y
|
||||
repeat 2 {
|
||||
for y in 5 to 18 {
|
||||
txt.setchr(x, y-1, sc:' ')
|
||||
txt.setchr(x, y, sc:'2')
|
||||
coroutines.yield()
|
||||
}
|
||||
for y in 18 downto 5 {
|
||||
txt.setchr(x, y+1, sc:' ')
|
||||
txt.setchr(x, y, sc:'2')
|
||||
coroutines.yield()
|
||||
}
|
||||
}
|
||||
txt.setchr(x, 5, sc:' ')
|
||||
|
||||
; add a new task dynamically
|
||||
void coroutines.add(&task5)
|
||||
}
|
||||
|
||||
sub task3() {
|
||||
ubyte x
|
||||
const ubyte y = 10
|
||||
repeat 4 {
|
||||
for x in 14 to 38 {
|
||||
txt.setchr(x-1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'3')
|
||||
coroutines.yield()
|
||||
}
|
||||
for x in 38 downto 14 {
|
||||
txt.setchr(x+1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'3')
|
||||
coroutines.yield()
|
||||
}
|
||||
}
|
||||
txt.setchr(14, y, sc:' ')
|
||||
}
|
||||
|
||||
sub task4() {
|
||||
ubyte x
|
||||
const ubyte y = 14
|
||||
repeat 4 {
|
||||
for x in 15 to 30 {
|
||||
txt.setchr(x-1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'4')
|
||||
coroutines.yield()
|
||||
}
|
||||
for x in 30 downto 15 {
|
||||
txt.setchr(x+1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'4')
|
||||
coroutines.yield()
|
||||
}
|
||||
}
|
||||
txt.setchr(15, y, sc:' ')
|
||||
}
|
||||
|
||||
sub task5() {
|
||||
ubyte x
|
||||
const ubyte y = 16
|
||||
repeat 4 {
|
||||
for x in 15 to 30 {
|
||||
txt.setchr(x-1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'5')
|
||||
coroutines.yield()
|
||||
}
|
||||
for x in 30 downto 15 {
|
||||
txt.setchr(x+1, y, sc:' ')
|
||||
txt.setchr(x, y, sc:'5')
|
||||
coroutines.yield()
|
||||
}
|
||||
}
|
||||
txt.setchr(15, y, sc:' ')
|
||||
}
|
||||
|
||||
sub vsynctask() {
|
||||
repeat 200 {
|
||||
sys.waitvsync()
|
||||
sys.waitvsync()
|
||||
coroutines.yield()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user