# HG changeset patch # User Paul Boddie # Date 1435493315 -7200 # Node ID caa80f526ae6b3df5d11538276a850677a3aaed5 # Parent 67e61de96a24c8fb5d099bd09ab70ee371547342 Introduced "user space" stacks for tasks so that they do not employ the CPU stack directly when invoking subroutines but instead employ independent stacks that should not be disrupted by task switches. diff -r 67e61de96a24 -r caa80f526ae6 macros.oph --- a/macros.oph Sat Jun 27 20:35:31 2015 +0200 +++ b/macros.oph Sun Jun 28 14:08:35 2015 +0200 @@ -52,6 +52,51 @@ sta _2+1 .macend +; copy word from location to indirect location +; +; _1: source +; _2: target reference +; +; affects: A, Y + +.macro mov16_to_ref + ldy #0 + lda _1 + sta (_2), y + iny + lda _1+1 + sta (_2), y +.macend + +; copy word from indirect location to location +; +; _1: source reference +; _2: target +; +; affects: A, Y + +.macro mov16_from_ref + ldy #0 + lda (_1), y + sta _2 + iny + lda (_1), y + sta _2+1 +.macend + +; copy byte between indirect locations +; +; _1: source reference +; _2: target reference +; +; Y: offset +; affects: A + +.macro mov8_refs + lda (_1), y + sta (_2), y +.macend + ; copy word from location to stack ; ; _1: source @@ -95,4 +140,47 @@ sta _2+1 .macend +; subtract word from locations +; +; _1: valueW +; _2: {valueL, valueH} -> {valueL', valueH'} + +.macro sub16 + sec + lda _2 + sbc #<_1 + sta _2 + lda _2+1 + sbc #>_1 + sta _2+1 +.macend + +; negate accumulator +; +; affects: A, C + +.macro negate + clc + eor #$ff + adc #1 +.macend + +; push PC onto a "user space" stack and jump to subroutine +; +; _1: target + +.macro call + sei ; prevent CPU stack disturbance + jsr copy_to_user_stack + cli ; permit CPU stack access again + jmp _1 + nop +.macend + +; pull PC from a "user space" stack and return from subroutine + +.macro return + jmp copy_from_user_stack +.macend + ; vim: tabstop=4 expandtab shiftwidth=4 diff -r 67e61de96a24 -r caa80f526ae6 switcher.oph --- a/switcher.oph Sat Jun 27 20:35:31 2015 +0200 +++ b/switcher.oph Sun Jun 28 14:08:35 2015 +0200 @@ -25,24 +25,158 @@ .alias NEXTH $73 .alias CURRENT $74 .alias CURRENTH $75 +.alias USER $76 +.alias ARG0 $80 +.alias ARG0H $81 +.alias ARG1 $82 +.alias ARG1H $83 .alias TASK_TABLE_LENGTH 10 +; "user space" stack for the main program where the invocations might be +; interrupted and where the CPU stack might be disrupted + +.alias main_stack $1fff + + + .org $2000 .text + + + +; main program, installing the handler and adding example tasks + main: jsr install_handler - .invoke store16 first_task, $76 - jsr new_task - .invoke store16 second_task, $76 - jsr new_task - .invoke store16 third_task, $76 - jsr new_task + .invoke store16 tasks, ARG1 + .invoke store16 main_stack, USER + .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 wait: jmp wait ; wait for the switcher to take over +; move CPU stack data to the "user space" stack +; +; affects: USER (gains LSB, MSB) + +copy_to_user_stack: + pha ; A -> stack + txa + pha ; X -> stack + tsx ; capture the stack pointer now for later use + ; (stack is Y, X, A, LSB, MSB) + tya + pha ; Y -> stack + + ; save zero-page locations used + + .invoke push16 SP + + ; initialise the stack frame pointer + + txa + sta SP + lda #$01 ; $01xx + sta SPH + + ; allocate "user space" stack space to be compatible with the CPU stack + + .invoke sub16 5, USER + + ; copy the PC, updated by 4 (see the call macro) + + ldy #3 + clc + lda (SP), y + adc #4 + sta (USER), y + iny + lda (SP), y + adc #0 + sta (USER), y + + ; correct "user space" stack + + .invoke add16 3, USER + + ; restore zero-page locations used + + .invoke pull16 SP + + ; return to the caller + + pla + tay ; stack -> Y + pla + tax ; stack -> X + pla ; stack -> A + rts + + + +; move CPU stack data from the "user space" stack +; +; affects: USER (loses LSB, MSB) + +copy_from_user_stack: + pha ; padding + pha ; padding + pha ; A -> stack + txa + pha ; X -> stack + tsx ; capture the stack pointer now for later use + ; (stack is Y, X, A, LSB, MSB) + tya + pha ; Y -> stack + + ; save zero-page locations used + + .invoke push16 SP + + ; initialise the stack frame pointer + + txa + sta SP + lda #$01 ; $01xx + sta SPH + + ; adjust the "user space" stack pointer to be compatible with the PC stack + + .invoke sub16 3, USER + + ; copy the PC + + ldy #3 + .invoke mov8_refs USER, SP + iny + .invoke mov8_refs USER, SP + + ; update the "user space" stack pointer + + .invoke add16 5, USER + + ; restore zero-page locations used + + .invoke pull16 SP + + ; return to the stored PC + + pla + tay ; stack -> Y + pla + tax ; stack -> X + pla ; stack -> A + rts + + + ; install the interrupt handler address in IRQ1V ; ; affects: A @@ -134,32 +268,34 @@ .invoke mov16 current_task, CURRENT + ; store the user stack for the task + + .invoke mov16_to_ref USER, CURRENT + ldy #5 ; offset of MSB (Y, X, A, F, LSB, MSB) - lda (SP), y - sta (CURRENT), y + .invoke mov8_refs SP, CURRENT dey - lda (SP), y - sta (CURRENT), y + .invoke mov8_refs SP, CURRENT dey - lda (SP), y - sta (CURRENT), y + .invoke mov8_refs SP, CURRENT ; load flags, PC from next task structure - lda (NEXT), y - sta (SP), y + .invoke mov8_refs NEXT, SP iny - lda (NEXT), y - sta (SP), y + .invoke mov8_refs NEXT, SP iny - lda (NEXT), y - sta (SP), y + .invoke mov8_refs NEXT, SP iny ; make the next task the current one .invoke mov16 NEXT, current_task + ; set the user stack for the task + + .invoke mov16_from_ref NEXT, USER + exit_handler: ; restore zero-page locations used in the handler @@ -202,9 +338,8 @@ ; add a new task to the table ; -; $76, $77: location of task structure +; ARG0, ARG0H: location of task structure ; affects: A, Y -; returns: A = 0 (success); A = 1 (failure) new_task: @@ -230,19 +365,17 @@ ; copy the task structure location to the table sei - lda $77 + lda ARG0H sta tasks, y dey - lda $76 + lda ARG0 sta tasks, y cli - lda #0 - rts + .invoke return no_new_task: - lda #1 - rts + .invoke return @@ -262,14 +395,14 @@ iny sta tasks, y cli - rts + .invoke return + ; example tasks first_task: - .byte 0 ; currently unused - .byte 0 ; currently unused + .word 0 ; user stack pointer .byte 0 ; currently unused .byte 0 ; saved flags .word first_task_start ; saved PC @@ -280,8 +413,7 @@ second_task: - .byte 0 ; currently unused - .byte 0 ; currently unused + .word 0 ; user stack pointer .byte 0 ; currently unused .byte 0 ; saved flags .word second_task_start ; saved PC @@ -292,8 +424,7 @@ third_task: - .byte 0 ; currently unused - .byte 0 ; currently unused + .word third_task_stack_base ; user stack pointer .byte 0 ; currently unused .byte 0 ; saved flags .word third_task_start ; saved PC @@ -308,10 +439,18 @@ cmp #$ff bne _continue txa - jsr remove_task + .invoke call remove_task inx _continue: .invoke add16 1, $7010 jmp _loop +third_task_stack: + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 +third_task_stack_base: ; vim: tabstop=4 expandtab shiftwidth=4