; AVENGER.ASM    
; For Usgard
; By Joel Jordan <jcjordan@qooiii.com>
; Released 01/30/1998

; I taught myself Z80 assembly over Christmas break one year, and this was the
; result.  I quit developing it shortly after school started again, and this was the
; final release.  TechnoMac was my handle back before Qooiii.

    .org 0
    .db "Avenger 1.0 by TechnoMac",0

#include "usgard.h"

yourxy =    TEXT_MEM                ;(2*1 bytes) (actually stored y,x)
yourshots = TEXT_MEM+2              ;(16*3+1 bytes-->1st byte=# of shots
                                    ;then description byte
                                    ;then (x,y) pos for each shot
enemies =   TEXT_MEM2               ;(10*3+1 bytes), (type, y, x)
                                    ;(0 in byte one means non-existent!
countE =    TEXT_MEM2+32            ;used for hitdetect (enemy)
countB =    TEXT_MEM2+33            ;used for hitdetect (bullet)
score =     TEXT_MEM2+34            ;WORD for score max 65535
lives =     TEXT_MEM2+36            ;number of guys left
randm =     TEXT_MEM2+38
status =    TEXT_MEM2+40
enemyshots = TEXT_MEM+64            ;(4*3+1 bytes-->same as "yourshots"


    ld hl, $8641                    ; Blacken the starfield
    call &BLACKLCD                  ; Located in Graph Memory
    call CLEARLCD                   ; Clear the screen
    ld hl, &LogoBox                 ; Now put the "Avenger" box
    call &PUTBOX                    ; In the top corner
    ld hl, &ScoreGFX                ; and the score
    call &PUTBOX
    ld hl, &LivesGFX                ; and lives graphics under it
    call &PUTBOX
; initialize data    
    xor a                           ; fastest way to make a 0!
    ld (yourshots), a               ; no initial shots
    ld (score), a                   ; no initial score
    ld (score+1), a                 ; both bytes of it
    ld hl, enemyshots               ; no enemy shots
    ld (hl), a                      ; and save HL to clear shot table
    ld b, 12                        ; for 12 bytes
es_init_loop:    
    inc hl                          ; starts at "enemyshots+1"
    ld (hl), a                      ; init to 0
    djnz es_init_loop               ; dec b and jump back if not zero
    ld a, 9                         ; start with nine "lives"
    ld (lives), a
    xor a                           ; clear a
    ld b, 48                        ; initialize for 48 bytes
    ld hl, yourshots+1              ; of shot data
initjmp1:
    ld (hl), a                      ; clear mem
    inc hl                          ; next byte
    djnz initjmp1                   ; dec b and jump back if not zero
; now the initial x and y of the ship and status
    ld (status), a
    ld bc, $2537                    ; start at (37, 55)-->(25h, 37h)
    ld (yourxy), bc
; gimme some enemies
    ld de, enemies                  ; load in initial ten enemies
    ld a, 10                        ; so enemy generator doesn't 
    ld (de), a                      ; make 10 identical enemies at the
    inc de                          ; beginning of the game
    ld bc, 30                       ; 30 bytes of data
    ld hl, &enemydata               ; stored there
    ldir                            ; ld (de), (hl) / inc hl / inc de
                                    ; and dec bc till zero 

mainloop:                           ; Here's where the fun is
                                    ; delay loop--R.I.P. :-)
    call &BACKGROUND                ; Scroll the neato stars
    call &UPDATESHOTS               ; move all shots and draw them
    call &UPDATE_E_SHOTS            ; move all enemy shots and draw them
    call &UPDATEYOU                 ; Place your sprite
    call &PROCESS_ENEMIES           ; move enemies
    call &GENERATE_ENEMIES          ; Bring in more enemies
    call &HITDETECT                 ; see if you hit an enemy
    call &HITDETECT_YOU             ; see if an enemy hit you
    call &UPDATESTATUS              ; update the score and lives
    ld a, (lives)                   ; check to see if you're still alive
    or a                            ; or a = cp 0 (but destroys a)
    jp z, &quit
    ld bc, (yourxy)                 ; if not, get your position to update
    call OTH_ARROW                  ; when a direction is pushed
    bit 1, a                        ; left arrow pushed?
    call z, &goleft                 ; Call for multikey
    bit 2, a                        ; right arrow pushed?
    call z, &goright                
    bit 3, a                        ; up arrow?
    call z, &goup
    bit 0, a                        ; down arrow?
    call z, &godown    
    bit 6, a                        ; exit button?
    jr z, quit                      
    bit 5, a                        ; 2nd?
    call z, &shoot
    call GET_KEY                    ; get all other keys
    cp 0                            ; if zero, skip all this
    jr z, skipkeys
    cp K_PLUS                       ; plus key?
    call z, &CONT_UP                ; increase contrast
    cp K_MINUS                      ; minus key?
    call z, &CONT_DOWN              ; decrease contrast
    cp K_MORE                       ; More key?
    call z, &pause                  ; Pause
skipkeys:    
    ld (yourxy), bc                 ; update your position in RAM
    jr mainloop                     ; loop until dead
goright:
    push af                         ; a is needed for subsequent checks
    ld a, b                         ; if x=119, don't change
    cp 55
    jr z, rret                      ; must jump so af can be popped
    inc b                           ; increment x-coord.
rret:    
    pop af
    ret
goleft:
    push af
    ld a, b                         ; if x=0, don't change
    cp 0
    jr z, rret
    dec b                    
    pop af
    ret
goup:
    push af
    ld a, c
    cp 1
    jr z, rret
    dec c                           
    pop af
    ret
godown:
    push af
    ld a,c
    cp 55
    jr z, rret
    inc c                           
    pop af
    ret
shoot:
    ld a, (status)                  ; Are you currently dead?
    and %11100000                   ; if yes, then you can't shoot
    or a
    ret nz
    push bc                         ; We need to keep the position
    ld hl, yourshots                ; now check to see if you've already
    ld a, (hl)                      ; shot 16 times
    cp 16                           ; if you have,
    jr z, shootret                  ; return to mainloop
    inc a                           ; else increase shot count by one
    ld (hl), a                      ; and update in RAM
    inc hl                          ; update pointer to point to data
    ld b, 16                        ; and we'll loop for 16 shots
                                    ; unless we find a free spot in the 
                                    ; shot table earlier (which allows for
                                    ; shots to disappear when they hit an
                                    ; enemy )
S_findoffset:
    ld a, (hl)                      ; we'll check to see if the first byte of
    cp $0                           ; data is 0.
    jr z, putdata                   ; if it is, this position is unused
    inc hl                          ; but if it isn't, we have to go on to 
    inc hl                          ; the next "row" of the table
    inc hl
    djnz S_findoffset               ; loop at most 16 times
    ret                             ; and return on failure
putdata:
    ld (hl), 1                      ; declare the shot type "1" --> it exists
    inc hl                          ; increment pointer to shot data
    ld bc, (yourxy)                 ; and load ship's position so we can
    inc b                           ; add 3 to the x coordinate to put the
    inc b                           ; shot in the middle of the ship, then
    inc b                           ; load this number into the shot's 
    ld (hl), c                      ; position, y followed by x for 
    inc hl                          ; compatibility reasons
    ld (hl), b      
shootret:    
    pop bc                          ; then pop and return
    ret

quit:
    ld hl, &GameOver
    call &PUTBOX
    call GET_KEY                    ; clear buffer on return to Usgard
quitloop:    
    call GET_KEY
    or a
    jr z, quitloop
    call OTH_EXIT                   ; no need for push and pop

; BEGINNING OF CALLS!
; this is important to know because all these routines are called by th
; previous ones (placed as they are to make use of the relative jumps)

CONT_UP:
    ld a, (CONTRAST)                ; get contrast value
    cp $1f                          ; is it at max?
    ret z                           ; then quit
    inc a                           ; else increment it
    out (2), a                      ; and output it to the contrast port
    ld (CONTRAST), a                ; and update the value in RAM
    ret
CONT_DOWN:
    ld a, (CONTRAST)                ; get contrast value
    cp $00                          ; is it minimum?
    ret z                           ; then quit
    dec a                           ; else increment it
    out (2), a                      ; and output it to the contrast port
    ld (CONTRAST), a                ; and update the value in RAM
    ret

; Simple pause routine--some ideas:
; Make it copy VIDEO_MEM to GRAPH_MEM and show a neat graphic
; Use above, but clear screen and just show text
; Move a small window to GRAPH_MEM and overlay a "PAUSE" or graphic
; (none of which I will probably ever do!)

pause:
    push af                         ; modifies the a register--only important
                                    ; to keep this in case I use multi-key
                                    ; support like I used to
P_wait:    
    call GET_KEY                    ; get the last key pressed
    cp 0
    jr z, P_wait                    ; if none, then keep waiting
    cp K_PLUS                       ; plus will increase the contrast
    call z, &CONT_UP
    cp K_MINUS                      ; minus will decrease it
    call z, &CONT_DOWN
    cp K_MORE                       ; more un-pauses
    jr nz, P_wait
    pop af                          ; get a back
    ret

; Routine to Make Screen Black at HL, mostly stolen from SQRXZ (sorry!)
BLACKLCD:       
    ld bc, 1023     ; Fills BC+1 chars from HL with (HL)
    ld (hl), $FF    ; HL = GRAPH_MEM
    ld d,h          ; and swap that with de
    ld e,l          ; and (HL) is black
    inc de
    ldir            ; ld (DE), (HL), increment pointers and decrement de
    ret             ; till it's 0, then return

; Standard Zshell School Putsprite routine
; B=X,C=Y,HL points to sprite
; sprite data is .db X size (pixels), Y size (pixels), .db data
; refer to Zshell school for explanation
; but briefly as follows:
; for every pixel, see if it's set, if it is, and it with the screen
; if not, or it.

PSPR_NR:
    push bc
    push de
    ld a, 63
    sub c
    ld c,a
    push hl
    push hl
    call FIND_PIXEL
    ld de, $FC00
    add hl,de
    ex de,hl
    pop hl
    ld b,(hl)
    inc hl
    ld c,(hl)
    inc hl
    push hl
    pop ix
    ex de,hl
PS_NewRow:
    push bc
    ld d, (ix)
    inc ix
    push af
    push hl
PS_NewCol:
    rl d
    ld e, a
    jr nc, PS_NoPixel
    or (hl)
    ld (hl), a
    jr PS_NextPixel
PS_NoPixel:
    cpl
    and (hl)
    ld (hl),a
PS_NextPixel:
    ld a,e
    rrca
    jr nc, PS_SameByte
    inc hl
PS_SameByte:
    djnz PS_NewCol
    pop hl
    pop af
    ld de, 16
    add hl,de
    pop bc
    dec c
    jr nz, PS_NewRow
    pop hl
    pop de
    pop bc
    ret

PS_Clip:
    push bc
    push de
    push hl
    push hl
    push de
    ld c, 63
    call FIND_PIXEL    
    ld de, $FC00
    add hl, de
    pop bc
    pop ix
    jr PS_NewRow

; Draw some "random" stars and scroll down
; This routine will use the graph memory to draw the background
; each cycle it will display one of four star backgrounds and scroll down

BACKGROUND:
    ld hl, $8A28            ; copy each pixel to the one one row below
    ld de, $8A38            ; starting at bottem of Graph mem
    ld b, 63                ; 63 times!
BGND_copyloop:    
    push bc
    ld bc, 8                ; copy 1008 times (skip last row)        
    lddr                    ; this decrements pointers instead of inc'ing
    dec hl                  ; this is faster than a djnz
    dec de                  ; I only wrote all this out because
    dec hl                  ; this routine probably takes up
    dec de                  ; half the processing time of the whole game
    dec hl                  ; cutting it from 20 fps to about 13 I think
    dec de                  ; Any other suggestions?
    dec hl
    dec de
    dec hl
    dec de
    dec hl
    dec de
    dec hl
    dec de
    dec hl
    dec de
    pop bc
    djnz BGND_copyloop
; now put stars on the top row to scroll down later    
; This random routine is about 250% faster than using a RANDOM call and    
; checking against a variable like I used to do!
; BTW--Usgard's RANDOM call is TOO SLOOOOOW

    ld hl, &stars
rerandom:    
    ld a, r                         ; R is refresh register and is "random"
    srl a                           ; but is always even, so shift it once
    and %00000111                   ; and only get the last three bits (a<8)
    ld b, a                         ; put that in b
    ld a, (randm)                   ; and the last number used in a
    xor b                           ; if they are the same, (xor is a good
                                    ; way to compare 2 registers)
    jr z, rerandom                  ; do this again
    ld a, b                         ; if not, store this to "randm"
    ld (randm), a                   ; and now find the offset from my pointer
    or a
    jr z, placestars                ; if it's 0, no need to add to pointer
    ld de, 8                        ; but if not, we have to add 8 bytes
getstaroffset:                      ; until it points at the row # we want
    add hl, de                      ; so this does that till b is 0
    djnz getstaroffset
placestars:
    ld de, $8641                    ; now we put that graphic on the top row
    ld bc, 8                        ; it's 8 bytes long
    ldir
; Now we copy background to regular video mem    
    ld de, $FC00                    ; we only need to copy the stars tho
    ld hl, $8641
    ld b, 64                        ; so just copy 64 lines
BGND_loop3:     
    push bc                         ; and push this for djnz(we need it first)
    ld bc, 8                        ; copy half the line (8 bytes of 16)
    ldir
    ld bc, 8                        ; then add 8 to point to next line
    ex de, hl                       ; exchange these registers because you
    add hl, bc                      ; can only add to HL
    ex de, hl                       ; switch 'em back
    add hl, bc                      ; add 8 to other location
    pop bc                          ; get bc back to djnz with b
    djnz BGND_loop3
    ret

UPDATEYOU:
; I am changing this to update your status byte as well as the sprite pos
; this will allow things like explosions, blinking sprites, etc.
    
    ld bc, (yourxy)                 ; get your position
    ld a, (status)                  ; Are you exploding/invincible?
    cp 0
    jr nz, UY_AI
UP_pspr:    
    ld hl, &yoursprite              ; get your sprite pointer
    call &PSPR_NR                   ; put sprite
    ret                             ; quit
UY_AI:
    bit 7, a                          ; bit 7 (exploding bit) set?
    jr nz, UY_Explode                ; then explode
    dec a
    ld (status), a
    bit 3, a
    ret z                           ; bit 5 (flash invisible) set?
    jr UP_pspr
UY_Explode:
    inc a
    cp $F8 
    jr z, UY_done
    ld (status), a
    push bc
    and %00000111
    srl a    
    ld b, a
    inc b
    ld de, 10
    ld hl, &enemysprite1
UY_Explodeloop:
    add hl, de
    djnz UY_Explodeloop
    pop bc
    call &PSPR_NR
    ret
UY_done:
    ld a, $3F
    ld (status), a
    ret

UPDATESHOTS:
    ld hl, yourshots                ; get number of shots fired
    ld a, (hl)                      ; if it's zero, none to process, so
    or a                            ; just return
    ret z
    ld b, 16                        ; go ahead and do all 16
    inc hl                          ; increment shot pointer to point to data
process_shot:
    push bc                         ; needed for djnz
    ld a, (hl)                      ; is type 0?
    inc hl                          ; inc hl here for ease of use
    cp 0                            ; if it is, go on to the next shot
    jr z, nextbullet
    ld c, (hl)
    inc hl
    ld b, (hl)
    dec hl
    dec c
    dec c
    ld a, c
    cp 0
    jr z, killshot
    cp -1
    jr nz, us_updatexy
killshot:    
    ld a, (yourshots)
    dec a
    ld (yourshots), a
    dec hl
    ld (hl), $0
    inc hl
    jr us_morestuff
us_updatexy:
    ld (hl), c
    inc hl
    ld (hl), b
    dec hl
us_morestuff:    
    push hl
    ld hl, &bullet
    call &PSPR_NR
    pop hl
nextbullet:    
    inc hl
    inc hl
    pop bc   
    djnz process_shot
us_ret:    
    ret

PROCESS_ENEMIES:
    ld hl, enemies              ; load in byte one
    ld a, (hl)                  ; if there are no enemies
    or a                        ; then return
    ret z                       ; without drawing them
    ld b, 10                    ; load 10 enemies to count with (djnz)
    inc hl
ForEachEnemy:
    push bc
    ld a, (hl)
    cp 0   
    jr z, NextEnemy2
    call &EnemyAI
; Change X,Y in memory    
    inc hl
    ld (hl), c
    inc hl
    ld (hl), b
    dec hl
; End Current "AI"
E_putenemy:
    bit 7,a
    jr nz, E_clipenemy
    push hl
    ex de, hl
    call &PSPR_NR
    pop hl
    jr NextEnemy
NextEnemy2:
    inc hl
NextEnemy:    
    inc hl
    inc hl
    pop bc
    djnz ForEachEnemy
    ret
E_clipenemy:
    push hl
    ex de, hl
    inc hl
    inc hl
    ld a, c
    cpl
    and %00000111
    inc a
    ld e, a
E_cliploop:
    inc hl
    dec a
    jr nz, E_cliploop
E_DoMore:    
    ld a, 8
    ld d, a
    sub e
    ld e, a
    call &PS_Clip
    pop hl
    jr NextEnemy

HITDETECT:
    ld a, (enemies)               ; If no enemies left
    or a                            ; then
    ret z                           ; return

; Hit detection algorithm used to run really slow--used a "bounding box"
; type method where it would check all the space an enemy occupied
; I'm gonna mask right now to do it--a sub followed by an and to check if it's
; more than 4 pixels difference.  If not, must be a hit.
; while i was at it, i got rid of all my pushes and pops.
    
    ld a, 10            ; initialize enemy countdown
    ld (countE), a
    ld hl, enemies+1

H_ForEachEnemy:
    ld a, (hl)
    inc hl
    cp $0                           ; first make sure the enemy still exists
    jr z, H_NextEnemy
    and %11110000
    cp $F0
    jr z, H_NextEnemy
    ld e, (hl)                      ; Once it finds a valid enemy, it will
    inc e
    inc e
    inc e
    inc e
    inc hl                          ; load its coords into DE
    ld d, (hl)                      ; for later use
    inc d
    inc d
    inc d    
    inc d
    dec hl
    ld a, 16
    ld (countB), a
    ld ix, yourshots+1
H_ForEachBullet:
    ld a, (ix)                  ; gotta make sure the shot is still valid :-)
    inc ix
    cp $0                          ; I'm gonna be brave and use IX (wow!)
    jr z, H_NextShot
    ld c, (ix)
    inc ix
    ld b, (ix)
    dec ix
    ld a, d                         ; now compare the x coordinates
    sub b                           ; mask away last 3 (within 7 if none set)
    and %01111000                   ; and also get rid of negative sign
    or a                            ; if zero, we have a hit!
    jr nz, H_NextShot
    ld a, e                         ; compare y coordinates using same
    sub c                           ; technique
    and %01111000
    or a
    jr z, H_Hit
H_NextShot:
    inc ix
    inc ix
    ld a, (countB)        
    dec a
    ld (countB), a
    or a
    jr nz, H_ForEachBullet
; First see if you survived the onslaught!
    ld a, (status)                  ; Check to see if you're already dead
    or a                            ; And skip it all if you are
    jr nz, H_NextEnemy
    ld bc, (yourxy)
    ld a, d
    inc b
    inc b
    inc b
    inc b
    inc c
    inc c
    inc c
    inc c
    sub b
    and %011111000
    or a    
    jr nz, H_NextEnemy
    ld a, e
    sub c
    and %01111000
    cp 0
    jr nz, H_NextEnemy
    ld a, (lives)
    cp 0
    jr z, H_Hit2
    dec a
    ld (lives), a
    ld a, $F0
    ld (status), a
    jr H_Hit2
H_NextEnemy:    
    inc hl
    inc hl
    ld a, (countE)
    dec a
    ld (countE), a
    or a
    jp nz, &H_ForEachEnemy
    ret
H_Hit:
    dec ix
    ld (ix), $0
    inc ix
    ld a, (yourshots)
    dec a
    ld (yourshots), a
    push hl
    ld hl, (score)
    ld de, 10
    add hl, de
    ld (score), hl
    pop hl
H_Hit2:                             ; Amazing! You hit an enemy!
    dec hl
    ld (hl), $F0                    ; set its flag to "exploding 1"
    inc hl
; Put the explosions here when I add an "exploding" byte to every enemy!
    jr H_NextEnemy



UPDATESTATUS:
; Here's where I get to finally make use of my "half-screen buffer"
; Basically, I can do anything I want with the right half of the screen
; and it will stay there without updates
; this makes it simple to put a large image that would have to be copied
; every frame otherwise--especially bad when I start using compression :-)
    
    ld de, $0C03                    ; Gonna display large text at 13,4
    ld ($800C), de
    ld hl, (score)
    call D_HL_DECI
    ld de, $0C05
    ld ($800C), de
    ld a, (lives)
    ld h, 0
    ld l, a
    call D_HL_DECI
    ret

