;-------------------------
;| Pool Table v1.0       |
;| Logan Bauer  2/12/96  |
;-------------------------

#include "ti-85.h"

; Text Memmory is from $80DF to $8187
temp         = $80DF       ; 2 bytes at Start of Text Memmory
ZCPdata      = $80E1       ; Used for ZCP decoder
ZCPcount     = $80E2       ; Used for ZCP decoder
ballCounter  = $80E3       ; To keep track of current animation ball
bits         = $80E4       ; When ya' just need some bits
B_x          = $80E5       ; 16 bytes for dynamic ball x table
B_y          = $80F5       ; 16 bytes for dynamic ball y table
E_x          = $8105       ; 16 bytes for last ball x table (erase)
E_y          = $8115       ; 16 bytes for last ball y table (erase)
V_x          = $8125       ; 16 bytes for ball x velocity
V_y          = $8135       ; 16 bytes for ball y velocity
Dampers      = $8145       ; 16 bytes for ball dampers
BallBits     = $8155       ; 16 bytes to store Info about eachballs
TheBall      = $8165       ; To choose the black ball
saveSP       = $8166       ; To hold the Stack Pointer
Aim_Cnt      = $8168       ; To keep track of where your'e aiming
Aim_x        = $8159       ; Current Aim X
Aim_y        = $815A       ; Current Aim Y

;-----------------  Start of program.  ------------------

.org  0
Title: .db   "Pool  Table  v1.0",0

Start:
    ld      (saveSP), sp
    ld      a,4
    out     (5),a               ; Set page to 4 (Graphics)

    ld      a,1
    ld      (ballCounter),a     ; For ball animation

    ld      hl, (PROGRAM_ADDR)  ; --- Used to set up default ball locations ---
    ld      de, Ball_x
    add     hl, de              ; Now hl has loc of Ball_x table
    ld      de, B_x             ; DE now has dynamic table
    ld      bc, 32              ; Copy 32 bytes (x tables and y tables)
    ldir                        ; Copy Default ball table into temp table
    ld      hl, B_x             ; now copy to the erase tables
    ld      de, E_x             ; erase table location
    ld      bc, 32              ; both x and y erase tables
    ldir                        ; Copy ball table into ball erase table

    ROM_CALL(CLEARLCD)
    CALL_(DispPoolTable)            ; Display Pool Table (ZCP Encoded)
    CALL_(DrawAllBalls)             ; Draw balls on the table

;--------------------------- M A I N   P R O G R A M ---------------------------



Main:                                   ; JUST TEMPORARY
    CALL_(Aim)
    jr      Main


WarpOut:
    ld      hl, ZS_BITS
    set     1, (hl)                     ; Get out without screen
FastQuit:
    ld      hl, (saveSP)                ; Load saved pos of stack
    ld      sp, hl                      ; set it to new pos (beg. of prog)
    ret                                 ; go to that pos. (ZShell)


Aim:
    push    af
    push    de
    push    hl
    ld      a, (Aim_Cnt)                ; d = this aim offset
AimLoop:
    ld      a, (Aim_Cnt)
    CALL_(AimInvertBox)
AimKey:
    CALL_(Key)
    or      a
    jr      z, AimKey
    ld      h, a                        ; Save key
    ld      a, (Aim_Cnt)                ; Erase Box for e
    CALL_(AimInvertBox)
    ld      a, h                        ; restore key
    cp      $09         ;ENTER
    jr      z, AimDone                  ; if key is enter then done
    cp      $03         ;RIGHT
    CALL_Z(AimNext)                     ; if key is "->" then inc d
    cp      $02         ;LEFT
    CALL_Z(AimPrev)                     ; if key is "<-" then dec d
    cp      $38         ;MORE
    CALL_Z(MoveBalls)
    jr      AimLoop
AimDone:                                ;ENTER PRESSED
    ld      hl, (PROGRAM_ADDR)
    ld      de, Aim_XS                  ; load the velocity table
    add     hl, de
    ld      a, (Aim_Cnt)                ; get current offset
    CALL_(AddAtoHL)                     ; now HL points to correct pos
    ld      a, (hl)
    ld      (Aim_x), a
    push    de
    ld      de, 16
    add     hl, de
    pop     de
    ld      a, (hl)
    ld      (Aim_y), a
    pop     hl
    pop     de
    pop     af
    CALL_(Shoot)
    ret

AimNext:                                ;Circular increment of d
    push    af                          ; save A (only A used)
    ld      a, (Aim_Cnt)                ; load D into A for comparison
    inc     a                           ; inc A (D)  [if -1 then = 0]
    cp      16                          ; see if A (D) is 15
    jr      nz, ANmore                  ; if not then just inc D
    ld      a, 0                        ; is 15 so put -1 into a (0 - 1)
ANmore:
    ld      (Aim_Cnt), a                ; put it back
    pop     af
    ret                                 ; Done

