Files
gameboy-rs/cpu_instrs/source/common/delay.s
Ajurna 918c9020b5 Add system-specific boot symbols and CPU instruction tests
Introduced symbol files for various Game Boy systems (CGB, AGB, MGB, SGB) to define boot sequences and functionalities. Included CPU instruction behavior tests, with detailed coverage of standard operations and interrupt handling. Added documentation for test execution and internal framework operations.
2025-05-02 17:33:07 +01:00

221 lines
5.7 KiB
ArmAsm

; Delays in cycles, milliseconds, etc.
; All routines are re-entrant (no global data). Routines never
; touch BC, DE, or HL registers. These ASSUME CPU is at normal
; speed. If running at double speed, msec/usec delays are half advertised.
; Delays n cycles, from 0 to 16777215
; Preserved: AF, BC, DE, HL
.macro delay ARGS n
.if n < 0
.printt "Delay must be >= 0"
.fail
.endif
.if n > 16777215
.printt "Delay must be < 16777216"
.fail
.endif
delay_ n&$FFFF, n>>16
.endm
; Delays n clocks, from 0 to 16777216*4. Must be multiple of 4.
; Preserved: AF, BC, DE, HL
.macro delay_clocks ARGS n
.if n # 4 != 0
.printt "Delay must be a multiple of 4"
.fail
.endif
delay_ (n/4)&$FFFF,(n/4)>>16
.endm
; Delays n microseconds (1/1000000 second)
; n can range from 0 to 4000 usec.
; Preserved: AF, BC, DE, HL
.macro delay_usec ARGS n
.if n < 0
.printt "Delay must be >= 0"
.fail
.endif
.if n > 4000
.printt "Delay must be <= 4000 usec"
.fail
.endif
delay_ ((n * 1048576 + 500000) / 1000000)&$FFFF,((n * 1048576 + 500000) / 1000000)>>16
.endm
; Delays n milliseconds (1/1000 second)
; n can range from 0 to 10000 msec.
; Preserved: AF, BC, DE, HL
.macro delay_msec ARGS n
.if n < 0
.printt "Delay must be >= 0"
.fail
.endif
.if n > 10000
.printt "Delay must be <= 10000 msec"
.fail
.endif
delay_ ((n * 1048576 + 500) / 1000)&$FFFF, ((n * 1048576 + 500) / 1000)>>16
.endm
; All the low/high quantities are to deal wla-dx's asinine
; restriction full expressions must evaluate to a 16-bit
; value. If the author ever rectifies this, all "high"
; arguments can be treated as zero and removed. Better yet,
; I'll just find an assembler that didn't crawl out of
; the sewer (this is one of too many bugs I've wasted
; hours working around).
.define max_short_delay 28
.macro delay_long_ ARGS n, high
; 0+ to avoid assembler treating as memory read
ld a,0+(((high<<16)+n) - 11) >> 16
call delay_65536a_9_cycles_
delay_nosave_ (((high<<16)+n) - 11)&$FFFF, 0
.endm
; Doesn't save AF, allowing minimization of AF save/restore
.macro delay_nosave_ ARGS n, high
; 65536+11 = maximum delay using delay_256a_9_cycles_
; 255+22 = maximum delay using delay_a_20_cycles
; 22 = minimum delay using delay_a_20_cycles
.if high > 1
delay_long_ n, high
.else
.if high*n > 11
delay_long_ n, high
.else
.if (high*(255+22+1))|n > 255+22
ld a,>(((high<<16)+n) - 11)
call delay_256a_9_cycles_
delay_nosave_ <(((high<<16)+n) - 11), 0
.else
.if n >= 22
ld a,n - 22
call delay_a_20_cycles
.else
delay_short_ n
.endif
.endif
.endif
.endif
.endm
.macro delay_ ARGS low, high
.if (high*(max_short_delay+1))|low > max_short_delay
push af
delay_nosave_ ((high<<16)+low - 7)&$FFFF, ((high<<16)+low - 7)>>16
pop af
.else
delay_short_ low
.endif
.endm
; Delays A cycles + overhead
; Preserved: BC, DE, HL
; Time: A+20 cycles (including CALL)
delay_a_20_cycles:
- sub 5 ; 2
jr nc,- ;3/2 do multiples of 5
rra ; 1
jr nc,+ ;3/2 bit 0
+ adc 1 ; 2
ret nc ;5/2 -1: 0 cycles
ret z ;5/2 0: 2 cycles
nop ; 1 1: 4 cycles
ret ; 4 (thanks to dclxvi for original algorithm)
; Delays A*256 cycles + overhead
; Preserved: BC, DE, HL
; Time: A*256+12 cycles (including CALL)
delay_256a_12_cycles:
or a ; 1
ret z ; 5/2
delay_256a_9_cycles_:
- delay 256-4
dec a ; 1
jr nz,- ;3/2
ret ; 4
; Delays A*65536 cycles + overhead
; Preserved: BC, DE, HL
; Time: A*65536+12 cycles (including CALL)
delay_65536a_12_cycles:
or a ; 1
ret z ;5/2
delay_65536a_9_cycles_:
- delay 65536-4
dec a ; 1
jr nz,- ;3/2
ret ; 4
; Delays H*256+L cycles + overhead
; Preserved: AF, BC, DE, HL
; Time: H*256+L+51 cycles
delay_hl_51_cycles:
push af
ld a,h
call delay_256a_12_cycles
ld a,l
call delay_a_20_cycles
pop af
ret
; delay_short_ macro calls into these
.ds max_short_delay-10,$00 ; NOP repeated several times
delay_unrolled_:
ret
.macro delay_short_ ARGS n
.if n < 0
.fail
.endif
.if n > max_short_delay
.fail
.endif
.if n == 1
nop
.endif
.if n == 2
nop
nop
.endif
.if n == 3
.byte $18,$00 ; JR +0
.endif
.if n == 4
.byte $18,$00 ; JR +0
nop
.endif
.if n == 5
.byte $18,$00 ; JR +0
nop
nop
.endif
.if n == 6
.byte $18,$00 ; JR +0
.byte $18,$00 ; JR +0
.endif
.if n == 7
push af
pop af
.endif
.if n == 8
push af
pop af
nop
.endif
.if n == 9
push af
pop af
nop
nop
.endif
.if n >= 10
call delay_unrolled_ + 10 - n
.endif
.endm