GENERATE_ENEMIES:
    ld a, (enemies)                 ; Are there already 10 enemies?
    cp 10
    ret z                           ; if so, return
    inc a                           ; if not, add one
    ld (enemies), a
    ld hl, enemies+1                ; enemies+1 is where the data begins  
    ld b, 10                        ; check against 10 enemy entries
G_findoffset:
    ld a, (hl)                      ; and find the first empty enemy slot
    cp $0                           ; enemy type 0--dead.  Can be used
    jr z, G_putdata                 ; if it's any other type, go to the
    inc hl                          ; next entry on the enemy table
    inc hl
    inc hl
    djnz G_findoffset               ; loop until we've reached 10
    ret                             ; must've been an error, return just in
                                    ; case
G_putdata:    
    ld a, r                         ; random routine at work again
    and %00000011                   ; this time, only 3 to choose from
    cp 0                            ; come up from the right
    jr z, GPutAt63  
    cp 1                            ; come up from left
    jr z, GPutAt0
    ld a, (yourxy+1)                ; or come straight down--eventually
    ld b, a                         ; may become asteroids that can hit
    ld c, -7                        ; the other ships, maybe not though
    ld (hl), 4                      ; put these just off the screen
    jr GDone
GPutAt0:
    ld b, 0                         ; put on left side
    jr GDone1