AimPrev:                                ;Circular decrement of D
    push    af
    ld      a, (Aim_Cnt)                ; load D into A (for compares)
    dec     a                           ; dec A (D) [if 16 then = 15]
    cp     -1                           ; is A (D) = 0
    jr      nz, APmore                  ; if not then just dec D
    ld      a, 15                       ; is = 0 so put 16 into A (15 + 1)
APmore:
    ld      (Aim_Cnt), a                ; restore D
    pop     af
    ret                                 ; Done

AimInvertBox:                           ;takes A as current ball
    push    bc
    push    de
    push    hl
    ld      hl, (PROGRAM_ADDR)
    ld      de, Aim_XT                  ; Get Visual aim table
    add     hl, de
    CALL_(AddAtoHL)
    ld      a, (B_x)                    ; get the Cue-ball x pos
    add     a, (hl)                     ; add item A in table with x pos
    ld      b, a                        ; save that in B
    push    de
    ld      de, 16
    add     hl, de
    pop     de
    ld      a, (B_y)                    ; get Cue-ball y pos
    add     a, (hl)                     ; add item A in table with y pos
    ld      c, a                        ; save that om C
    CALL_(Pixel_Change)
    inc     b
    CALL_(Pixel_Change)
    dec     c
    CALL_(Pixel_Change)
    dec     b
    CALL_(Pixel_Change)
    pop     hl
    pop     de
    pop     bc
    ret                                 ; Done


Shoot:
    push    af
    push    hl
    CALL_(ResetVeloc)
    CALL_(ResetDampers)
    ld      a, (Aim_x)
    ld      (V_x), a
    ld      a, (Aim_y)
    ld      (V_y), a
ShootLoop:
    CALL_(DampenBalls)
    CALL_(ChkCollision)         ; fixes collisions with balls
    CALL_(CheckWalls)           ; Checks walls AND adds velocities (faster)
    CALL_(DrawAllBalls)         ; erases old balls and draws new ones
    CALL_(Key)
    cp      $09         ; ENTER          ; pressed enter then pause
    CALL_Z(pause)
    cp      $38         ; MORE
    jr      z, ShootDone
    CALL_(CheckMoving)
    or      a
    jr      nz, ShootLoop
ShootDone:
    CALL_(ResetVeloc)
    ld      hl, BallBits        ; item 0 of ballbits (cue-ball)
    bit     0, (hl)             ; bit zero is "pocketed" balls (scratch)
    CALL_NZ(Scratch)            ; Put Cue Ball back
    CALL_(DrawAllBalls)
    pop     hl
    pop     af
    ret

ResetVeloc:
    push    bc
    push    hl
    ld      hl, V_x
    ld      a, 0
    ld      b, 32
RVloop:
    ld      (hl), a
    inc     hl
    djnz    RVloop
    pop     hl
    pop     bc
    ret

ResetDampers:
    ld      hl, dampers
    ld      a, 60
    ld      b, 16
RDloop:
    ld      (hl), a
    inc     hl
    sub     5
    djnz    RDloop
    ret

Scratch:                                ; Reset Cue-ball after a shot
    push    hl
    ld      hl, BallBits
    res     0, (hl)                     ; bit 0 is "pocketed" balls (set to off)
    ld      hl, (PROGRAM_ADDR)
    ld      de, Ball_X                  ; get default ball x table
    add     hl, de                      ; now HL is pos of default ball x pos
    ld      a, (hl)
    ld      (B_x), a                    ; Dynamic ball pos (cue ball)
    push    de
    ld      de, 16
    add     hl, de                      ; now HL points to default ball y pos
    pop     de
    ld      a, (hl)
    ld      (B_y), a
    pop     hl
    ret


Pause:                                  ; WAITS UNTIL USER PRESSES ENTER
    push    af
    push    hl
    ROM_CALL(BUSY_ON)                   ; So you can see it's paused
PauseLoop:
    CALL_(Key)
    cp      $09                         ; ENTER
    jr      nz,PauseLoop
    ROM_CALL(BUSY_OFF)                  ; turn it off
    pop     hl
    pop     af
    ret

MoveBalls:
    push    af
    push    bc
    xor     a
    ld      (TheBall), a
    CALL_(GetBallLoc)
    CALL_(DrawAllBalls)
MoveLoop:
    CALL_(GetArrow)
    ld      a, (temp)
    cp      $09
    jr      z, MoveDone
    cp      $38
    CALL_Z(NextBall)
    ld      a, (TheBall)
    CALL_(SetBallLoc)
    CALL_(DrawAllBalls)
    jr      MoveLoop
MoveDone:
    xor     a
    ld      (TheBall), a
    CALL_(DrawAllBalls)
    pop     bc
    pop     af
    ret

NextBall:
    push    af
    push    hl
    ld      a, (TheBall)
NBloop:
    inc     a
    cp      16
    jr      nz, NBmore
    ld      a, 0
