; ============================================================================= ; 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