GPutAt63:
    ld b, 55                        ; right side
GDone1:
    ld c, 63                        ; bottom line of screen
    ld (hl), 1                      
GDone:    
    inc hl
    ld (hl), c
    inc hl
    ld (hl), b
    ret

PUTBOX:                             ; From the Zshell School
    ld e, (hl)                      ; Ripped right out of a tutorial
    inc hl                          ; Sorry!
    ld d, (hl)
    inc hl
    ld b, (hl)
    inc hl
    ld c, (hl)
    inc hl
PB_NewRow:
    push bc
    push de
PB_NewCol:
    ld a, (hl)
    inc hl
    ld (de), a
    inc de
    djnz PB_NewCol
    pop de
    ld bc, 16
    ex de, hl
    add hl, bc
    ex de, hl
    pop bc
    dec c
    jr nz, PB_NewRow
    ret

; Here is my quick and dirty attempt at AI.  No boss yet, but
; 4 types of enemies
; I really don't want to slow the game down too much though!

; 3 Types of Enemies:
; 1) moving up along edge of screen
; 2) moving left, moves up once at edge
; 3) moving right, moves up once at edge
; 4) moving down 
; F0 to F7) Exploding (7 frames)



; Enemy type changes are handled here.  Their positions, however, are not
; changed!  BC must be stored in enemy memory!

