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
|
; =============================================================================
; sched.asm - round-robin scheduler + yield + timer stub
; =============================================================================
INCLUDE "include/gbos.inc"
SECTION "sched", ROM0
; -----------------------------------------------------------------------------
; SchedInit - prime the round-robin cursor so the first pick lands on slot 0.
; -----------------------------------------------------------------------------
SchedInit::
ld a, MAX_PROCS - 1
ld [wSchedNext], a
ret
; -----------------------------------------------------------------------------
; SchedRunFirst - hand control to the first runnable task. Never returns; the
; kernel-init context is saved into wKernelPCB and simply abandoned.
; -----------------------------------------------------------------------------
SchedRunFirst::
ld hl, wKernelPCB
ld a, l
ld [wCurProc], a
ld a, h
ld [wCurProc+1], a
call FindNextReady ; DE = &first ready PCB
jp hSwitchTo
; -----------------------------------------------------------------------------
; SchedYield - cooperative switch to the next runnable task. Returns to the
; caller when this task is eventually rescheduled.
; -----------------------------------------------------------------------------
SchedYield::
call FindNextReady ; DE = &next PCB, CF if none runnable
ret c ; nobody else ready: keep running current
call hSwitchTo
ret
; -----------------------------------------------------------------------------
; FindNextReady - round-robin from wSchedNext.
; out: DE = &PCB and CF clear on success; CF set if nothing is PS_READY.
; -----------------------------------------------------------------------------
FindNextReady::
ld a, [wSchedNext]
ld b, MAX_PROCS
.scan
inc a
cp MAX_PROCS
jr c, .nowrap
xor a
.nowrap
push af ; save candidate slot
call PcbPtr ; DE = &PCB[slot] (preserves BC)
ld a, [de] ; state
cp PS_READY
jr z, .found
pop af ; restore candidate slot
dec b
jr nz, .scan
scf ; nothing runnable
ret
.found
pop af ; A = chosen slot
ld [wSchedNext], a
call PcbPtr ; DE = &PCB
and a ; clear carry
ret
; -----------------------------------------------------------------------------
; TimerISR - the system tick. TAC runs the timer at 16384 Hz with TMA=0 -
; but the CPU is in DOUBLE-SPEED mode, so the input clock is really 32768 Hz
; and this fires at 128 Hz. A toggle halves that back down: wTicks stays a
; monotonic 32-bit 1/64-second counter (SYS_UPTIME reads it; wraps ~2.1 yr).
; Scheduling stays cooperative - this ISR is deliberately transparent (all
; registers preserved, fixed-WRAM only), so it can interrupt anything,
; including kernel code mid-syscall. Preemption would hook in here later.
; -----------------------------------------------------------------------------
TimerISR::
push af
push hl
ld hl, wTickTog
ld a, [hl]
xor 1
ld [hl], a
jr nz, .done ; first IRQ of the 128 Hz pair: count nothing
ld hl, wTicks
inc [hl]
jr nz, .done ; no carry out of byte 0
inc hl
inc [hl]
jr nz, .done
inc hl
inc [hl]
jr nz, .done
inc hl
inc [hl]
.done
pop hl
pop af
reti
|