-
Notifications
You must be signed in to change notification settings - Fork 0
/
scheduler.asm
259 lines (189 loc) · 4.79 KB
/
scheduler.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
; IRQ steps
; Push to the stack:
;
; PB - Program Bank - 1 byte
; PCH - Program Counter High - 1 byte
; PCL - Program Counter Low - 1 byte
; SR - Status Register - 1 byte
;
; PB set to $00
; Stacks and direct page
; 0000-00FF : kernel direct page
; 0100-01FF : kernel stack
; 9000-90FF : task 0 - direct
; A000-A0FF : task 0 - stack
; 9F00-9FFF : task 16 - direct
; AF00-AFFF : task 16 - stack
; B000-C000 : I/O
InterruptStackY = 2+1
InterruptStackX = InterruptStackY+2
InterruptStackA = InterruptStackX+2
InterruptDP = InterruptStackA+2
InterruptDB = InterruptDP+2
InterruptStatusRegister = InterruptDB+1
InterruptPC = InterruptStatusRegister+1
InterruptPB = InterruptPC+2
.SEGMENT "KERNEL"
KERNEL_SCHEDULER_TASK_SWITCHES: .res 2
TempStackReturnBank: .res 1
TempStackReturnPC: .res 2
NextTaskId: .res 2
.code
.A8
.I8
Scheduler_NextTask:
inc KERNEL_SCHEDULER_TASK_SWITCHES
BNE @lowcntSwitch ; Branch to end if the low byte didn't roll over to 00.
inc KERNEL_SCHEDULER_TASK_SWITCHES+1
@lowcntSwitch:
; DEBUG PRINT SERIAL
LDA #'T'
jsr SerialPutC
; END: DEBUG PRINT SERIAL
ldx ActiveTask
; DEBUG
txa
clc
adc #$30
JSR SerialPutC
; END DEBUG
lda TaskStatus,x
cmp #TASK_STATUS_RUNNING
bne @notrunnable
; Save the current task state
lda #TASK_STATUS_RUNNABLE ; if running then set to runnable
sta TaskStatus,x
@notrunnable:
lda InterruptDB,s
sta TaskDataBank,x ; save Data Bank
lda InterruptPB,s
sta TaskProgramBank,x ; save Program Bank
lda InterruptStatusRegister,s
and #%11111011 ; make sure interrupt disable is not set
sta TaskStatusRegister,x
txa
asl
tax ; double the x for 2 byte indexes
; SAVE STACK POINTER
longa
tsc ; A = stack pointer
clc
adc #InterruptPB ; A = stack pointer - ...
sta TaskStackPointer,x
lda InterruptStackA,s
sta TaskA,x
lda InterruptStackX,s
sta TaskX,x
lda InterruptStackY,s
sta TaskY,x
lda InterruptPC,s
sta TaskProgramPointer,x
shorta
ldx ActiveTask ; X is the task being interrupted
@loop:
inx
cpx #NUMBER_OF_TASKS
bne @fine
ldx #$FF ; will roll to 0 on inx
jmp @loop
@fine:
lda TaskStatus,x
beq @loop ; 0 - means no task on this index
cmp #TASK_STATUS_RUNNABLE
beq @task_switch
cmp #TASK_STATUS_RUNNING
beq @goreturn ; some task is already running. Should not happen
jmp @loop
@goreturn:
jmp @return
@task_switch:
; SWITCH TO NEW TASK
stx ActiveTask
; DEBUG
lda #'N'
JSR SerialPutC
txa
clc
adc #$30
JSR SerialPutC
; END DEBUG
lda #TASK_STATUS_RUNNING ; if running then set to runnable
sta TaskStatus,x
lda 1,s
sta TempStackReturnPC+1
lda 2,s
sta TempStackReturnPC
lda 3,s
sta TempStackReturnBank
; Set up stack
txa
asl
tax
longr
lda TaskStackPointer,x
clc
sbc #InterruptPB
tcs
shortr
lda TempStackReturnPC+1
sta 1,s
lda TempStackReturnPC
sta 2,s
lda TempStackReturnBank
sta 3,s
ldx ActiveTask
lda TaskProgramBank,x
sta InterruptPB,s
lda TaskDataBank,x
sta InterruptDB,s
lda TaskStatusRegister,x
and #%11111011
sta InterruptStatusRegister,s
txa
asl
tax
; Set Direct Page to $9x00
lda ActiveTask
clc
adc #$90 ; A = $9x
sta InterruptDP+1,s
; Set registers
lda TaskA,x
sta InterruptStackA,s
lda TaskA+1,x
sta InterruptStackA+1,s
lda TaskY,x
sta InterruptStackY,s
lda TaskY+1,x
sta InterruptStackY+1,s
lda TaskX,x
sta InterruptStackX,s
lda TaskX+1,x
sta InterruptStackX+1,s
lda TaskProgramPointer+1,x
sta InterruptPC+1,s
lda TaskProgramPointer,x
sta InterruptPC,s
jmp @return
@return:
rts
.A16
.I16
InitScheduler:
stz KERNEL_SCHEDULER_TASK_SWITCHES ; set task switch count to 0
lda #$0100
sta NextTaskId
; should be approx 256 times per second
;lda #$9896
lda #$FFFF
sta VIA1_T1CL
shortr
lda VIA1_ACR ; Clear the ACR's bit that
AND #%01111111 ; tells T1 to toggle PB7 upon time-out, and
ORA #%01000000 ; set the bit that tells T1 to automatically
STA VIA1_ACR ; produce an interrupt at every time-out and
; just reload from the latches and keep going.
LDA #%11000000
STA VIA1_IER ; Enable the T1 interrupt in the VIA.
longr
rts