NBmore:
    CALL_(CheckPocketed)
    jr      nz, NBloop
    ld      (TheBall), a
    CALL_(GetBallLoc)
    pop     hl
    pop     af
    CALL_(DrawAllBalls)
    ret

GetBallLoc:
    push    hl
    ld      hl, B_x
    CALL_(AddAtoHL)
    ld      b, (hl)
    ld      hl, B_y
    CALL_(AddAtoHL)
    ld      c, (hl)
    pop     hl
    ret

SetBallLoc:
    push    hl
    ld      hl, B_x
    CALL_(AddAtoHL)
    ld      (hl), b
    ld      hl, B_y
    CALL_(AddAtoHL)
    ld      (hl), c
    pop     hl
    ret


GetArrow:
    CALL_(Key)
    or      a
    jr      z, GetArrow
    ld      (temp), a
    cp      $09                         ; ENTER
    ret     z
    cp      $38                         ; MORE
    ret     z
    cp      $04                         ; UP ARROW
    jr      z, IncC
    cp      $01                         ; DOWN ARROW
    jr      z, DecC
    cp      $02                         ; LEFT ARROW
    jr      z, DecB
    cp      $03                         ; RIGHT ARROW
    jr      z, IncB
    jr      GetArrow
IncC:
    ld      a, c
    cp      58
    ret     z
    inc     c
    ret
DecC:
    ld      a, c
    cp      15
    ret     z
    dec     c
    ret
IncB:
    ld      a, b
    cp      118
    ret     z
    inc     b
    ret
DecB:
    ld      a, b
    cp      5
    ret     z
    dec     b
    ret


CheckMoving:                    ; a = 0 if not moving, a > 0 if balls moving
    push    bc
    push    de
    push    hl
    ld      de, $0000
    ld      b, 32
    ld      hl, V_x
CMloop:
    ld      a, (hl)
    or      a
    jr      nz, CMisMoving
    inc     hl
    djnz    CMloop
CMisMoving:
    pop     hl
    pop     de
    pop     bc
    ret


Key:
    push    hl
    call    GET_KEY
    pop     hl
    cp      $37         ; EXIT
    JUMP_Z(FastQuit)
    cp      $0F         ; CLEAR
    JUMP_Z(WarpOut)
    cp      $0A         ; PLUS (+)
    JUMP_Z(Contrast_Up)
    cp      $0B         ; MINUS (-)
    JUMP_Z(Contrast_Dn)
    ret


ClearBottom:
    push    bc
    ld      bc, $3C06
CBloop:
    CALL_(Pixel_Off)
    djnz    CBloop
    ld      b, $3C
    dec     c
    jr      nz, CBloop
    pop     bc
    ret


;---------------------------- Ball handeling stuff -----------------------------

ChkCollision:                       ; Fixes all collisions with balls
    push    bc
    push    de
    push    hl
    xor     a                       ; a = 0  (this is just fast and small)
    ld      d, a                    ; d = 0  (ball counter 1)
    ld      e, a                    ; e = 0  (ball counter 2)
CCLoopA:                            ; Two loops.    LOOP A
    ld      e, d                    ; don't need to check previous collisions
    inc     e                       ; don't check a ball with itself!
CCLoopB:                            ; smaller loop  LOOP B
    ld      a, e
    CALL_(CheckPocketed)
    jr      nz, CCskip              ; if not zero (nz) then pocketed... so skip
    ;--- First Check X ---
    ld      a, d                    ; get ball 1 first
    ld      hl, B_x                 ; get x list addr
    CALL_(AddAtoHL)                 ; add ball number to addr (now at current)
    ld      b, (hl)                 ; load B with ball 1(x)
    ld      a, e                    ; now get ball 2
    ld      hl, B_x                 ; get x list addr again
    CALL_(AddAtoHL)                 ; add this ball number to addr
    ld      c, (hl)                 ; load C with ball 2(x)
    ld      a, b                    ; load ball 1(x) into a
    sub     c                       ; a = a - c   (A is now deltaX)
    jr      nc, CCabsXcont          ; if neg then cont, else skip
    neg
CCabsXcont:                         ; now a = abs(deltaX)
    cp      3                       ; a - 3
    jr      nc, CCskip              ; if neg (a > 4) then skip
    ;--- Then Check Y ---
    ld      a, d                    ; get ball 1 first
    ld      hl, B_y                 ; get y list addr
    CALL_(AddAtoHL)                 ; add ball number to addr (now at current)
    ld      b, (hl)                 ; load B with ball 1(y)
    ld      a, e                    ; now get ball 2
    ld      hl, B_y                 ; get y list addr again
    CALL_(AddAtoHL)                 ; add this ball number to addr
    ld      c, (hl)                 ; load C with ball 2(y)
    ld      a, b                    ; load ball 1(y) into a
    sub     c                       ; a = a - c   (A is now deltaY)
    jr      nc, CCabsYcont
    neg
