1 ; A task switcher for the Acorn Electron. 2 3 ; Copyright (C) 2015 Paul Boddie <paul@boddie.org.uk> 4 5 ; This program is free software; you can redistribute it and/or modify it under 6 ; the terms of the GNU General Public License as published by the Free Software 7 ; Foundation; either version 3 of the License, or (at your option) any later 8 ; version. 9 10 ; This program is distributed in the hope that it will be useful, but WITHOUT 11 ; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 12 ; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 13 ; details. 14 15 ; You should have received a copy of the GNU General Public License along with 16 ; this program. If not, see <http://www.gnu.org/licenses/>. 17 18 .include "macros.oph" 19 20 .alias IRQ1V $204 21 22 .alias SP $70 23 .alias SPH $71 24 .alias NEXT $72 25 .alias NEXTH $73 26 .alias CURRENT $74 27 .alias CURRENTH $75 28 29 .alias TASK_TABLE_LENGTH 10 30 31 .org $2000 32 .text 33 main: 34 jsr install_handler 35 .invoke store16 first_task, $76 36 jsr new_task 37 .invoke store16 second_task, $76 38 jsr new_task 39 test: 40 jmp test 41 42 43 44 ; install the interrupt handler address in IRQ1V 45 ; 46 ; affects: A 47 48 install_handler: 49 sei 50 .invoke mov16 IRQ1V, old_handler 51 .invoke store16 handler, IRQ1V 52 cli 53 rts 54 55 56 57 ; handle interrupts 58 ; 59 ; affects: (temporary stack usage) 60 61 handler: 62 pha ; A -> stack 63 txa 64 pha ; X -> stack 65 tsx ; capture the stack pointer now for later use 66 ; (stack is <empty>, X, A, F, LSB, MSB) 67 tya 68 pha ; Y -> stack 69 70 ; save zero-page locations used in the handler 71 72 .invoke push16 SP 73 .invoke push16 NEXT 74 .invoke push16 CURRENT 75 76 init_sp: 77 78 ; initialise the stack frame pointer 79 80 txa 81 sta SP 82 lda #$01 ; $01xx 83 sta SPH 84 85 test_pc: 86 87 ; test PC for execution of ROM routines 88 ; these are probably not re-entrant 89 90 ; obtain the stack location of the stored PC MSB 91 92 ldy #5 ; offset of MSB (Y, X, A, F, LSB, MSB) 93 lda (SP), y 94 95 ; reference the stack location and compute PC MSB & $80 96 and #$80 97 cmp #$80 98 99 ; exit if PC MSB & $80 != 0 100 beq exit_handler 101 102 load_task: 103 104 ; load next task 105 106 ldx task_offset 107 lda tasks, x 108 sta NEXT 109 inx 110 lda tasks, x 111 sta NEXTH 112 inx 113 114 ; reset the task index if necessary 115 116 cpx #TASK_TABLE_LENGTH 117 bne test_task 118 ldx #0 119 120 test_task: 121 stx task_offset 122 123 ; exit if null task 124 125 lda NEXTH 126 cmp #0 127 beq exit_handler 128 129 switch_task: 130 131 ; store flags, PC in current task structure 132 133 .invoke mov16 current_task, CURRENT 134 135 ldy #5 ; offset of MSB (Y, X, A, F, LSB, MSB) 136 lda (SP), y 137 sta (CURRENT), y 138 dey 139 lda (SP), y 140 sta (CURRENT), y 141 dey 142 lda (SP), y 143 sta (CURRENT), y 144 145 ; load flags, PC from next task structure 146 147 lda (NEXT), y 148 sta (SP), y 149 iny 150 lda (NEXT), y 151 sta (SP), y 152 iny 153 lda (NEXT), y 154 sta (SP), y 155 iny 156 157 ; make the next task the current one 158 159 .invoke mov16 NEXT, current_task 160 161 exit_handler: 162 163 ; restore zero-page locations used in the handler 164 165 .invoke pull16 CURRENT 166 .invoke pull16 NEXT 167 .invoke pull16 SP 168 169 pla 170 tay ; stack -> Y 171 pla 172 tax ; stack -> X 173 pla ; stack -> A 174 jmp (old_handler) 175 176 177 178 ; location of previous interrupt handler 179 180 old_handler: .word 0 181 182 ; location of current task (duplicated from the table) 183 184 current_task: .word 0 185 186 ; offset of current task in table (in multiples of 2) 187 188 task_offset: .byte 0 189 190 ; task table containing locations of each task 191 192 tasks: 193 .word 0 194 .word 0 195 .word 0 196 .word 0 197 .word 0 198 199 200 201 ; add a new task to the table 202 ; 203 ; $76, $77: location of task structure 204 ; affects: A, Y 205 ; returns: A = 0 (success); A = 1 (failure) 206 207 new_task: 208 209 ; check the MSB of each task table entry 210 211 ldy #1 212 lda tasks, y 213 cmp #0 214 beq add_new_task 215 iny 216 iny 217 218 ; if no space is found by the end of the table, exit 219 220 cpy #TASK_TABLE_LENGTH 221 bpl no_new_task 222 223 add_new_task: 224 225 ; copy the task structure location to the table 226 227 lda $77 228 sta tasks, y 229 dey 230 lda $76 231 sta tasks, y 232 233 lda #0 234 rts 235 236 no_new_task: 237 lda #1 238 rts 239 240 241 242 ; example tasks 243 244 first_task: 245 .byte 0 ; currently unused 246 .byte 0 ; currently unused 247 .byte 0 ; currently unused 248 .byte 0 ; saved flags 249 .word first_task_start ; saved PC 250 first_task_start: 251 .invoke add16 1, $7000 252 jmp first_task_start 253 254 255 256 second_task: 257 .byte 0 ; currently unused 258 .byte 0 ; currently unused 259 .byte 0 ; currently unused 260 .byte 0 ; saved flags 261 .word second_task_start ; saved PC 262 second_task_start: 263 .invoke add16 1, $7008 264 jmp second_task_start 265 266 ; vim: tabstop=4 expandtab shiftwidth=4