EnemyAI:
    push hl
    ld a, (hl)    
    inc hl
    ld c, (hl)
    inc hl
    ld b, (hl)
    dec hl
    dec hl
    cp 2
    jr z, Enemy2
    cp 3
    jr z, Enemy3
    cp 4
    jr z, Enemy4
    ld d, a
    and %11110000
    cp $F0
    jr z, Exploding
Enemy1:
    dec c
    ld a, c
    cp -7
    jr z, AI_killenemy
    cp 32
    jr nz, EAI_ret    
    ld a, b
    or a
    jr z, AI1_to_right
AI1_to_left:      
    ld (hl), 2
    jr EAI_ret
AI1_to_right:
    ld (hl), 3
    jr EAI_ret
Enemy2:
    dec b
    jr z, AI2or3_to_1
    call &EnemyFire
    ld a, c
    cp 20
    jr z, EAI_ret
    dec c
EAI_ret:    
    pop hl
    ld de, &enemysprite1
    ret
AI2or3_to_1:
    dec c
    ld (hl), 1
    jr EAI_ret
Enemy3:
    inc b
    ld a, b
    cp 55
    jr z, AI2or3_to_1
    call &EnemyFire
    jr EAI_ret
AI_killenemy:
    ld (hl), 0
    ld a, (enemies)
    dec a
    ld (enemies), a
    jr EAI_ret