CCabsYcont:
    cp      3                       ; a - 3
    jr      nc, CCskip              ; if neg (a > 4) then skip
    CALL_(Collide)
CCskip:
    inc     e                       ; increment ball 2 counter
    ld      a, e                    ; load A with ball 2 counter
    cp      16                      ; check if it is 16 (we have done all balls)
    jr      nz, CCloopB             ; if not then do it again
    inc     d                       ; increment ball 1 counter
    ld      a, d                    ; load A with ball 1 counter
    cp      15                      ; check if it is 15 (we have done all balls)
    jr      nz, CCloopA             ; if not then do it again
    pop     hl
    pop     de
    pop     bc
    ret

; NOTE: the velocities are calculated by taking the diferences in x and y
; positions and using that for velocities. (works pretty well to a point)
Collide:                            ; ball D has collided with ball E
    push    bc
    push    hl

    ld      a, d                    ; d is first ball
    ld      hl, B_x                 ; First get the first balls x pos
    CALL_(AddAtoHL)                 ; used to quickly get ball A in list
    ld      b, (hl)                 ; save it in B (ball 1(x)
    ld      a, e                    ; e is second ball
    ld      hl, B_x                 ; get the table addr again
    CALL_(AddAtoHL)                 ; choose ball A in list
    ld      c, (hl)                 ; save it in C (ball 2(y)

    ld      a, d                    ; now update velocity.  ball 1
    ld      hl, V_x                 ; load velocity x table
    CALL_(AddAtoHL)
    ld      a, b                    ; load ball 1 x pos into a
    add     a, (hl)                 ; slow ball 1 x velocity down
    sub     c                       ; subtract ball 2's pos
    ld      (hl), a                 ; save it in velocity

    ld      a, e                    ; now update ball 2
    ld      hl, V_x
    CALL_(AddAtoHL)
    ld      a, c                    ; load ball 2 x pos into a
;   add     a, (hl)                 ; supposed to slow ball 2 down (doesn't)
    sub     b                       ; subtract ball 1 pos
    ld      (hl), a                 ; save it in velocity

    ld      a, d                    ; DO THE SAVE AS ABOVE FOR Y VELOCITY
    ld      hl, B_y
    CALL_(AddAtoHL)
    ld      b, (hl)                 ; B = ball 1 y pos
    ld      a, e
    ld      hl, B_y
    CALL_(AddAtoHL)
    ld      c, (hl)                 ; C = ball 2 y pos
    ld      a, d

    ld      a, d
    ld      hl, V_y
    CALL_(AddAtoHL)
    ld      a, b                    ; load ball 1 y pos into a
    add     a, (hl)                 ; add current velocity (slow ball down)
    sub     c                       ; subtract ball 2 pos
    ld      (hl), a                 ; save it
    ld      a, e                    ; DO AGAIN FOR BALL 2 Y VELOCITY
    ld      hl, V_y
    CALL_(AddAtoHL)
    ld      a, c                    ; load ball 2 y pos into a
;   add     a, (hl)                 ; Supposed to slow ball 2 down (doesn't)
    sub     b                       ; subtract ball 1 y pos
    ld      (hl), a                 ; save it
    pop     hl
    pop     bc
    ret


; NOTE:  all the ball velocities are added to the balls in this section.
;        this is done to save speed. (and it's small)
; This routine will also be made smaller and faster.
CheckWalls:                         ; Fixes all the collisions with walls
    push    bc
    push    de
    push    hl
    ld      de, 0                   ; de is used as counter (just e changed)
CWLoop:                             ; Done 16 times
    ld      c, 0                    ; if c is 1 then ball in pocket
    ld      a, e
    CALL_(CheckPocketed)
    jr      nz, CWPcont             ; if not zero (nz) then pocketed... so skip
    push    de                      ; just to make sure

    ld      hl, V_x                 ; load x velocity
    add     hl, de                  ; add counter to addr (get item de)
    ld      a, (hl)                 ; load a with current x veloc
    or      a                       ; just set flags
    jr      z, SkipCWVX               ; if it is zero (no veloc) then don't calc
    ld      b, a                    ; save it in b
    ld      hl, B_x                 ; get x table addr
    add     hl, de                  ; select item de in table
    add     a, (hl)                 ; put it into a
    push    hl
    CALL_(CheckXBounds)             ; check boundries, fix velocities & pos
    pop     hl
    bit     0, c
    jr      nz, SkipCWVX            ; if C is 1 then ball is in a pocket
    ld      (hl), a                 ; save new x pos
    ld      hl, V_x
    add     hl, de
    ld      (hl), b                 ; update new velocity
SkipCWVX:
    ld      hl, V_y                 ; DO THE SAME AS ABOVE FOR Y VELOCITIES
    add     hl, de
    ld      a, (hl)
    or      a
    jr      z, SkipCWVY               ; is y veloc 0? then don't calc
    ld      b, a
    ld      hl, B_y
    add     hl, de
    add     a, (hl)
    push    hl
    CALL_(CheckYBounds)             ; check walls, fix velocity and pos
    pop     hl
    bit     0, c
    jr      nz, SkipCWVY            ; if C is 1 then ball in pocket
    ld      (hl), a                 ; save pos
    ld      hl, V_y
    add     hl, de
    ld      (hl), b                 ; save velocity
SkipCWVY:
    pop     de                      ; get back current counter
CWPcont:
    inc     de                      ; next ball
    ld      a, e                    ; used for compare
    cp      16                      ; if is 16 (17'th ball) then exit  :)
    jr      nz, CWLoop
    pop     hl
    pop     de
    pop     bc
    ret

CheckXBounds:                       ; CALLED FROM CheckWalls
    cp      5                       ; left wall
    CALL_C(XMin)                    ; is it < left wall
    cp      118                     ; right wall
    CALL_NC(XMax)                   ; is it >= right wall
    ret
CheckYBounds:                       ; CALLED FROM CheckWalls
    cp      15                      ; bottom wall
    CALL_C(YMin)                    ; is it < bottom wall
    cp      58                      ; top wall
    CALL_NC(YMax)                   ; is it >= top wall
    ret
XMin:                               ; hit left wall
    CALL_(NegVelocity)
    ld      a, 5
    CALL_(CheckXPockets)
    ret
XMax:                               ; hit right wall
    CALL_(NegVelocity)
    ld      a, 118
    CALL_(CheckXPockets)
    ret
YMin:                               ; hit bottom wall
    CALL_(NegVelocity)
    ld      a, 15
    CALL_(CheckYPockets)
    ret
YMax:                               ; hit top wall
    CALL_(NegVelocity)
    ld      a, 58
    CALL_(CheckYPockets)
    ret
NegVelocity:                        ; switch direction of velocity
    push    af
    ld      a, b
    neg
;    CALL_(Slower)
    ld      b, a
    pop     af
    ret

CheckPocketed:                      ;Return if ball A is pocketed
    push    hl
    ld      hl, BallBits
    CALL_(AddAtoHL)
    bit     0, (hl)                 ; z if not pocketed   nz if pocketed
    pop     hl
    ret

CheckYPockets:                      ;ONLY CALLED IF HIT TOP OR BOTTOM SIDES
    ld      c, a
    ld      hl, B_x
    add     hl, de                  ;de is the current ball counter
    ld      a, (hl)
    cp      7                       ;  A - 7
    jr      c, CPout                ;if X < 7 then in left pocket
    cp      117                     ;  A - 117
    jr      nc, CPout               ;if X >= 117 then in right pocket
    cp      60                      ;  A - 60
    jr      c, CPdone               ;if X < 60 then it's OK (sides checked)
    cp      64                      ;  A - 64
    jr      nc, CPdone              ;if X >= 64 then it's OK (sides checked)
    jr      CPout                   ; in the middle pocket
CheckXPockets:                      ;ONLY CALLED IF HIT LEFT OR RIGHT SIDES
    ld      c, a
    ld      hl, B_y
    add     hl, de                  ;de is the current ball counter
    ld      a, (hl)
    cp      17                      ;  A - 17
    jr      c, CPout                ;if Y < 17 then in lower pocket
    cp      57                      ;  A - 57
    jr      nc, CPout               ;if Y >= 57 then in upper pocket
CPdone:
    ld      a, c
    ld      c, 0
    ret

CPout:
    CALL_(BallOut)
    ld      a, c
    ld      c, 1                    ; a ball IS in a pocket so set C to 1
    ret

BallOut:                            ; Takes A as ball number takes it from game
    push    af
    push    bc
    push    de                      ; save DE
    push    hl
    ld      hl, BallBits
    add     hl, de
    set     0, (hl)                 ; bit 0 is the "pocketed" bit
    ld      hl, B_x
    add     hl, de                  ; Now hl is ball X pos for ball A
    ld      a, e
    ld      b, a                    ; --- THIS SEQUENCE = 123-(5*(15-A)) ---
    ld      a, 15                     ;-
    sub     b                         ;-
    ld      b, a                      ;-
    sla     a                         ;-
    sla     a                         ;-
    add     a, b                      ;-
    neg                               ;-
    add     a, 123                    ;- Now a = 123-(5*(15-A))  [x pixel pos]
    ld      (hl), a                 ; Set new ball x pos (at bot of screen)
    ld      de, 16                  ; load DE with 16
    add     hl, de                  ; add 16 to HL (now HL points to B_y)
    ld      a, 5                    ; load A with 5 (y)
    ld      (hl), a                 ; load 5 into (B_y)
    ld      de, 48                  ; B_y + 48 = V_x
    add     hl, de                  ; Now HL at V_x
    xor     a                       ; a = 0
    ld      (hl), a                 ; Zero out that V_x
    ld      de, 16
    add     hl, de                  ; Now HL at V_y
    ld      (hl), a                 ; Zero out that V_y
    pop     hl
    pop     de
    pop     bc
    pop     af
    ret


DampenBalls:
    push    af
    push    bc
    push    hl
    ld      hl, Dampers
    ld      b, 0
DampenLoop:
    ld      a, (hl)
    or      a
    CALL_Z(DampenSlow)
    dec     a
    ld      (hl), a
    inc     hl
    inc     b
    ld      a, b
    cp      16
    jr      nz, DampenLoop
    pop     hl
    pop     bc
    pop     af
    ret
DampenSlow:
    push    hl
    ld      a, b
    ld      hl, V_x
    CALL_(AddAtoHL)
    ld      a, (hl)
    CALL_(Slower)
    ld      (hl), a
    ld      a, b
    ld      hl, V_y
    CALL_(AddAtoHL)
    ld      a, (hl)
    CALL_(Slower)
    ld      (hl), a
    ld      a, 20
    pop     hl
    ret

; Takes any num for A and makes closer to 0 (neg or pos)
Slower:
    or      a
    ret     z
    bit     7, a
    jr      nz, SlowerInc
    dec     a
    jr      SlowerCont
SlowerInc:
    inc     a
SlowerCont:
    ret

;----------------------------- Ball drawing routines ----------------------------


; NOTE: Will be made smaller and faster!
DrawAllBalls:                       ; Erases and draws all balls in sequence
    push    bc
    push    de
    push    hl
    ld      de, 0                   ; de is ball counter (just e changed)
DAllLoop:
    ld      hl, E_x                 ; erase last position
    add     hl, de
    ld      b, (hl)                 ; load Erase x pos
    ld      hl, E_y
    add     hl, de
    ld      c, (hl)                 ; load Erase y pos
    CALL_(EraseBall)                ; erase one ball

    ld      hl, B_x                 ; now draw ball in new position
    add     hl, de
    ld      b, (hl)                 ; load Draw x pos
    ld      hl, B_y
    add     hl, de
    ld      c, (hl)                 ; load Draw y pos
    push    de                      ; save counter
    ld      a, (TheBall)
    cp      e                       ; Which black ball
    ld      de, WBall               ; draw a white ball (WILL BE DIFFERENT)
    jr      nz, DABdoDrw
    ld      de, BBall               ; draw a black ball if CUE BALL
DABdoDrw:
    CALL_(DrawBall)                 ; draws one ball

    pop     de                      ; get counter back
    inc     de                      ; check next ball
    ld      a, e
    cp      16                      ; is a 16 (17th ball)
    jr      nz, DAllLoop            ; do it again

    ld      hl, B_x                 ; we want to now update the ERASE TABLES
    ld      de, E_x                 ; ld erase table
    ld      bc, 32                  ; 32 times (do 16 byte x and 16 byte y)
    ldir                            ; Update temporary tables for next draw

    pop     hl
    pop     de
    pop     bc
    ret


; NOTE: THIS IS USED TO SHOW AN ANIMATING BALL.  THIS WILL BE USED TO SHOW
;       THE LOWEST BALL NUMBER IN 9 BALL (MAYBE).  ...OR SOMETHING ELSE...
; WILL BE MADE SMALLER AND FASTER
;DrawAnimBall:                       ; (ballCounter) holds ball number (1 to 4)
;    push    de
;    push    hl
;    ld      a, (ballCounter)        ; Get current ball number
;    ld      hl, ABall               ; get animation ball table
;    ld      de, $0004               ; counter
;DABloop:                            ; just add (5 * ballCounter) HL
;    dec     a
;    jr      z,DABcont
;    add     hl, de
;    jr      DABloop
;DABcont:                            ; DONE so draw next ball
;    ex      de, hl
;    CALL_(DrawBall)                 ; draw the ball at (B,C)
;    pop     hl
;    pop     de
;    ret
;
;NextBallFrame:                      ; CYCLE THROUGH BALL FRAMES
;    ld      a,(ballCounter)
;    inc     a
;    cp      5
;    jr      nz, NBFcont
;    ld      a,1
;NBFcont:
;    ld      (ballCounter),a
;    ret


EraseBall:                      ; Erases ball at (b,c)
    push    de
    ld      de, BBall           ; LOAD BLACK BALL FOR TEMPLATE TO ERASE
    ld      a, 0
    ld      (bits), a           ; DrawSprite uses bits as turn off (0) or on (1)
    CALL_(DrawSprite)           ; Draw ball
    pop     de
    ret

DrawBall:                       ; Draw ball at addr DE on screen at (b,c)
    ld      a, 1
    ld      (bits), a           ; turn on pixels (bits = 1)
    CALL_(DrawSprite)           ; Must have ball addr in DE!
    ret

;Draws a sprite at (b,c).   5x5 sprite bmp at addr DE.
DrawSprite:
    push    bc                  ; Save away, so it can be used again
    push    de
    push    hl
    ld      hl,(PROGRAM_ADDR)
    add     hl, de
    ld      d, (hl)             ; Now D has one byte in the bmp
    ld      e, b                ; E is used to reset x coord
    ld      a, 4                ; A is x counter
    ld      (temp), a           ; (temp) is y counter (ran out of regesters)
SpriteLoop:
    rl      d                   ; move 8 bits to the left.  first bit into C reg.
    jr      nc, NoDrawSprite
    push    af
    ld      a, (bits)
    or      a
    CALL_NZ(Pixel_On)           ; Want pixel on?
    CALL_Z(Pixel_Off)           ; Want pixel off?
    pop     af
NoDrawSprite:
    inc     b                   ; move one pixel to the right
    dec     a                   ; decrement x counter
    jr      nz, SpriteLoop      ; if a <> 0 then repeat
    dec     c                   ; move one pixel down
    inc     hl                  ; go to next byte in bmp
    ld      d, (hl)             ; put the byte into the D regester
    ld      b, e                ; put saved x coordinate back into b
    ld      a, (temp)           ; \
    dec     a                   ;  >-decrement y counter
    ld      (temp), a           ; /
    ld      a, 4
    jr      nz, SpriteLoop      ; repeat
    pop     hl
    pop     de
    pop     bc
    ret

Delay:
    push    bc
    ld      bc, $5000
DelayLoop:
    dec     bc
    ld      a, b
    or      c
    jr      nz, DelayLoop
    pop     bc
    ret


Contrast_Up:
    push    af
    ld      a, (CONTRAST)
    cp      $1F
    jr      z, CUDone
    inc     a
    ld      (CONTRAST), a
    out     (2), a
CUDone:
    pop     af
    JUMP_(Key)

Contrast_Dn:
    push    af
    ld      a, (CONTRAST)
    cp      $00
    jr      z, CUDone
    dec     a
    ld      (CONTRAST), a
    out     (2), a
CDDone:
    pop     af
    JUMP_(Key)




;--------------------- S U B S --------------------------

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³0,64   127,64³
;³             ³  SCREEN PIXEL LAYOUT
;³0,0     127,0³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÙ

Pixel_on:                       ;pixel on at (b,c)  NOTE! Destroys DE!
    push    bc
    push    hl
    push    de
    push    af
    ROM_CALL(FIND_PIXEL)        ;Get the pixel offset WARNING: page must be 4
    ld      de,$FC00
    add     hl,de               ;Point into the graphics memory
    or      (hl)                ;A now contains the modified screen byte
    ld      (hl),a              ;Write back to screen to update
    pop     af
    pop     de
    pop     hl
    pop     bc                  ;Restore
    ret

Pixel_off:                      : pixel off at (b,c)  NOTE! Destroys DE!   
    push    bc
    push    hl
    push    de
    push    af
    ROM_CALL(FIND_PIXEL)        ;Get the pixel offset
    ld      de,$FC00
    add     hl,de               ;Point into graphics memory
    xor     255                 ;Invert the accumulator
    and     (hl)                ;A now contains the modified screen byte
    ld      (hl),a              ;Write back to screen to update
    pop     af
    pop     de
    pop     hl
    pop     bc                      ;Restore
    ret

Pixel_change:                   ;invert pixel at (b,c)
    push    bc
    push    hl
    push    de
    push    af
    ROM_CALL(FIND_PIXEL)        ;Get the pixel offset WARNING: page must be 4
    ld      de,$FC00
    add     hl,de               ;Point into the graphics memory
    xor      (hl)               ;A now contains the modified screen byte
    ld      (hl),a              ;Write back to screen to update
    pop     af
    pop     de
    pop     hl
    pop     bc                  ;Restore
    ret


; USED BY MANY HANDELERS TO SELECT AN ITEM IN A TABLE
AddAtoHL:
    push    de                      ; save de
    ld      d, 0                    ; just use e (d = 0)
    ld      e, a                    ; use e as number to add
    add     hl, de                  ; add a to hl
    pop     de                      ; done with de
    ret


;--------  Z C P  D e c o d e r  ------------
DispPoolTable:
        DI
        ld      HL, $FFFF
        xor     A
        ld      (HL),A
        ld      DE, $FFFE
        ld      BC, $03FF
        lddr
        exx
        ld      HL,(PROGRAM_ADDR)
        ld      DE, PoolPic
        add     HL, DE
        ld      C,$80
ZCPLoop:
        ld      A,(HL)
        inc     HL
        ld      D,A
        or      A
        jr      Z, ZCPEndDecode
        rlca
        jr      C,ZCPCopy
ZCPStream:
        ld      A,D
        and     $3F
        ld      B,A
        ld      A,D
        and     $40
StreamLoop:
        or      A
        jr      Z,SLSuite
        ex      AF,AF'
        ld      A,C
        exx
        or      (HL)
        ld      (HL),A
        exx
        ex      AF,AF'
SLSuite:
        srl     C
        jr      NC,DjnzSL
        exx
        inc     HL
        exx
        ld      C,$80
DjnzSL:
        djnz    StreamLoop
        jr      ZCPLoop
ZCPCopy:
        ld      B,$07
ZCPCopyLoop:
        rlca
        jr      NC,CLSuite
        ex      AF,AF'
        ld      A,C
        exx
        or      (HL)
        ld      (HL),A
        exx
        ex      AF,AF'
CLSuite:
        srl     C
        jr      NC,DjnzCL
        exx
        inc     HL
        exx
        ld      C,$80
DjnzCL:
        djnz    ZCPCopyLoop
        jr      ZCPLoop
ZCPEndDecode:
        exx
        EI
        ret
;----------  END  Z C P  D e c o d e r  ---------------


;------------------- D E F I N E S ----------------------

; 5x5 Sprites (bits:  bbbbbxxx where x's not used)
WBall:  .db  $60,$90,$90,$60    ; Plain White ball
BBall:  .db  $60,$F0,$F0,$60    ; Solid Black Ball
ABall:  .db  $60,$B0,$90,$60    ; List of 4 balls for animation
        .db  $60,$90,$B0,$60
        .db  $60,$90,$D0,$60
        .db  $60,$D0,$90,$60


; BALL LOCATIONS:  15 balls and then the CUE-Ball
Ball_x:   .db  030,094,097,097,100,100,103,103,100,103,103,106,106,106,106,106
Ball_y:   .db  037,037,039,035,041,033,043,039,037,035,031,045,041,037,033,029

; AIM TABLE:  used to move the aim cursor around...  [Viusal and actual]
Aim_XT:   .db  09,08,07,04,01,-2,-5,-6,-7,-6,-5,-2,01,04,07,08
Aim_YT:   .db  -1,-4,-7,-8,-9,-8,-7,-4,-1,02,05,06,07,06,05,02
Aim_XS:   .db  02,02,02,01,00,-1,-2,-2,-2,-2,-2,-1,00,01,02,02
Aim_YS:   .db  00,-1,-2,-2,-2,-2,-2,-1,00,01,02,02,02,02,02,01


PreZCP:
        .db     $00
PoolPic:        ;--- Beginning of ZCP data stream
      .db    $9F,$7F,$77,$84,$38,$FF,$3A,$C8,$F8,$14,$E0,$15,$C0,$A0,$14,$E0
      .db    $13,$F8,$D3,$98,$2F,$49,$32,$E6,$AB,$97,$71,$80,$74,$A6,$D4,$C0
      .db    $3F,$2E,$CA,$D4,$3F,$34,$D5,$B0,$3F,$34,$EA,$B0,$3F,$32,$E5,$88
      .db    $3F,$33,$C5,$88,$3F,$33,$C5,$88,$3F,$33,$C5,$88,$3F,$33,$C5,$88
      .db    $3F,$33,$C5,$88,$3F,$33,$C5,$88,$3F,$33,$C5,$88,$3F,$33,$C5,$88
      .db    $3F,$33,$C5,$88,$3F,$33,$C5,$88,$3F,$33,$C5,$88,$3F,$33,$C5,$88
      .db    $3F,$33,$C5,$88,$3F,$33,$C5,$88,$3F,$33,$C5,$88,$3F,$33,$C5,$88
      .db    $3F,$33,$C5,$88,$3F,$33,$C5,$A8,$3F,$33,$D5,$A8,$3F,$33,$D5,$88
      .db    $3F,$33,$C5,$88,$3F,$33,$C5,$88,$3F,$33,$C5,$88,$3F,$33,$C5,$88
      .db    $3F,$33,$C5,$88,$3F,$33,$C5,$88,$3F,$33,$C5,$88,$3F,$33,$C5,$88
      .db    $3F,$33,$C5,$88,$3F,$33,$C5,$88,$3F,$33,$C5,$88,$3F,$33,$C5,$88
      .db    $3F,$33,$C5,$88,$3F,$33,$C5,$88,$3F,$33,$C5,$88,$3F,$33,$C5,$88
      .db    $3F,$33,$C5,$88,$3F,$33,$C5,$88,$3F,$33,$C5,$98,$3F,$33,$E5,$B0
      .db    $3F,$34,$EA,$D0,$3F,$32,$D5,$A4,$3F,$32,$CA,$D9,$BF,$6E,$80,$74
      .db    $A6,$D3,$98,$2F,$49,$32,$E6,$A8,$F8,$14,$E0,$15,$C0,$A0,$14,$E0
      .db    $13,$F8,$C8,$37,$FF,$3A,$C3,$7F,$7A,$00
;--- Data stream size : 249 bytes.


.END

