;      ZX80 assembler --- zpace invaders
;
; relocatable code    2 byte numbers and addresses combined  
;         passes z80-asm tests
; display vertical 12 x horizontal 15 set up by BASIC print of spaces
;               (see zpace.inf for BASIC)
;    each branch takes circa 1400 cycles which fits in fly-back period
;    so no flicker in TV display.
; info stored in video memory starting at (64,12) 
;
;  Addresses: 0 ........14 NL
;             16........30 NL
;
;             176......190 NL
;
; address (1 byte) stored for you, bomb, laser and some info about SI's
;
; SI initially  5x10 at top,  number in (64,30), top left position in e'
;   move 1 SI line at a time: line count in h' (-5 initially, bottom)
;  no of goes in d' (=0 initially)
; bottom line (176,..,190) is you (in c', initially 183)
; laser fired from you on bottom line (max 1) in l'
; bomb dropped from SI (always 1) in b'
; on screen:  SI are "S", bomb is "*", laser is "."
;
      LD BC, 75     ; offset to INIT   (LSB=75 MSB=0)
      ADD HL,BC
      PUSH HL       ; needed since JR only ranges +/-128
      POP IX        ; HL contained start address, IX is  +75: INIT:
      EXX           ; store on alt registers
      LD H, 251     ; sline=-5    line of SI to move first (bottom)
      LD A, (16415) ; number of frames mod 7-> quasi random bomb position 64,31
      AND 7
      LD B,A        ;  bomb position
      LD C, 183     ;  your position on bottom line (176...190)
      LD DE, 0      ;  no of goes, lowest position of a SI
      LD L,H        ;  laser position
      EXX
      LD HL, (16396)  ; start of display buffer ( 15 wide 12 high )
      LD A, 5         ; put symbol "56"=S into 5*10 space invader block
L2:   LD B, 10
L1:   INC HL
      LD (HL), 56    
      DJNZ L1         ; decrements B until 0
      LD BC, 6        ; start of next line
      ADD HL,BC
      DEC A
      JR NZ L2
      NOP
      LD (IY+30), 50  ; number of SI
      LD HL, (16400)  ; display end  64,16
      LD BC, (16420)  ; next position in line 64,36 (B=row, C=across)
      INC B
      XOR A
      CP C
      LD A, 118       ; newline     C=0 then newline already signalled
      JR Z G1         ; check label G1 (was 2 in code tested, ie GG1!)
NLL:  LD (HL),A
G1:   INC HL
GG1:  DJNZ NLL        ; fill rest of display with newlines
      JR INIT
RET1: POP HL
      POP HL
RET2: LD H, 0
      LD L, (IY+30)
      RET             ; end of game
INIT: LD HL, (16396)  ;  reload l,b,y ---------------
      INC HL
      EX DE,HL
      LD H, 0
      EXX
      LD A,L
      EXX
      LD L,A          ; laser l status
      ADD HL,DE       
      CP 191
      JR NC NOL       ; check > 191    ie no laser
      LD A, 56
      CP (HL)         ; check if SI (symbol 56)  on l site
      JR Z HIT
      LD (HL), 27     ; place l symbol (.)
DEL2: PUSH HL         ; delay
      POP HL
      JR ENDL
NOL:  JR DEL1
DEL1: NOP             ; delay
      JR DEL2
HIT:  LD (HL), 184  ; splat:  symbol inverse S
      DEC (IY+30)   ; one less SI
      JR Z RET2     ; if no SI left -> RET2    END: killed all SI
ENDL: PUSH HL
      LD H, 0
      EXX
      LD A,C        ; y -> L
      EXX
      LD L,A
      ADD HL,DE
      PUSH HL
      LD A, 56
      CP (HL)        ; check if SI on y -> RET1
      JR Z RET1
      LD (HL), 128   ; else char 128 (inverse space)
      LD H, 0
      EXX
      LD A,B
      EXX
      LD L,A         ; b -> L
      ADD HL,DE
      LD A,(HL)
      CP 38          ; see what bomb does  l(27) sp(0) | SI(56,184) y(128)  
      JR NC, NOB
      LD (HL), 20    ; load *  
NOB:  JR NC, DB      ; no bomb
DB:   CP 128
      JR Z RET1      ; bomb on y -> RET1    END of game
      PUSH HL
FRM:  LD (IY+35), 56 ;   output to Display---------------------
      LD A, 15       ;   code copied from ROM: IY=64,00
      OUT (255) A
      LD A, 236
      LD B, 25
      LD HL, (16396)  ; 64,12
      SET 7, H
      CALL 429        ; 1,173
      LD A, 243
      INC B
      DEC HL
      DEC (IY+35)
      JR FRD
FRD:  CALL 429
      IN A, (254)    ; end copied code----------------
      EXX
      INC D          ; number of goes
      LD A,D
      AND 3          ; bomb and laser speed (7 is slower)
      JR Z DLBY      ; ->->->   delete l b y  (only exit from frm loop)
      LD A,E
      EXX
      LD C,A
      LD HL, (16396)
      INC HL
      LD B, 0
      ADD HL,BC
      PUSH HL        ;  save address of Smin +1
      LD D,C         ;  Smin
      EXX            ;  calculate b'   new (random) bomb origin
      LD A,D         ;  quasi-random - based on other info
      RRA
      RRA
      ADD A,B
      ADD A,L        ; N_goes/4+b+l 
      AND 23         ; 00010111 : pseudo rand 0..7   16..23
      ADD A, 1       ; +1
      EXX            ; top 2 rows 0..9xxxxx16...25 from Smin
      LD C,A         ; C stores rnd
      ADD HL,BC      ; HL points at rnd + Smin
      LD A, 26 
      SUB C
      LD B,A       ;  26 - rnd: count for search from rnd to end
      LD A, 56     ; 
      LD E,B       ;  if no SI on top 2 rows:  use  rnd (was 22 -BUG)
      NOP
BC1:  CP (HL)      ;  SI at address?
      JR NZ SK1
      LD E,B       ; b1_loop   keep first SI position found (rel 26-rnd)
      INC A        ; so ignore later sites 
SK1:  INC HL      
      DJNZ BC1     ; step through top 2 rows of SI from rnd
      EX AF,AF'
      LD A,E       ; first SI position: (26-rnd-x   if at rnd+x)
      SUB 26
      ADD A,C
      LD E,A       ; store b1_loop -(26-rnd)  ie  -x  (rel rnd)
      POP HL       ; Smin
      LD B,C       ; rnd
      EX AF,AF'
BC2:  CP (HL)      ; search for SI if not found above from 0 to rnd
      JR NZ SK2
      LD E,B       ; b2_loop  (rnd-x) if at x (rel 1)
      INC A        ; ignore SI  if found 
SK2:  INC HL
      DJNZ BC2
      LD A,C
      SUB E        ; rnd + x  ie position rel 1 
      ADD A,D      ; 
      LD (16415), A   ; 64,31  Smin+b'
      LD B, 12
DLB:  DJNZ DLB        ; delay
      JR FRM
DLBY: EXX             ; delete l b y ----------------------
      LD B, 0
      POP HL
      LD A, 20
      CP (HL)
      JR NZ NOBD      ; check if * (bomb) on screen
      LD (HL) B       ; replace * by 0
NOBD: POP HL
      LD (HL) B
      POP HL
      EXX
      LD A,L
      CP 191          ; check if laser in use (< 191)
      EXX
      LD A,(HL)   
      JR NC NOLD     
      LD (HL) B       ; replace by 0
NOLD: CP 184          ; check if inverse S on screen (ie SI hit)
      EXX
      JR NZ NOHIT
      LD L, 254       ; flag l as not in use
NOHIT: LD A,D
      EXX
      AND 31       ;   SI moving speed (63 slower)
      JR Z SIM     ;   to SI move
      LD B, 0      ;     MOVE l b y ------------------------------
      LD C, 254
      IN A (C)     ; read keyboard ; 1:left, 2:right, 7:fire
      CPL        
      LD E,A       ; key in E
      EXX
      LD A,C       
      EXX
      LD D,A       ;y
      EXX
      LD A,L       ;l
      EXX
      LD C, 191    ; only fire if l is (off) ie > 191
      SCF
      BIT 3, E
      JR Z NOF
      CP C
NOF:  JR C LSM
      LD A,D       ; set l from y if was > 191 and key ON
LSM:  CP C
      JR NC LOFF
      SUB 16       ; move up 1 line
LOFF: EXX
      LD L,A
      EXX
      LD A,D
      BIT 0, E    ; move L ?
      JR Z NOMV
      CP 176      ; check allowed move 176---190
NOMV: JR Z NOTL
      DEC A
NOTL: BIT 1, E    ; move R ?
      JR Z NOMV1
      CP 190
NOMV1: JR Z NOTR
      INC A
NOTR: EXX
      LD C,A         ; store y
      LD A,B
      ADD A,16       ; move b down 1 line
      LD B, (IY+31)  ; get b'  
      CP 191
      JR NC NEWB
      LD B,A         ; keep old b if still on screen
NEWB: EXX
      LD B, 47       ; delay
DLL:  DJNZ DLL
      JP (IX)
      NOP
SIM:  EXX            ; SI MOVE ---------------- zig-zag-------------------
      LD A,E         ; moves 1 block of 10 at a time
      EXX
      LD C,A         ; Smin (address top left of SI block)
      AND 7          ; 00000111 mask
      BIT 4, C       ; 16's bit: so if odd or even row
      JR Z EV        ; calc shift of SI
      CP 0           ; Smin moves 0 1 2 3 4 5 21 20 .. 16 32 33 34 ...
      LD A, 255      ; -1
      JR NZ XEND     ; ie delS : +1, -1 or +16 (down row)
      ADD A, 17
XEND: NOP
      JR NXT
EV:   CP 5           ; ie last place to right to will move down
      LD A, 1        ; +1
      JR NZ XEND1
      ADD A,15       ; +16
XEND1: JR NXT
NXT:  LD D,A         ; delS in D
      ADD A,C        ; 
      LD C,A         ; Smin+delS in C
      SUB 127        ; C -(191-64)   LHS of new bottom line of SI rel 191
      JR C NBOT      ; not beyond bottom line
      AND 240        ; bottom line of SI rel 191 (line*16)  ( 11110000 )
      CP 48          ; test: top of SI will be on bottom line
      JR Z RETT      ; SI are on bottom row -------> END (was NZ in test code)
      ADD A,16       
      NEG            ; -16 or -32 or -48 
      ADD A,C        ;  shift Smin+delS up to ignore empty lower rows
      LD C,A         ;    (so transfer an extra empty lines)
      NOP            ;    needed if all bottom rows of SI are killed
      JR FN
NBOT: LD B, 4        ; delay
SLF:  DJNZ SLF
FN:   LD A,D        ; Smin
      EXX           ; eval source, dest for block tranfers of 10
      INC H         ; filling with zeroes keep other symbols
      JR NZ NTOP    ; H contains block number -1 -2 -3 -4 -5 (increment)
      LD H, 251     ; bottom line
      ADD A,E       ; Smin+delS
      LD E,A        ; update Smin if top line moving
      LD A, 9       ; 9 to move topline
      JR SKP
NTOP: LD A,H        ; not top line
      CPL
      RLCA
      RLCA
      RLCA
      RLCA          ; -H * 16
      ADD A,25      ; +9+16 : dest 25, .., 105  (top line 9)
SKP:  EXX
      ADD A,C       ; Smax (dest)   
      SUB D         ; Smax (source)
      CP (HL)
      LD B, 6       ; delay
DDD:  DJNZ DDD
      LD E,A
      LD A,D
      LD BC, 2560      ; 10,0 in BC
      LD D,C
      LD HL, (16396)   ; 64,12
      BIT 7, A
      JR NZ INX
      NOP              ; DelS=1,16 
      ADD HL,DE
      INC HL
      LD E,A
      EX DE,HL
      ADD HL,DE
      EX DE,HL
DCL:  LD A, (HL)       ;  loop 
      LD (DE), A       ; move SI info  to DE
      LD (HL), C       ; fill old site with zero
      DEC HL
      DEC DE
      DJNZ DCL         ;  loop 10(from B)
      JP (IX)          ;  to INIT ----------------------->
INX:  LD A,E
      SUB 8            ; delS = -1 
      LD E,A
      ADD HL,DE
      LD E,L
      LD D,H
      DEC DE
INCL: LD A,(HL)       ; loop
      LD (DE),A       ; move SI info 
      LD (HL),C       ; fill zero
      INC HL
      INC DE
      DJNZ INCL       ;  loop 10(from B)
      JP (IX)         ;   to INIT ------------------------->
RETT: LD H, 0
      LD L, (IY+30)
      RET