Enemy4:
    inc c
    ld a, c
    cp 63
    jr z, AI_killenemy
    jr EAI_ret
Exploding:
    ld a, d
    inc a
    cp $F8
    jr z, AI_killenemy
    push bc
    ld (hl), a
    and %00000111
    srl a
    ld b, a
    inc b
    ld de, 10
    ld hl, &enemysprite1
AI_Explodeloop:
    add hl, de
    djnz AI_Explodeloop
    ex de, hl
    pop bc
    pop hl
    ret

; Enemies can shoot!
EnemyFire:
    ld a, (enemyshots)              ; Only four shots right now...
    cp 4
    ret z
    push hl
    push bc
    push bc
    inc a
    ld (enemyshots), a
    ld hl, enemyshots+1
    ld b, 4    
ES_findoffset:
    ld a, (hl)
    cp $0
    jr z, E_putdata
    inc hl
    inc hl
    inc hl
    djnz ES_findoffset
    ret
E_putdata:
    pop bc
    dec b
    dec b
    dec b
    ld a, c
    sub 8
    ld a, (yourxy+1)
    sub b
    bit 7, a
    jr nz, E_shootleft
E_shootright:
    ld a, 1
    jr E_more
E_shootleft:
    ld a, 2
E_more:    
    ld (hl), a
    inc hl
    ld (hl), c
    inc hl
    ld (hl), b
