mirror of
https://github.com/irmen/prog8.git
synced 2025-12-19 20:17:17 +00:00
pet32: fixed txt.plot() mixing up row and column
This commit is contained in:
@@ -5,27 +5,28 @@
|
|||||||
; Uses cpu stack return address juggling to cycle between different tasks.
|
; Uses cpu stack return address juggling to cycle between different tasks.
|
||||||
;
|
;
|
||||||
; Features:
|
; Features:
|
||||||
; - can have a dynamic number of tasks (max 64), when tasks end they're automaticall removed from the task list.
|
; - can have a dynamic number of active tasks (max 64), when a task ends it is automatically removed from the task list.
|
||||||
; - you can add new tasks, even from IRQ handlers, while the rest is already running.
|
; - you can add new tasks, while the rest is already running. Just not yet from inside IRQ handlers!
|
||||||
; - tasks are regular subroutines but have to call yield() to pass control to the next task (round-robin)
|
; - tasks are regular subroutines but have to call yield() to pass control to the next task (round-robin)
|
||||||
; - yield() returns the registered userdata value for that task, so a single subroutine could be used as multiple tasks on different userdata
|
; - yield() returns the registered userdata value for that task, so a single subroutine could be used as multiple tasks on different userdata
|
||||||
; BUT!! in that case, the subroutine cannot have any variables of its own that keep state, because they're shared across the multiple tasks
|
; BUT!! in that case, the subroutine cannot have any variables of its own that keep state, because they're shared across the multiple tasks
|
||||||
; - you can kill a task (if you know it's id...)
|
; - you can kill a task (if you know it's id...)
|
||||||
; - when all tasks are finished the run() call will also return.
|
; - 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.
|
; - tasks can't push anything on the cpu stack before calling yield() - that will cause chaos.
|
||||||
|
; - this library is not (yet) usable from IRQ handlers. Don't do it. It will end badly. (can't manipulate the task list simultaneously)
|
||||||
;
|
;
|
||||||
; Difference from IRQ handlers:
|
; Difference from IRQ handlers:
|
||||||
; - you can have many tasks instead of only 2 (main program + irq handler)
|
; - 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
|
; - 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
|
; - tasks fully control the switch to the next task; there is no preemptive switching
|
||||||
|
; - tasks will need to save/restore their own state, maybe by useing the userdata (pointer?) and/or task id for that.
|
||||||
;
|
;
|
||||||
; USAGE:
|
; USAGE:
|
||||||
; - call add(taskaddress) to add a new task. It returns the task id.
|
; - call add(taskaddress) to add a new task. It returns the task id.
|
||||||
; - call run() to start executing all tasks until none are left.
|
; - call run() to start executing all tasks until none are left.
|
||||||
; - in tasks: call yield() to pass control to the next task. Use the returned userdata value to do different things.
|
; - in tasks: call yield() to pass control to the next task. Use the returned userdata value to do different things.
|
||||||
; - in tasks: if you need that userdata value immediately, simply start the task with a yield() call.
|
|
||||||
; - call current() to get the current task id.
|
; - call current() to get the current task id.
|
||||||
; - call kill(tasknumber) to kill a task by id.
|
; - call kill(taskid) to kill a task by id.
|
||||||
; - call killall() to kill all tasks.
|
; - call killall() to kill all tasks.
|
||||||
; - IMPORTANT: if you add the same subroutine multiple times, IT CANNOT DEPEND ON ANY LOCAL VARIABLES OR R0-R15 TO KEEP STATE. NOT EVEN REPEAT LOOP COUNTERS.
|
; - IMPORTANT: if you add the same subroutine multiple times, IT CANNOT DEPEND ON ANY LOCAL VARIABLES OR R0-R15 TO KEEP STATE. NOT EVEN REPEAT LOOP COUNTERS.
|
||||||
; Those are all shared in the different tasks! You HAVE to use a mechanism around the userdata value (pointer?) to keep separate state elsewhere!
|
; Those are all shared in the different tasks! You HAVE to use a mechanism around the userdata value (pointer?) to keep separate state elsewhere!
|
||||||
@@ -41,18 +42,15 @@ coroutines {
|
|||||||
; find the next empty slot in the tasklist and stick it there
|
; 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!
|
; 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)
|
; 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 {
|
for cx16.r0L in 0 to len(tasklist)-1 {
|
||||||
if tasklist[cx16.r0L] == 0 {
|
if tasklist[cx16.r0L] == 0 {
|
||||||
tasklist[cx16.r0L] = taskaddress
|
tasklist[cx16.r0L] = taskaddress
|
||||||
userdatas[cx16.r0L] = userdata
|
userdatas[cx16.r0L] = userdata
|
||||||
returnaddresses[cx16.r0L] = 0
|
returnaddresses[cx16.r0L] = 0
|
||||||
sys.irqsafe_clear_irqd()
|
|
||||||
sys.set_carry()
|
sys.set_carry()
|
||||||
return cx16.r0L
|
return cx16.r0L
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sys.irqsafe_clear_irqd()
|
|
||||||
; no space for new task
|
; no space for new task
|
||||||
sys.clear_carry()
|
sys.clear_carry()
|
||||||
return 255
|
return 255
|
||||||
@@ -60,11 +58,9 @@ coroutines {
|
|||||||
|
|
||||||
sub killall() {
|
sub killall() {
|
||||||
; kill all existing tasks
|
; kill all existing tasks
|
||||||
sys.irqsafe_set_irqd()
|
|
||||||
for cx16.r0L in 0 to len(tasklist)-1 {
|
for cx16.r0L in 0 to len(tasklist)-1 {
|
||||||
kill(cx16.r0L)
|
kill(cx16.r0L)
|
||||||
}
|
}
|
||||||
sys.irqsafe_clear_irqd()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub run() {
|
sub run() {
|
||||||
@@ -104,7 +100,6 @@ resume_with_next_task:
|
|||||||
|
|
||||||
sub next_task() -> bool {
|
sub next_task() -> bool {
|
||||||
; search through the task list for the next active task
|
; search through the task list for the next active task
|
||||||
sys.irqsafe_set_irqd()
|
|
||||||
repeat len(tasklist) {
|
repeat len(tasklist) {
|
||||||
active_task++
|
active_task++
|
||||||
if active_task==len(returnaddresses)
|
if active_task==len(returnaddresses)
|
||||||
@@ -112,18 +107,16 @@ resume_with_next_task:
|
|||||||
task_start = tasklist[active_task]
|
task_start = tasklist[active_task]
|
||||||
if task_start!=0 {
|
if task_start!=0 {
|
||||||
task_continue = returnaddresses[active_task]
|
task_continue = returnaddresses[active_task]
|
||||||
sys.irqsafe_clear_irqd()
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sys.irqsafe_clear_irqd()
|
|
||||||
return false ; no task
|
return false ; no task
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub kill(ubyte tasknum) {
|
sub kill(ubyte taskid) {
|
||||||
tasklist[tasknum] = 0
|
tasklist[taskid] = 0
|
||||||
returnaddresses[tasknum] = 0
|
returnaddresses[taskid] = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
sub current() -> ubyte {
|
sub current() -> ubyte {
|
||||||
|
|||||||
@@ -225,13 +225,13 @@ asmsub plot (ubyte col @ Y, ubyte row @ X) {
|
|||||||
jsr home
|
jsr home
|
||||||
cpy #0
|
cpy #0
|
||||||
beq +
|
beq +
|
||||||
- lda #17
|
- lda #29
|
||||||
jsr chrout
|
jsr chrout
|
||||||
dey
|
dey
|
||||||
bne -
|
bne -
|
||||||
+ cpx #0
|
+ cpx #0
|
||||||
beq +
|
beq +
|
||||||
- lda #29
|
- lda #17
|
||||||
jsr chrout
|
jsr chrout
|
||||||
dex
|
dex
|
||||||
bne -
|
bne -
|
||||||
|
|||||||
@@ -29,20 +29,22 @@ main {
|
|||||||
repeat {
|
repeat {
|
||||||
ubyte key = cbm.GETIN2()
|
ubyte key = cbm.GETIN2()
|
||||||
if key!=0 {
|
if key!=0 {
|
||||||
void coroutines.add(&countertask, key)
|
ubyte taskid = coroutines.add(&countertask, key)
|
||||||
|
counters[taskid] = 222
|
||||||
}
|
}
|
||||||
void coroutines.yield()
|
void coroutines.yield()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ubyte[coroutines.MAX_TASKS] counters = [100] * coroutines.MAX_TASKS
|
ubyte[coroutines.MAX_TASKS] counters
|
||||||
|
|
||||||
sub countertask() {
|
sub countertask() {
|
||||||
repeat {
|
repeat {
|
||||||
uword userdata = coroutines.yield() ; yield and obtain our userdata
|
uword userdata = coroutines.yield() ; yield and obtain our userdata
|
||||||
ubyte tid = coroutines.current() ; what task are we?
|
ubyte tid = coroutines.current() ; what task are we?
|
||||||
|
counters[tid]-- ; our counter is in the array, it cannot be kept in a local variable (shared state)
|
||||||
|
|
||||||
txt.plot(15, 10 + tid)
|
txt.plot(15, 10 + tid)
|
||||||
counters[tid]-- ; our counter is in the array, cannot be a local variable (shared state)
|
|
||||||
if counters[tid] == 0 {
|
if counters[tid] == 0 {
|
||||||
txt.print(" ")
|
txt.print(" ")
|
||||||
return ; done, exit the task
|
return ; done, exit the task
|
||||||
|
|||||||
118
examples/test.p8
118
examples/test.p8
@@ -1,4 +1,3 @@
|
|||||||
%import coroutines
|
|
||||||
%import textio
|
%import textio
|
||||||
|
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
@@ -6,120 +5,7 @@
|
|||||||
|
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
cx16.r0--
|
txt.plot(20,5)
|
||||||
|
txt.print("hello")
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user