; Delays in CPU clocks, milliseconds, etc. All routines are re-entrant
; (no global data). No routines touch X or Y during execution.
; Code generated by macros is relocatable; it contains no JMPs to itself.

zp_byte delay_temp_ ; only written to

; Delays n clocks, from 2 to 16777215
; Preserved: A, X, Y, flags
.macro delay n
    .if (n) < 0 .or (n) = 1 .or (n) > 16777215
	.error "Delay out of range"
    .endif
    delay_ (n)
.endmacro


; Delays n milliseconds (1/1000 second)
; n can range from 0 to 1100.
; Preserved: A, X, Y, flags
.macro delay_msec n
    .if (n) < 0 .or (n) > 1100
	.error "time out of range"
    .endif
    delay ((n)*CLOCK_RATE+500)/1000
.endmacro


; Delays n microseconds (1/1000000 second).
; n can range from 0 to 100000.
; Preserved: A, X, Y, flags
.macro delay_usec n
    .if (n) < 0 .or (n) > 100000
	.error "time out of range"
    .endif
    delay ((n)*((CLOCK_RATE+50)/100)+5000)/10000
.endmacro

.align 64

; Delays A clocks + overhead
; Preserved: X, Y
; Time: A+25 clocks (including JSR)
:       sbc #7          ; carry set by CMP
delay_a_25_clocks:
	cmp #7
	bcs :-          ; do multiples of 7
	lsr a           ; bit 0
	bcs :+
:                       ; A=clocks/2, either 0,1,2,3
	beq @zero       ; 0: 5
	lsr a
	beq :+          ; 1: 7
	bcc :+          ; 2: 9
@zero:  bne :+          ; 3: 11
:       rts             ; (thanks to dclxvi for the algorithm)


; Delays A*256 clocks + overhead
; Preserved: X, Y
; Time: A*256+16 clocks (including JSR)
delay_256a_16_clocks:
	cmp #0
	bne :+
	rts
delay_256a_11_clocks_:
:       pha
	lda #256-19-22
	jsr delay_a_25_clocks
	pla
	clc
	adc #$FF
	bne :-
	rts


; Delays A*65536 clocks + overhead
; Preserved: X, Y
; Time: A*65536+16 clocks (including JSR)
delay_65536a_16_clocks:
	cmp #0
	bne :+
	rts
delay_65536a_11_clocks_:
:       pha
	lda #256-19-22-13
	jsr delay_a_25_clocks
	lda #255
	jsr delay_256a_11_clocks_
	pla
	clc
	adc #$FF
	bne :-
	rts

max_short_delay = 41

	; delay_short_ macro jumps into these
	.res (max_short_delay-12)/2,$EA ; NOP
delay_unrolled_:
	rts

.macro delay_short_ n
    .if n < 0 .or n = 1 .or n > max_short_delay
	.error "Internal delay error"
    .endif
    .if n = 0
    	; nothing
    .elseif n = 2
	nop
    .elseif n = 3
	sta <delay_temp_
    .elseif n = 4
	nop
	nop
    .elseif n = 5
	sta <delay_temp_
	nop
    .elseif n = 6
	nop
	nop
	nop
    .elseif n = 7
	php
	plp
    .elseif n = 8
	nop
	nop
	nop
	nop
    .elseif n = 9
	php
	plp
	nop
    .elseif n = 10
	sta <delay_temp_
	php
	plp
    .elseif n = 11
	php
	plp
	nop
	nop
    .elseif n = 13
	php
	plp
	nop
	nop
	nop
    .elseif n & 1
	sta <delay_temp_
	jsr delay_unrolled_-((n-15)/2)
    .else
	jsr delay_unrolled_-((n-12)/2)
    .endif
.endmacro

.macro delay_nosave_ n
    ; 65536+17 = maximum delay using delay_256a_11_clocks_
    ; 255+27   = maximum delay using delay_a_25_clocks
    ; 27       = minimum delay using delay_a_25_clocks
    .if n > 65536+17
	lda #^(n - 15)
	jsr delay_65536a_11_clocks_
	; +2 ensures remaining clocks is never 1
	delay_nosave_ (((n - 15) & $FFFF) + 2)
    .elseif n > 255+27
	lda #>(n - 15)
	jsr delay_256a_11_clocks_
	; +2 ensures remaining clocks is never 1
	delay_nosave_ (<(n - 15) + 2)
    .elseif n >= 27
	lda #<(n - 27)
	jsr delay_a_25_clocks
    .else
	delay_short_ n
    .endif
.endmacro

.macro delay_ n
    .if n > max_short_delay
	php
	pha
	delay_nosave_ (n - 14)
	pla
	plp
    .else
	delay_short_ n
    .endif
.endmacro

