# HG changeset patch # User Paul Boddie # Date 1435526359 -7200 # Node ID 522fbe2bcc6d352d7642066371332c20e52e3960 # Parent b639ee361009a584e94b2d1178b396451af3928b Preserve registers upon switching: not sure why I didn't do this before! Changed switching to evict the current task before looking for others, detecting when no tasks are installed and exiting to a designated task environment, adding an end handler to the main program. Tidied up the example tasks. diff -r b639ee361009 -r 522fbe2bcc6d switcher.oph --- a/switcher.oph Sun Jun 28 19:23:06 2015 +0200 +++ b/switcher.oph Sun Jun 28 23:19:19 2015 +0200 @@ -38,7 +38,7 @@ ; "user space" stack for the main program where the invocations might be ; interrupted and where the CPU stack might be disrupted -.alias main_stack $1ffc +.alias main_stack_base $1ffc .alias ABSTEMP $1ffe @@ -51,17 +51,65 @@ ; main program, installing the handler and adding example tasks main: - .invoke store16 tasks, ARG1 - .invoke store16 main_stack, USER + .invoke store16 tasks, ARG1 ; for debugging + + ; initialise a user stack and add this program as a task + + .invoke store16 main_stack_base, USER + .invoke store16 main_task, ARG0 + .invoke call new_task, + + + ; install the interrupt handler + +* jsr install_handler + + ; perform task installation + .invoke store16 first_task, ARG0 .invoke call new_task, + * .invoke store16 second_task, ARG0 .invoke call new_task, + * .invoke store16 third_task, ARG0 - .invoke call new_task, + -* jsr install_handler + .invoke call new_task, wait + + ; loop while other tasks exist + wait: - jmp wait ; wait for the switcher to take over + ldx #7 + sei + lda tasks, x ; check for the third task + cli + cmp #0 + bne wait + + ; where no other tasks exist, remove this task by updating its PC to the + ; end label and removing its entry + + .invoke store16 end, main_task+6 + lda #0 + .invoke call remove_task, shutdown + + ; wait for shutdown + +shutdown: + jmp shutdown + + ; return here with the initial main program stack + +end: + rts + + + +; main program task structure + +main_task: + .word main_stack_base ; user stack pointer + .byte 0 ; Y + .byte 0 ; X + .byte 0 ; A + .byte 0 ; saved flags + .word 0 ; saved PC @@ -86,21 +134,24 @@ pha ; A -> stack txa pha ; X -> stack - tsx ; capture the stack pointer now for later use - ; (stack is , X, A, F, LSB, MSB) tya pha ; Y -> stack + tsx ; capture the stack pointer now for later use + ; (stack is , Y, X, A, F, LSB, MSB) + ; save zero-page locations used in the handler .invoke push16 SP .invoke push16 NEXT .invoke push16 CURRENT + .invoke push16 TEMP init_sp: ; initialise the stack frame pointer + dex ; make the frame compatible with the task structure txa sta SP lda #$01 ; $01xx @@ -113,7 +164,7 @@ ; obtain the stack location of the stored PC MSB - ldy #5 ; offset of MSB (Y, X, A, F, LSB, MSB) + ldy #7 ; offset of MSB (_, _, Y, X, A, F, LSB, MSB) lda (SP), y ; reference the stack location and compute PC MSB & $80 @@ -123,16 +174,35 @@ ; exit if PC MSB & $80 != 0 beq exit_handler -load_task: +evict_task: - ; load next task + ; obtain the current task using the task offset ldx task_offset lda tasks, x - sta NEXT + sta CURRENT inx lda tasks, x - sta NEXTH + sta CURRENTH + dex + + ; store the user stack for the task + + .invoke mov16_to_ref USER, CURRENT + + ; store registers, flags, PC in current task structure + + ldy #7 ; offset of MSB (_, _, Y, X, A, F, LSB, MSB) +* .invoke mov8_refs SP, CURRENT + dey + cpy #2 + bpl - + +load_task: + + ; examine the next task + + inx inx ; reset the task index if necessary @@ -142,43 +212,55 @@ ldx #0 test_task: + + ; obtain task location MSB + + inx + lda tasks, x + sta NEXTH + + ; install if not a null task (tasks do not reside in zero page) + + cmp #0 + bne restore_task + + ; test against the start index + ; where a complete circuit of the task table has not been performed, + ; continue + + dex + txa + cmp task_offset + bne load_task + + ; where a complete circuit has been performed without finding any tasks + ; uninstall the handler and install the details of the end handler + + sei + .invoke mov16 old_handler, IRQ1V + cli + + ; the end handler resides at the end of the task table + + ldx #TASK_TABLE_LENGTH + inx + lda tasks, x + sta NEXTH + +restore_task: + + dex + lda tasks, x + sta NEXT stx task_offset - ; exit if null task - - lda NEXTH - cmp #0 - beq exit_handler - -switch_task: - - ; store flags, PC in current task structure - - .invoke mov16 current_task, CURRENT - - ; store the user stack for the task - - .invoke mov16_to_ref USER, CURRENT + ; load registers, flags, PC from next task structure - ldy #5 ; offset of MSB (Y, X, A, F, LSB, MSB) - .invoke mov8_refs SP, CURRENT - dey - .invoke mov8_refs SP, CURRENT + ldy #7 ; offset of MSB (_, _, Y, X, A, F, LSB, MSB) +* .invoke mov8_refs NEXT, SP dey - .invoke mov8_refs SP, CURRENT - - ; load flags, PC from next task structure - - .invoke mov8_refs NEXT, SP - iny - .invoke mov8_refs NEXT, SP - iny - .invoke mov8_refs NEXT, SP - iny - - ; make the next task the current one - - .invoke mov16 NEXT, current_task + cpy #2 + bpl - ; set the user stack for the task @@ -188,6 +270,7 @@ ; restore zero-page locations used in the handler + .invoke pull16 TEMP .invoke pull16 CURRENT .invoke pull16 NEXT .invoke pull16 SP @@ -205,10 +288,6 @@ old_handler: .word 0 -; location of current task (duplicated from the table) - -current_task: .word 0 - ; offset of current task in table (in multiples of 2) task_offset: .byte 0 @@ -220,7 +299,9 @@ .word 0 .word 0 .word 0 - .word 0 + .word 0 ; last entry +end_handler: + .word main_task ; end handler entry @@ -294,51 +375,58 @@ first_task: .word 0 ; user stack pointer - .byte 0 ; currently unused + .byte 0 ; Y + .byte 0 ; X + .byte 0 ; A .byte 0 ; saved flags .word first_task_start ; saved PC first_task_start: lda #1 + sta $7000 first_task_continue: - sta $7000 - adc #1 + inc $7000 jmp first_task_continue second_task: .word 0 ; user stack pointer - .byte 0 ; currently unused + .byte 0 ; Y + .byte 0 ; X + .byte 0 ; A .byte 0 ; saved flags .word second_task_start ; saved PC second_task_start: lda #1 + sta $7010 second_task_continue: - sta $7008 - adc #1 + inc $7010 jmp second_task_continue third_task: .word third_task_stack_base ; user stack pointer - .byte 0 ; currently unused + .byte 0 ; Y + .byte 0 ; X + .byte 0 ; A .byte 0 ; saved flags .word third_task_start ; saved PC third_task_start: - ldx #0 + ldx #2 _begin: ldy #0 ; reset counter LSB lda #0 ; reset counter MSB - sta $7011 + sta $7021 _loop: - sty $7010 + stx $7028 + sty $7020 cpy #$ff bne _continue clc - lda $7011 ; next MSB + lda $7021 ; next MSB adc #1 - sta $7011 + sta $7021 cmp #$ff bne _continue txa @@ -348,6 +436,11 @@ tax inx ; move to next offset inx ; which is 2 locations away + cpx #TASK_TABLE_LENGTH + beq _next + jmp _begin +_next: + ldx #0 jmp _begin _continue: iny