E_shootret:    
    pop bc
    pop hl
    ret

UPDATE_E_SHOTS:
    ld hl, enemyshots
    ld a, (hl)
    or a
    ret z
    ld b, 4
    inc hl
ES_process_shot:
    push bc    
    ld a, (hl)
    inc hl
    cp 0
    jr z, ES_nextbullet
    ld c, (hl)
    inc hl
    ld b, (hl)
    dec hl
    cp 1
    jr z, ES_MoveRight
    cp 2
    jr z, ES_MoveLeft
    jr ES_Down  
ES_MoveLeft:
    dec b
    ld a, b
    cp 0
    jr z, ES_killshot
    dec b
ES_MoveRight:
    inc b
    ld a, b
    cp 62
    jr z, ES_killshot
ES_Down:    
    inc c
    ld a, c
    cp 62
    jr nz, ES_updatexy
ES_killshot:    
    ld a, (enemyshots)
    dec a
    ld (enemyshots), a
    dec hl
    ld (hl), $0
    inc hl
    jr ES_morestuff
ES_updatexy:
    ld (hl), c
    inc hl
    ld (hl), b
    dec hl
ES_morestuff:    
    push hl
    ld hl, &Ebullet
    call &PSPR_NR
    pop hl
ES_nextbullet:    
    inc hl
    inc hl
    pop bc   
    djnz ES_process_shot
