aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/boot.asm14
-rw-r--r--src/kdata.asm2
-rw-r--r--src/sched.asm12
-rw-r--r--src/syscall.asm9
4 files changed, 31 insertions, 6 deletions
diff --git a/src/boot.asm b/src/boot.asm
index 88eb240..f649008 100644
--- a/src/boot.asm
+++ b/src/boot.asm
@@ -44,6 +44,20 @@ KernelInit:
push af ; boot ROM hands the console model in A/B -
push bc ; park it on the (uncleared) stack for dmesg
+ ; CGB double-speed mode: 8.4 MHz for the whole kernel + userland. The
+ ; PPU/LCD keep their normal rate; the timer and DIV double with the CPU,
+ ; which TimerISR (128 Hz -> 64 Hz toggle) and sys_sleep (DIV halving)
+ ; compensate for. P1 select lines off first, per the hardware procedure.
+ ldh a, [$FF4D] ; KEY1
+ bit 7, a ; already fast? (warm reboot)
+ jr nz, .fast
+ ld a, $30
+ ldh [$FF00], a ; P1: no select lines (STOP quirk guard)
+ ld a, 1
+ ldh [$FF4D], a ; KEY1.0 = prepare speed switch
+ stop ; switch: continues in double speed
+.fast
+
; enable cart RAM (MBC5)
ld a, $0A
ld [MBC5_RAM_ENABLE], a
diff --git a/src/kdata.asm b/src/kdata.asm
index b0cd71b..13dc6dd 100644
--- a/src/kdata.asm
+++ b/src/kdata.asm
@@ -66,3 +66,5 @@ wBootA:: DS 1 ; A at entry: console model from the boot ROM
wBootB:: DS 1 ; B at entry: bit0 set = AGB (GBA in CGB mode)
wBootFsFresh::DS 1 ; fs_init formatted a blank disk this boot
wTicks:: DS 4 ; monotonic 64 Hz system tick (TimerISR), LE
+wTickTog:: DS 1 ; TimerISR fires at 128 Hz in double-speed mode;
+ ; this toggle keeps wTicks counting at 64 Hz
diff --git a/src/sched.asm b/src/sched.asm
index 0369ff4..b4b5a30 100644
--- a/src/sched.asm
+++ b/src/sched.asm
@@ -67,9 +67,10 @@ FindNextReady::
ret
; -----------------------------------------------------------------------------
-; TimerISR - the system tick. TAC runs the timer at 16384 Hz with TMA=0, so
-; TIMA overflows (and this fires) at 64 Hz: wTicks is a monotonic 32-bit
-; 1/64-second counter (SYS_UPTIME reads it; wraps after ~2.1 years).
+; 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.
@@ -77,6 +78,11 @@ FindNextReady::
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
diff --git a/src/syscall.asm b/src/syscall.asm
index e3f0b1b..4bcb601 100644
--- a/src/syscall.asm
+++ b/src/syscall.asm
@@ -277,8 +277,10 @@ ENDR
; -----------------------------------------------------------------------------
; sys_sleep(B = 1/64-second units) - cooperative delay driven by the DIV timer.
-; DIV (FF04) free-runs at 16384 Hz, so 256 ticks = 1/64 s. We accumulate DIV
-; deltas across SchedYields (other procs run) until enough units have elapsed.
+; DIV free-runs at 16384 Hz - 32768 Hz in double-speed mode, where 256 ticks
+; = 1/128 s - so the accumulated high byte is halved before comparing against
+; the target. We accumulate DIV deltas across SchedYields (other procs run)
+; until enough 1/64-second units have elapsed.
; -----------------------------------------------------------------------------
sys_sleep:
ld a, b
@@ -305,7 +307,8 @@ sys_sleep:
ld [wSleepAcc], a
ld a, [wSleepAcc+1]
adc 0
- ld [wSleepAcc+1], a ; acc high byte = elapsed 1/64-second units
+ ld [wSleepAcc+1], a ; acc high byte = elapsed 1/128-second units
+ srl a ; /2: double-speed DIV -> 1/64-second units
ld b, a
ld a, [wSleepTarget]
cp b