; Bresenham's like circle algorithm
; best known as Middle Point Circle drawing algorithm

#include once <error.asm>
#include once <plot.asm>
#include once <in_screen.asm>

; Draws a circle at X, Y of radius R
; X, Y on the Stack, R in accumulator (Byte)

    push namespace core

    PROC
    LOCAL __CIRCLE_ERROR
    LOCAL __CIRCLE_LOOP
    LOCAL __CIRCLE_NEXT

__CIRCLE_ERROR:
    jp __OUT_OF_SCREEN_ERR
CIRCLE:
    ;; Entry point
    pop hl    ; Return Address
    pop de    ; D = Y
    ex (sp), hl ; __CALLEE__ convention
    ld e, h ; E = X
    ld h, a ; H = R

#ifdef SCREEN_Y_OFFSET
    ld a, SCREEN_Y_OFFSET
    add a, d
    ld d, a
#endif

#ifdef SCREEN_X_OFFSET
    ld a, SCREEN_X_OFFSET
    add a, e
    ld e, a
#endif

    ld a, h
    add a, d
    sub 192
    jr nc, __CIRCLE_ERROR

    ld a, d
    sub h
    jr c, __CIRCLE_ERROR

    ld a, e
    sub h
    jr c, __CIRCLE_ERROR

    ld a, h
    add a, e
    jr c, __CIRCLE_ERROR


; __FASTCALL__ Entry: D, E = Y, X point of the center
; A = Radious
__CIRCLE:
    push de
    ld a, h
    exx
    pop de        ; D'E' = x0, y0
    ld h, a       ; H' = r

    ld c, e
    ld a, h
    add a, d
    ld b, a
    call __CIRCLE_PLOT    ; PLOT (x0, y0 + r)

    ld b, d
    ld a, h
    add a, e
    ld c, a
    call __CIRCLE_PLOT    ; PLOT (x0 + r, y0)

    ld c, e
    ld a, d
    sub h
    ld b, a
    call __CIRCLE_PLOT ; PLOT (x0, y0 - r)

    ld b, d
    ld a, e
    sub h
    ld c, a
    call __CIRCLE_PLOT ; PLOT (x0 - r, y0)

    exx
    ld b, 0        ; B = x = 0
    ld c, h        ; C = y = Radius
    ld hl, 1
    or a
    sbc hl, bc    ; HL = f = 1 - radius

    ex de, hl
    ld hl, 0
    or a
    sbc hl, bc  ; HL = -radius
    add hl, hl    ; HL = -2 * radius
    ex de, hl    ; DE = -2 * radius = ddF_y, HL = f

    xor a        ; A = ddF_x = 0
    ex af, af'    ; Saves it

__CIRCLE_LOOP:
    ld a, b
    inc a
    cp c
    ret nc        ; Returns when x >= y

    bit 7, h    ; HL >= 0? : if (f >= 0)...
    jp nz, __CIRCLE_NEXT

    dec c        ; y--
    inc de
    inc de        ; ddF_y += 2

    add hl, de    ; f += ddF_y

__CIRCLE_NEXT:
    inc b        ; x++
    ex af, af'
    add a, 2    ; 1 Cycle faster than inc a, inc a

    inc hl        ; f++
    push af
    add a, l
    ld l, a
    ld a, h
    adc a, 0    ; f = f + ddF_x
    ld h, a
    pop af
    ex af, af'

    push bc
    exx
    pop hl        ; H'L' = Y, X

    ld a, d
    add a, h
    ld b, a        ; B = y0 + y
    ld a, e
    add a, l
    ld c, a        ; C = x0 + x
    call __CIRCLE_PLOT ; plot(x0 + x, y0 + y)

    ld a, d
    add a, h
    ld b, a        ; B = y0 + y
    ld a, e
    sub l
    ld c, a        ; C = x0 - x
    call __CIRCLE_PLOT ; plot(x0 - x, y0 + y)

    ld a, d
    sub h
    ld b, a        ; B = y0 - y
    ld a, e
    add a, l
    ld c, a        ; C = x0 + x
    call __CIRCLE_PLOT ; plot(x0 + x, y0 - y)

    ld a, d
    sub h
    ld b, a        ; B = y0 - y
    ld a, e
    sub l
    ld c, a        ; C = x0 - x
    call __CIRCLE_PLOT ; plot(x0 - x, y0 - y)

    ld a, l
    cp h
    jr z, 1f

    ld a, d
    add a, l
    ld b, a        ; B = y0 + x
    ld a, e
    add a, h
    ld c, a        ; C = x0 + y
    call __CIRCLE_PLOT ; plot(x0 + y, y0 + x)

    ld a, d
    add a, l
    ld b, a        ; B = y0 + x
    ld a, e
    sub h
    ld c, a        ; C = x0 - y
    call __CIRCLE_PLOT ; plot(x0 - y, y0 + x)

    ld a, d
    sub l
    ld b, a        ; B = y0 - x
    ld a, e
    add a, h
    ld c, a        ; C = x0 + y
    call __CIRCLE_PLOT ; plot(x0 + y, y0 - x)

    ld a, d
    sub l
    ld b, a        ; B = y0 - x
    ld a, e
    sub h
    ld c, a        ; C = x0 + y
    call __CIRCLE_PLOT ; plot(x0 - y, y0 - x)

1:
    exx
    jp __CIRCLE_LOOP



__CIRCLE_PLOT:
    ; Plots a point of the circle, preserving HL and DE
    push hl
    push de
    call __PLOT
    pop de
    pop hl
    ret

    ENDP

    pop namespace