ES_ret:    
    ret

HITDETECT_YOU:
    ld a, (enemyshots)
    or a
    ret z
    ld a, (status)
    or a
    ret nz    
    ld hl, enemyshots+1
Y_Check_your_ship:    
    ld de, (yourxy)
    inc d
    inc d
    inc d
    inc e
    inc e
    inc e
    ld b, 4
Y_For_Each_bullet:
    push bc
    ld a, (hl)
    inc hl
    cp 0
    jr z, Y_nextbullet
    ld c, (hl)    
    inc hl
    ld b, (hl)
    inc c
    inc c
    inc c
    inc b
    inc b
    inc b
    ld a, b
    sub d
    and %01111100
    cp 0
    jr nz, Y_nextbullet
    ld a, c
    sub e
    and %01111100
    cp 0
    jr z, Y_Hit
Y_nextbullet:
    inc hl
    pop bc
    djnz Y_For_Each_bullet
    ret
Y_Hit:
    ld a, (lives)
    dec a
    ld (lives), a
    ld a, $F0
    ld (status), a
    ld a, (enemyshots)
    dec a
    ld (enemyshots), a
    dec hl
    dec hl
    ld (hl), 0
    inc hl
    inc hl
    jr Y_nextbullet

; DATA BEGINS HERE!

yoursprite:
.db 8,8
.db %11100111
.db %01100110
.db %01100110
.db %01000010
.db %01000010
.db %01000010
.db %00000000
.db %00000000

stars:
.db %10111111,%11111111,%11110111,%11111011,%11111111,%11111101,%11111011,%11111111
.db %11111111,%11011111,%11111101,%11111111,%11110111,%11110111,%11111111,%11111101
.db %11101111,%11111111,%10111111,%11111111,%11111011,%11111111,%11011111,%11011111
.db %11011111,%11111011,%11111111,%11011111,%11111111,%10111111,%11111111,%11110111
.db %11110111,%11011111,%11111111,%11111110,%11011111,%11111111,%11110111,%11111111
.db %10111111,%11111111,%11111111,%11101111,%11111111,%11111111,%11011111,%11111111
.db %11111111,%11111111,%11111111,%11111111,%11111111,%11111111,%11111111,%11111111
.db %11111111,%11111111,%11111111,%11111111,%11111111,%11111111,%11111111,%11111111

bullet:
.db 2,2
.db %11
.db %11

Ebullet:
.db 3,3
.db %111
.db %111
.db %111
enemysprite1:
.db 8,8
.db %11000111
.db %10000011
.db %00000001
.db %00000001
.db %01000101
.db %01101101
.db %11000111
.db %11101111

explosion:
; Explosion sprites--10 bytes each
.db 8,8
.db %10101101
.db %11000111
.db %10010010
.db %10000011
.db %11000111
.db %10101101
.db %11011011
.db %11111111

.db 8,8
.db %11000111
.db %10000011
.db %01000001
.db %00001001
.db %10100001
.db %00000101
.db %11000111
.db %11101111

.db 8,8
.db %10111111
.db %11000110
.db %10000011
.db %00010001
.db %11000111
.db %11101111
.db %10111011
.db %11110111

.db 8,8
.db %11011101
.db %01101110
.db %11010101
.db %10111011
.db %10101101
.db %11101011
.db %10110110
.db %10011011

enemydata:
.db 4,32,05                     ; Table for initial enemies
.db 4,16,05                     ; Byte 1-->Type(0 = none, 4=moves down)
.db 4,28,15                     ; Byte 2, 3-->(y , x)
.db 4,12,15
.db 4,24,25
.db 4,08,25
.db 4,20,35
.db 4,04,35
.db 4,16,45
.db 4,00,45

LogoBox:
.dw $FC09
.db 7, 16
.db %11111111,%11111111,%11111111,%11111111,%11111111,%11111111,%11111111
.db %11010101,%01010101,%01010101,%01010101,%01010101,%01010101,%01010111
.db %10100000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000111
.db %11000011,%00011001,%10111110,%11100101,%11100011,%11101111,%10000111
.db %10100111,%10011001,%10111110,%11100101,%11110011,%11101111,%11000111
.db %11001100,%11011001,%10110000,%11100101,%00110011,%00001100,%11000111
.db %10101100,%11011001,%10110000,%11110101,%00000011,%00001100,%11000111
.db %11001111,%11011001,%10111100,%11110101,%00000011,%11001111,%10000111
.db %10101100,%11011011,%00111000,%11110101,%01111011,%10001111,%00000111
.db %11001100,%11011011,%00110000,%11011101,%00110011,%00001101,%10000111
.db %10101100,%11011011,%00111110,%11011101,%11110011,%11101100,%11000111
.db %11001100,%11001111,%00111110,%11011101,%11110011,%11101100,%11100111
.db %10100000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000111
.db %11000000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000111
.db %11111111,%11111111,%11111111,%11111111,%11111111,%11111111,%11111111
.db %11111111,%11111111,%11111111,%11111111,%11111111,%11111111,%11111111

ScoreGFX:
.dw $FD09
.db 5, 8
.db %11000000,%00000000,%00000000,%00000000,%00000011
.db %10001111,%00111100,%01111000,%01111000,%01111001
.db %00010000,%01000000,%10000100,%01000100,%01000110 
.db %00010000,%01000000,%10000100,%10001000,%11100000
.db %00001100,%01000000,%10000100,%11110000,%10000000
.db %00000010,%10000001,%00001001,%00100001,%00001100
.db %10111100,%11111001,%11111001,%00010001,%11100001
.db %11000000,%00000000,%00000000,%00000000,%00000011

LivesGFX:
.dw $FE09
.db 5, 8
.db %11000000,%00000000,%00000000,%00000000,%00000011
.db %10000100,%00011101,%00100111,%11001111,%10000001
.db %00000100,%00001001,%00100100,%00001000,%00110000 
.db %00001000,%00010001,%01001110,%00001000,%00000000
.db %00001000,%00010001,%01001000,%00000110,%00000000
.db %00010000,%00100001,%10010000,%00000001,%01100000
.db %10011111,%01110001,%00011111,%00111110,%00000001
.db %11000000,%00000000,%00000000,%00000000,%00000011

GameOver:
.dw $FE24
.db 7, 16
.db %11111111,%11111111,%11111111,%11111111,%11111111,%11111111,%11111111
.db %10000000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000001
.db %10111111,%00011100,%01100001,%10011111,%10000000,%00000000,%00000001
.db %10100001,%00100010,%01010010,%10010000,%00000000,%00000000,%00000001
.db %10100000,%00100010,%01010010,%10010000,%00000000,%00000000,%00000001
.db %10100111,%00111110,%01001100,%10011110,%00000000,%00000000,%00000001
.db %10100010,%00100010,%01000000,%10010000,%00000000,%00000000,%00000001
.db %10111110,%00100010,%01000000,%10011111,%10000000,%00000000,%00000001
.db %10000000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000001
.db %10000000,%00000000,%00011111,%10100001,%01111110,%11110000,%00000001
.db %10000000,%00000000,%00010000,%10100010,%01000000,%10001000,%00000001
.db %10000000,%00000000,%00010000,%10100100,%01111000,%11111000,%00000001
.db %10000000,%00000000,%00010000,%10101000,%01000000,%10010000,%00000001
.db %10000000,%00000000,%00011111,%10110000,%01111110,%10001000,%00000001
.db %10000000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000001
.db %11111111,%11111111,%11111111,%11111111,%11111111,%11111111,%11111111


.end

