[{"data":1,"prerenderedAt":454},["ShallowReactive",2],{"learn:invaders:12-waves":3},{"item":4,"marksAgainst":453},{"slug":5,"number":6,"title":7,"idea":8,"models":9,"body":12,"tryIt":380,"game":404,"cites":436,"program":452},"12-waves",12,"Waves, each faster than the last","a table copied afresh, a count of what is left, and the finished game",[10,11],3,1,[13,19,23,27,148,152,155,172,175,196,198,239,242,254,256,266,269,282,284,302,304,320,323,339,343,366,369,373,376],{"kind":14,"inlines":15},"p",[16],{"kind":17,"text":18},"text","The last chapter. Shoot down all 24 invaders and a new wave appears at the top, where the first began, and it marches faster: the first wave steps every 4 turns, the second every 3, the third every 2, and every wave after that on every turn. The score and the lives carry on from wave to wave, and the game goes on until a bomb takes the last life or the invaders land. That is the whole game.",{"kind":20,"id":21,"text":22},"h2","what-changed","What changed",{"kind":14,"inlines":24},[25],{"kind":17,"text":26},"Chapter 11's program, with these changes:",{"kind":28,"items":29},"ul",[30,36,54,66,84,99,110,120,127,137],[31,34],{"kind":32,"text":33},"strong","~",{"kind":17,"text":35}," The comment at the top, lines 1 to 5, now describes chapter 12.",[37,39,41,44,46,48,50,52],{"kind":32,"text":38},"+",{"kind":17,"text":40}," Three lines in ",{"kind":42,"text":43},"code","HIT",{"kind":17,"text":45},", lines 92 to 94, after ",{"kind":42,"text":47},"CALL ADDSCORE",{"kind":17,"text":49},": one invader fewer in the wave, and with none left, ",{"kind":42,"text":51},"CALL Z,NEWWAVE",{"kind":17,"text":53},".",[55,56,58,60,62,64],{"kind":32,"text":33},{"kind":17,"text":57}," ",{"kind":42,"text":59},"MARCH",{"kind":17,"text":61},"'s comment, line 143: the row steps every ",{"kind":42,"text":63},"SPEED",{"kind":17,"text":65}," turns.",[67,68,70,71,73,75,77,79,81,82],{"kind":32,"text":33},{"kind":17,"text":69}," In ",{"kind":42,"text":59},{"kind":17,"text":72},", ",{"kind":42,"text":74},"LD (HL),4",{"kind":17,"text":76}," becomes two lines: line 150 is changed and line 151 is new; ",{"kind":42,"text":78},"TICK",{"kind":17,"text":80}," starts again from ",{"kind":42,"text":63},{"kind":17,"text":83},", not from 4.",[85,86,87,89,91,93,95,97],{"kind":32,"text":38},{"kind":17,"text":57},{"kind":42,"text":88},"NEWWAVE",{"kind":17,"text":90}," and ",{"kind":42,"text":92},"PLACE",{"kind":17,"text":94},", new routines with their comments, after ",{"kind":42,"text":96},"LANDED",{"kind":17,"text":98},", lines 359 to 380: the next wave is faster, and starts where the first did.",[100,101,102,104,106,108],{"kind":32,"text":38},{"kind":17,"text":69},{"kind":42,"text":103},"START",{"kind":17,"text":105},", line 391: ",{"kind":42,"text":107},"CALL PLACE",{"kind":17,"text":109},", for the first wave, before the invaders are drawn.",[111,112,114,116,118,119],{"kind":32,"text":33},{"kind":17,"text":113}," The comment on the loop's ",{"kind":42,"text":115},"CALL MARCH",{"kind":17,"text":117},", line 416: every ",{"kind":42,"text":63},{"kind":17,"text":65},[121,122,124,126],{"kind":32,"text":33},{"kind":17,"text":123}," The comment above the table, lines 440 and 441, and its label, line 443: the starting places are now ",{"kind":42,"text":125},"HOME",{"kind":17,"text":53},[128,129,130,132,134,135],{"kind":32,"text":38},{"kind":17,"text":57},{"kind":42,"text":131},"INVS",{"kind":17,"text":133},", after ",{"kind":42,"text":125},{"kind":17,"text":136},", lines 449 to 451: a comment, and room for the wave's places.",[138,139,140,141,142,144,145,146],{"kind":32,"text":38},{"kind":17,"text":57},{"kind":42,"text":63},{"kind":17,"text":90},{"kind":42,"text":143},"LEFT",{"kind":17,"text":133},{"kind":42,"text":78},{"kind":17,"text":147},", lines 456 and 457: the turns between steps, and the invaders left in the wave.",{"kind":14,"inlines":149},[150],{"kind":17,"text":151},"Nothing is taken out.",{"kind":20,"id":153,"text":154},"the-table-set-up-again","The table, set up again",{"kind":14,"inlines":156},[157,159,160,162,164,166,167,169,170],{"kind":17,"text":158},"Until now ",{"kind":42,"text":131},{"kind":17,"text":161}," was both where the invaders started and where they were: the ",{"kind":42,"text":163},"DW",{"kind":17,"text":165}," lines filled it when the program loaded, and play changed it, moving places and making them 0. A new wave needs the starting places again, so they get a table of their own, ",{"kind":42,"text":125},{"kind":17,"text":168},", that nothing changes, and ",{"kind":42,"text":131},{"kind":17,"text":171}," becomes room for the wave in play:",{"kind":42,"lang":173,"text":174},"z80","INVS:   DS      2*NINV",{"kind":14,"inlines":176},[177,179,181,183,185,187,189,190,192,194],{"kind":42,"text":178},"DS",{"kind":17,"text":180},", define space, sets aside a number of bytes, here 48, two a place, and puts nothing in them: the program file does not carry them, and they hold whatever was in memory. The assembler works out ",{"kind":42,"text":182},"2*NINV",{"kind":17,"text":184},", as it worked out ",{"kind":42,"text":186},"3C80H+14",{"kind":17,"text":188},". ",{"kind":42,"text":92},{"kind":17,"text":191}," fills them, with the ",{"kind":42,"text":193},"LDIR",{"kind":17,"text":195}," lesson 4 used to print, before anything reads them:",{"kind":42,"lang":173,"text":197},"PLACE:  LD      HL,HOME         ; copy the starting places ...\n        LD      DE,INVS         ; ... into the table\n        LD      BC,2*NINV       ; two bytes a place\n        LDIR",{"kind":14,"inlines":199},[200,202,204,206,207,209,211,213,214,216,217,219,220,222,223,225,226,227,228,230,232,233,234,236,237],{"kind":17,"text":201},"In JavaScript that is ",{"kind":42,"text":203},"invs = home.slice()",{"kind":17,"text":205},", a fresh copy to play with. ",{"kind":42,"text":92},{"kind":17,"text":208}," then puts back everything else a wave starts with: ",{"kind":42,"text":210},"STEP",{"kind":17,"text":212}," marching right, ",{"kind":42,"text":78},{"kind":17,"text":215}," at ",{"kind":42,"text":63},{"kind":17,"text":218}," turns to the first step, and ",{"kind":42,"text":143},{"kind":17,"text":221}," at 24. ",{"kind":42,"text":103},{"kind":17,"text":224}," calls it once, for the first wave, before it draws the invaders, so ",{"kind":42,"text":78},{"kind":17,"text":90},{"kind":42,"text":210},{"kind":17,"text":229}," keep their ",{"kind":42,"text":231},"DB",{"kind":17,"text":90},{"kind":42,"text":163},{"kind":17,"text":235}," values only as what the program file holds; ",{"kind":42,"text":92},{"kind":17,"text":238}," sets each before the loop reads it.",{"kind":20,"id":240,"text":241},"counting-what-is-left","Counting what is left",{"kind":14,"inlines":243},[244,245,247,248,250,252],{"kind":42,"text":43},{"kind":17,"text":246}," counts the wave down. ",{"kind":42,"text":143},{"kind":17,"text":249}," is how many invaders are still standing, and ",{"kind":42,"text":251},"DEC (HL)",{"kind":17,"text":253}," sets Z when it reaches 0, so the last invader shot down brings the next wave:",{"kind":42,"lang":173,"text":255},"        LD      HL,LEFT         ; one invader fewer in the wave\n        DEC     (HL)\n        CALL    Z,NEWWAVE       ; none left: the next wave",{"kind":14,"inlines":257},[258,260,262,264],{"kind":42,"text":259},"CALL Z",{"kind":17,"text":261}," calls only when Z is set, and otherwise goes on to the next line, as ",{"kind":42,"text":263},"RET Z",{"kind":17,"text":265}," returns only then. Walking the table to see whether every place is 0 would give the same answer, but a byte counted down costs 3 instructions at a hit instead of a loop of 24 on every turn.",{"kind":20,"id":267,"text":268},"faster-each-wave","Faster each wave",{"kind":14,"inlines":270},[271,273,274,276,277,279,280],{"kind":17,"text":272},"The invaders step when ",{"kind":42,"text":78},{"kind":17,"text":275}," counts down to 0, and ",{"kind":42,"text":59},{"kind":17,"text":278}," now starts it again from ",{"kind":42,"text":63},{"kind":17,"text":281}," instead of from 4:",{"kind":42,"lang":173,"text":283},"        LD      A,(SPEED)       ; 0: a step now, and the next in SPEED turns\n        LD      (HL),A",{"kind":14,"inlines":285},[286,287,289,291,293,294,296,297,299,300],{"kind":42,"text":74},{"kind":17,"text":288}," stores a number straight into memory, but ",{"kind":42,"text":290},"LD",{"kind":17,"text":292}," has no form with memory on both sides; for one byte, A carries it. ",{"kind":42,"text":88},{"kind":17,"text":295}," makes ",{"kind":42,"text":63},{"kind":17,"text":298}," one smaller each wave, unless it is 1 already, then runs on into ",{"kind":42,"text":92},{"kind":17,"text":301},":",{"kind":42,"lang":173,"text":303},"        CP      1               ; every turn already: as fast as they go\n        JR      Z,PLACE\n        DEC     (HL)            ; one turn fewer",{"kind":14,"inlines":305},[306,308,309,311,312,314,315,317,318],{"kind":17,"text":307},"With a turn of about 137,400 T-states, and about 146,900 when the invaders step, the waves step about every 275, 208, 140 and 72 ms on the Model III. ",{"kind":42,"text":92},{"kind":17,"text":310}," sets ",{"kind":42,"text":78},{"kind":17,"text":313}," in the turn that cleared the wave, and ",{"kind":42,"text":59},{"kind":17,"text":316}," counts that turn too, so the new wave's first step comes ",{"kind":42,"text":63},{"kind":17,"text":319}," turns after the last hit, counting its own.",{"kind":20,"id":321,"text":322},"what-you-built","What you built",{"kind":14,"inlines":324},[325,327,329,331,333,335,337],{"kind":17,"text":326},"A game of 470 lines, comments and all, built in twelve chapters from a blank screen and a loop that did nothing. The screen is memory, so drawing is storing a byte (chapter 2). The keyboard is memory too, read a row at a time and tested a bit at a time (chapter 3). A shot is a screen address that moves 64 back each turn (chapter 4). The invaders are a table of addresses, walked with IX and counted with ",{"kind":42,"text":328},"DJNZ",{"kind":17,"text":330}," (chapter 6), moved together by adding one step to each (chapters 7 and 8), and shot down by reading the screen and writing a 0 (chapter 9). The score is BCD, put right by ",{"kind":42,"text":332},"DAA",{"kind":17,"text":334}," (chapter 10). The bombs come from R, the invaders land by reading their own addresses, and the game ends at a loop that jumps to itself (chapter 11). And every turn is one pass of ",{"kind":42,"text":336},"LOOP",{"kind":17,"text":338},", timed by a counted delay, with no interrupts at all.",{"kind":14,"inlines":340},[341],{"kind":17,"text":342},"It is a small game, and it leaves things out: there is no sound; there is one shot and one bomb at a time, and they cannot hit each other; a hit on the base costs a life and leaves the base where it is; there are no shields and no saucer; the score goes back to 0000 after 9990; and to play again, press Run ▶. Each of those is a chapter you could write.",{"kind":14,"inlines":344},[345,347,351,353,355,357,358,360,361,363,364],{"kind":17,"text":346},"Then look at a real one. ",{"kind":348,"text":349,"href":350},"link","Sea Dragon in the workbench","\u002F?example=sea-dragon&at=497F",{"kind":17,"text":352}," opens Wayne Westmoreland and Terry Gilman's game, from 1982, at its first instruction, with its annotated listing. Its ",{"kind":42,"text":354},"MAIN_LOOP",{"kind":17,"text":356}," is your ",{"kind":42,"text":336},{"kind":17,"text":359}," at full size; its objects are records read through IX, as ",{"kind":42,"text":131},{"kind":17,"text":362}," is read; its score is BCD with ",{"kind":42,"text":332},{"kind":17,"text":365},"; and its ships are a count with a game over at 0. You know every one of those now.",{"kind":20,"id":367,"text":368},"keys-go-to-the-screen-not-the-editor","Keys go to the screen, not the editor",{"kind":14,"inlines":370},[371],{"kind":17,"text":372},"Click the TRS-80's screen before you press the arrows or the space bar, so that it has the keyboard.",{"kind":20,"id":374,"text":375},"on-the-model-i","On the Model I",{"kind":14,"inlines":377},[378],{"kind":17,"text":379},"The Model I has the same keyboard row and the same screen, so the program runs unchanged. Fugløy runs the Model I at 1.77 MHz and the Model III at 2.03 MHz, so the game runs a little more slowly there.",[381,389],{"kind":14,"inlines":382},[383,385,387],{"kind":17,"text":384},"Press Run ▶, click the screen, and press the space bar to start. Shoot down the whole wave, dodging the bombs: when the last invader goes, all 24 appear again where the first wave started, on rows 2, 4 and 6, and they step every 3 turns instead of 4. Keep going until the top line says ",{"kind":42,"text":386},"GAME OVER",{"kind":17,"text":388},": lose your lives, or let a wave land.",{"kind":14,"inlines":390},[391,393,394,396,398,400,402],{"kind":17,"text":392},"Then change ",{"kind":42,"text":63},{"kind":17,"text":395},"'s ",{"kind":42,"text":397},"DB 4",{"kind":17,"text":399}," to ",{"kind":42,"text":401},"DB 1",{"kind":17,"text":403},": the first wave steps every turn, as fast as the invaders go, and the waves after it are no faster.",{"example":405,"addr":406,"name":407,"blocks":408},"sea-dragon",24587,"GAME_OVER",[409],{"kind":14,"inlines":410},[411,413,414,416,418,420,423,425,427,429,430,432,434],{"kind":17,"text":412},"Sea Dragon's ",{"kind":42,"text":407},{"kind":17,"text":415}," does what ",{"kind":42,"text":417},"OVER",{"kind":17,"text":419}," does, and then what this game leaves to Run ▶. It prints ",{"kind":421,"text":422},"em","Game Over Player",{"kind":17,"text":424}," on row 4 with ",{"kind":42,"text":426},"PRINT_TEXT",{"kind":17,"text":428},", and the player's number after it, then waits through three long delays, a ",{"kind":42,"text":328},{"kind":17,"text":431}," round a ",{"kind":42,"text":433},"CALL",{"kind":17,"text":435}," of the ROM's delay. Then, with one player, it jumps back to the title, the game's first instruction, where the game begins again; with two, play passes to the other player if they have ships left.",[437,441,445,449],{"source":438,"page":439,"about":440},"m3-trm","14","the Model III's keyboard is memory at 3800H-3BFFH, and its screen at 3C00H",{"source":442,"page":443,"about":444},"m1-trh","12","the Model I's keyboard is memory from 3800H, and its screen at 3C00H",{"source":446,"page":447,"about":448},"decker","64","the keyboard's rows are read at 3801H to 3840H, one address a row",{"source":450,"page":451,"about":448},"paay","26","; Build Space Invaders, chapter 12: waves. Chapter 11's game, and now\n; shooting down the last invader of a wave brings the next: NEWWAVE puts\n; the invaders back where they started, and they march faster, a step\n; every SPEED turns, one turn fewer each wave until they step every turn.\n; The finished game: march, shoot, dodge, and score, until GAME OVER.\n\n        ORG     7000H           ; the program lives at 7000H, in free memory\n\n; WAIT: a counted delay, 5,120 passes of 26 T-states, about 66 ms a turn at\n; 2.03 MHz. The routines come first, and the game itself after them, from\n; START, where END START says the program begins.\nWAIT:   PUSH    BC              ; save BC, which the loop uses\n        LD      BC,1400H        ; 1400H is 5,120 passes of 26 T-states\nDELAY:  DEC     BC\n        LD      A,B\n        OR      C\n        JR      NZ,DELAY\n        POP     BC              ; BC as the caller left it\n        RET\n\n; DRAWBASE: copy the three bytes HL points at to the base's place on row 15,\n; 3FC0H plus BASEX. BC is kept on the stack; A, DE and HL are not.\nDRAWBASE:\n        PUSH    BC              ; save BC, which LDIR counts down\n        LD      A,(BASEX)       ; the base's column ...\n        ADD     A,0C0H          ; ... plus 3FC0H, row 15: the base's left cell\n        LD      E,A\n        LD      D,3FH\n        LD      BC,3            ; three bytes\n        LDIR                    ; from (HL) to (DE), one byte at a time\n        POP     BC              ; BC as the caller left it\n        RET\n\n; MOVEBASE: with the left or right arrow down, erase the base, move it a\n; column, and draw it again. Columns 0 and 61 are the edges: there it stays.\nMOVEBASE:\n        LD      A,(3840H)       ; the arrows' row: bit 5 left, 6 right\n        AND     60H             ; keep those two bits (0110 0000)\n        RET     Z               ; neither is down: nothing to do\n        LD      C,A             ; keep the keys in C, so A is free\n        LD      HL,BLANK        ; erase the base where it is\n        CALL    DRAWBASE\n        LD      A,(BASEX)       ; the base's column, from memory\n        BIT     5,C             ; left?\n        JR      Z,NOTLT\n        CP      0               ; column 0, the left edge: stay\n        JR      Z,NOTLT\n        DEC     A\nNOTLT:\n        BIT     6,C             ; right?\n        JR      Z,NOTRT\n        CP      61              ; column 61, the right edge for three cells: stay\n        JR      Z,NOTRT\n        INC     A\nNOTRT:\n        LD      (BASEX),A       ; the new column, back to memory\n        LD      HL,BASE         ; draw the base there\n        CALL    DRAWBASE\n        RET\n\n; MOVESHOT: the shot, if there is one, moves up a row; past row 0 it is gone,\n; and moving into an invader it shoots it down, and is gone too.\nMOVESHOT:\n        LD      HL,(SHOT)       ; where the shot is: two bytes from memory into HL\n        LD      A,H             ; 0 is no shot: test HL as the clear tests BC\n        OR      L\n        RET     Z               ; no shot: nothing to do\n        LD      (HL),20H        ; erase the shot where it is\n        LD      DE,-64          ; up a row\n        ADD     HL,DE\n        BIT     2,H             ; still on the screen? 3C00H-3FFFH all have bit 2 of H set\n        JR      NZ,SHOTUP\n        LD      HL,0            ; off the top: no shot\nSHOTUP: LD      (SHOT),HL       ; keep where it is, or 0\n        LD      A,H\n        OR      L\n        RET     Z               ; gone: nothing to draw\n        LD      A,(HL)          ; what is in the cell the shot moves into?\n        CP      9EH             ; an invader's left cell: HL is its address\n        JR      Z,HIT\n        CP      0ADH            ; its right cell: its address is one before\n        JR      Z,HITRT\n        LD      (HL),21H        ; the shot is an exclamation mark\n        RET\nHITRT:  DEC     HL              ; back to the invader's left cell\nHIT:    LD      (HL),20H        ; erase the invader's two cells\n        INC     HL\n        LD      (HL),20H\n        DEC     HL              ; HL is the invader's address again\n        CALL    KILL            ; its place in the table becomes 0\n        CALL    ADDSCORE        ; 10 points\n        LD      HL,LEFT         ; one invader fewer in the wave\n        DEC     (HL)\n        CALL    Z,NEWWAVE       ; none left: the next wave\n        LD      HL,0            ; and the shot is gone too\n        LD      (SHOT),HL\n        RET\n\n; FIRE: with the space bar down and no shot in the air, a shot starts on\n; row 14, just above the base's middle cell.\nFIRE:   LD      A,(3840H)       ; the space bar is bit 7 of the arrows' row\n        BIT     7,A\n        RET     Z               ; not down: nothing to do\n        LD      HL,(SHOT)\n        LD      A,H\n        OR      L\n        RET     NZ              ; a shot is in the air already: one at a time\n        LD      A,(BASEX)       ; row 14 starts at 3F80H; the base's middle\n        ADD     A,81H           ; is its column plus one: 3F81H plus BASEX\n        LD      L,A\n        LD      H,3FH\n        LD      (HL),21H        ; draw the shot\n        LD      (SHOT),HL       ; and remember where it is\n        RET\n\n; DRAWROW: copy the two bytes DE points at to every invader's place: INVS\n; holds their screen addresses, a word each. IX walks the table and B counts\n; the invaders; BC is kept on the stack, and DE ends where it began.\nDRAWROW:\n        PUSH    BC              ; save BC: B counts the invaders\n        LD      IX,INVS         ; IX points at the table's first word\n        LD      B,NINV          ; NINV invaders, counted down by DJNZ\nDRAWONE:\n        LD      L,(IX+0)        ; this invader's screen address: the low byte\n        LD      H,(IX+1)        ; first, then the high, as a DW stores them\n        LD      A,H             ; 0 is an invader shot down: nothing to draw\n        OR      L\n        JR      Z,DRAWNEXT\n        LD      A,(DE)          ; the shape's left cell\n        LD      (HL),A\n        INC     HL\n        INC     DE\n        LD      A,(DE)          ; and its right cell\n        LD      (HL),A\n        DEC     DE              ; back to the shape's start, for the next one\nDRAWNEXT:\n        INC     IX              ; on to the next word of the table\n        INC     IX\n        DJNZ    DRAWONE         ; B minus 1, and the next invader until it is 0\n        POP     BC              ; BC as the caller left it\n        RET\n\n; MARCH: every SPEED turns, erase the row and move it a column, the way STEP\n; says. At the edge the row is marching to, it turns round, and that step\n; goes down a row instead. An invader that comes down to row 13 has landed,\n; and the game is over.\nMARCH:  LD      HL,TICK         ; one turn fewer to the row's next step\n        DEC     (HL)\n        RET     NZ              ; not 0 yet: not this turn\n        LD      A,(SPEED)       ; 0: a step now, and the next in SPEED turns\n        LD      (HL),A\n        LD      DE,BLANK        ; erase the row where it is\n        CALL    DRAWROW\n        LD      DE,(STEP)       ; the step: 1 right, or -1 left\n        CALL    EDGE            ; Z: an invader is at the edge ahead\n        JR      NZ,SIDE\n        LD      HL,0            ; turn round: the step becomes 0 minus the step\n        OR      A               ; clear the carry flag, which SBC takes off too\n        SBC     HL,DE\n        LD      (STEP),HL\n        LD      DE,64           ; a row is 64 cells: this step goes down\nSIDE:   CALL    MOVEROW         ; every invader moves by DE\n        CALL    LANDED          ; one on row 13 ends the game\n        RET\n\n; EDGE: is an invader at the edge the row is marching to? E is the step's\n; low byte, 1 or 0FFH. The low six bits of an invader's address are its\n; column; the next column would be 63 at the right edge, and -1 at the left,\n; and both are 3FH in six bits. Z if an invader is there, NZ if none is.\nEDGE:   LD      IX,INVS         ; IX walks the table, as in DRAWROW\n        LD      B,NINV\nEDGEONE:\n        LD      A,(IX+1)        ; 0 is an invader shot down: skip it\n        OR      (IX+0)\n        JR      Z,EDGENEXT\n        LD      A,(IX+0)        ; the low byte of the invader's address\n        ADD     A,E             ; plus the step\n        AND     3FH             ; its low six bits: the column it would move to\n        CP      3FH             ; 63, or -1: past an edge\n        RET     Z               ; this one is at the edge: return with Z\nEDGENEXT:\n        INC     IX\n        INC     IX\n        DJNZ    EDGEONE\n        OR      1               ; none is: A is not 0 after OR 1, so NZ\n        RET\n\n; MOVEROW: add DE to every invader's address in INVS: 1 moves the row a\n; column right, -1, the word 0FFFFH, a column left, and 64 a row down.\nMOVEROW:\n        LD      IX,INVS\n        LD      B,NINV\nMOVEONE:\n        LD      L,(IX+0)        ; the invader's address into HL\n        LD      H,(IX+1)\n        LD      A,H             ; 0 is an invader shot down: it stays 0\n        OR      L\n        JR      Z,MOVENEXT\n        ADD     HL,DE           ; plus the step\n        LD      (IX+0),L        ; and back into the table\n        LD      (IX+1),H\nMOVENEXT:\n        INC     IX\n        INC     IX\n        DJNZ    MOVEONE\n        RET\n\n; KILL: the invader at screen address HL is shot down: its place in INVS\n; becomes 0, which DRAWROW, EDGE and MOVEROW skip.\nKILL:   LD      IX,INVS\n        LD      B,NINV\nKILLONE:\n        LD      A,(IX+0)        ; this place's low byte: HL's?\n        CP      L\n        JR      NZ,KILLNEXT\n        LD      A,(IX+1)        ; and its high byte: HL's too?\n        CP      H\n        JR      NZ,KILLNEXT\n        LD      (IX+0),0        ; this is the invader: its place becomes 0\n        LD      (IX+1),0\n        RET\nKILLNEXT:\n        INC     IX\n        INC     IX\n        DJNZ    KILLONE\n        RET\n\n; ADDSCORE: 10 points more. SCORE is two bytes of BCD, two decimal digits in\n; each, four bits a digit: the low byte holds the last two digits. DAA after\n; each add puts the digits right, and its carry goes on to the high byte.\nADDSCORE:\n        LD      HL,SCORE        ; the last two digits\n        LD      A,(HL)\n        ADD     A,10H           ; plus 10: a 1 in the tens digit\n        DAA                     ; back to two decimal digits, carry past 99\n        LD      (HL),A\n        INC     HL              ; the first two digits (LD and INC HL keep C)\n        LD      A,(HL)\n        ADC     A,0             ; plus the carry from the last two\n        DAA\n        LD      (HL),A\n        RET\n\n; SHOWSCORE: print the lives at the end of row 0, and SCORE and its four\n; digits at the start. It runs every turn, so where a shot passes through\n; the top line, it is mended at once.\nSHOWSCORE:\n        LD      HL,LIVESTXT     ; the lives' word\n        LD      DE,3C39H        ; to row 0, column 57\n        LD      BC,6\n        LDIR                    ; DE ends on column 63, the row's last\n        LD      A,(LIVES)       ; the lives, 0 to 3: one digit\n        ADD     A,'0'\n        LD      (DE),A\n        LD      HL,SCORETXT     ; the word, from the table\n        LD      DE,3C00H        ; to row 0, column 0\n        LD      BC,6\n        LDIR                    ; DE ends on column 6, where the digits go\n        LD      HL,SCORE+1      ; the first two digits, in the high byte\n        CALL    TWODIG\n        DEC     HL              ; then the last two, in the low byte, and\n                                ; TWODIG's RET returns from SHOWSCORE\n; TWODIG: print the two digits of the byte HL points at, at DE, and move DE on.\nTWODIG: LD      A,(HL)          ; the byte: two digits\n        RRCA                    ; four rotates bring its high digit down\n        RRCA                    ; to the low four bits\n        RRCA\n        RRCA\n        AND     0FH             ; just that digit, 0 to 9\n        ADD     A,'0'           ; its character: '0' is 30H, '9' is 39H\n        LD      (DE),A\n        INC     DE\n        LD      A,(HL)          ; the byte again, for its low digit\n        AND     0FH\n        ADD     A,'0'\n        LD      (DE),A\n        INC     DE\n        RET\n\n; MOVEBOMB: the bomb, if there is one, falls a row. It is erased only if it\n; is still what is in its cell, and drawn only in a blank cell: anything else\n; there, an invader or the shot, it passes behind. Falling into the base, it\n; costs a life. With no bomb, DROP drops one.\nMOVEBOMB:\n        LD      HL,(BOMB)       ; where the bomb is, or 0 for none\n        LD      A,H\n        OR      L\n        JR      Z,DROP          ; no bomb: drop one\n        LD      A,(HL)          ; is the bomb still what is in its cell?\n        CP      BOMBCH\n        JR      NZ,FALL         ; no: something was drawn over it, leave that\n        LD      (HL),20H        ; yes: erase it\nFALL:   LD      DE,64           ; down a row\n        ADD     HL,DE\n        BIT     2,H             ; still on the screen, as for the shot?\n        JR      NZ,BOMBON\n        LD      HL,0            ; off the bottom: no bomb\nBOMBON: LD      (BOMB),HL       ; keep where it is, or 0\n        LD      A,H\n        OR      L\n        RET     Z               ; gone: nothing to draw\n        LD      A,(HL)          ; what is in the cell the bomb falls into?\n        CP      20H             ; a blank: draw the bomb there\n        JR      Z,BOMBDRAW\n        CP      0BCH            ; the base's wing\n        JR      Z,BASEHIT\n        CP      0BFH            ; or its middle\n        JR      Z,BASEHIT\n        RET                     ; anything else: the bomb passes behind it\nBOMBDRAW:\n        LD      (HL),BOMBCH\n        RET                     ; the bomb is in its new cell\nBASEHIT:\n        LD      HL,0            ; the bomb is gone\n        LD      (BOMB),HL\n        LD      HL,LIVES        ; and a life\n        DEC     (HL)\n        RET     NZ              ; lives left: play on\n        JP      OVER            ; none: the game is over\n\n; DROP: pick an invader with the R register, and drop a bomb from the cell\n; under its left cell. If the invader is shot down, no bomb this turn.\nDROP:   LD      A,R             ; R counts instruction fetches: 0 to 127 here\nWHICH:  CP      NINV            ; the remainder by NINV: carry when A is less\n        JR      C,PICKED\n        SUB     NINV            ; otherwise take NINV away and compare again\n        JR      WHICH\nPICKED: ADD     A,A             ; times 2: a word a place\n        LD      E,A\n        LD      D,0\n        LD      IX,INVS\n        ADD     IX,DE           ; IX points at the invader's place\n        LD      L,(IX+0)\n        LD      H,(IX+1)\n        LD      A,H             ; 0 is shot down: no bomb\n        OR      L\n        RET     Z\n        JR      FALL            ; the bomb starts a row below the invader\n\n; LANDED: has an invader come down to row 13? Then the game is over. Rows 12\n; to 15 are 3F00H to 3FFFH, and row 13 starts at 3F40H, so an address with\n; the high byte 3FH and the low byte 40H or more is on row 13 or below. A\n; place that is 0, shot down, has the high byte 0, and never has.\nLANDED: LD      IX,INVS\n        LD      B,NINV\nLANDONE:\n        LD      A,(IX+1)        ; the high byte: 3FH is rows 12 to 15\n        CP      3FH\n        JR      NZ,LANDNEXT\n        LD      A,(IX+0)        ; the low byte: 40H or more is row 13 or lower\n        CP      40H\n        JP      NC,OVER         ; landed: the game is over\nLANDNEXT:\n        INC     IX\n        INC     IX\n        DJNZ    LANDONE\n        RET\n\n; NEWWAVE: the last invader is shot down. The next wave steps one turn\n; sooner than this one did, down to every turn, and PLACE sets it up.\nNEWWAVE:\n        LD      HL,SPEED        ; turns between the invaders' steps\n        LD      A,(HL)\n        CP      1               ; every turn already: as fast as they go\n        JR      Z,PLACE\n        DEC     (HL)            ; one turn fewer\n; PLACE: a wave, as it starts: every invader in its place from HOME,\n; marching right, and NINV left to shoot down.\nPLACE:  LD      HL,HOME         ; copy the starting places ...\n        LD      DE,INVS         ; ... into the table\n        LD      BC,2*NINV       ; two bytes a place\n        LDIR\n        LD      HL,1            ; marching right\n        LD      (STEP),HL\n        LD      A,(SPEED)       ; the first step in SPEED turns\n        LD      (TICK),A\n        LD      A,NINV          ; and all of them to shoot down\n        LD      (LEFT),A\n        RET\n\nSTART:  LD      HL,3C00H        ; HL points at the top-left cell\n        LD      BC,400H         ; BC counts the cells: 400H is 1024\nCLEAR:  LD      (HL),20H        ; a space: the cell is blank\n        INC     HL              ; on to the next cell\n        DEC     BC              ; one cell fewer to go (DEC BC sets no flags ...)\n        LD      A,B             ; ... so test BC by hand: copy B into A\n        OR      C               ; and OR in C: zero only when B and C both are\n        JR      NZ,CLEAR        ; not zero yet: clear the next cell\n        LD      HL,BASE         ; draw the base where BASEX says\n        CALL    DRAWBASE\n        CALL    PLACE           ; the first wave\n        CALL    SHOWSCORE       ; the top line,\n        LD      DE,INVADER      ; and the invaders where they start\n        CALL    DRAWROW\n        LD      HL,GOTXT        ; PRESS SPACE TO START\n        LD      DE,3E96H        ; to row 10, column 22\n        LD      BC,20\n        LDIR\nGO:     CALL    WAIT            ; a turn's pause, then look at the space bar\n        LD      A,(3840H)       ; the space bar is bit 7 of the arrows' row\n        BIT     7,A\n        JR      Z,GO            ; not down: wait another turn\n        LD      HL,3E96H        ; down: erase the words\n        LD      B,20\nUNSAY:  LD      (HL),20H\n        INC     HL\n        DJNZ    UNSAY\n\n; The game loop: one turn of the game, then round again. A game's turn\n; reads the keys, moves things and draws them: the base, the shot, the\n; invaders, the bomb, and the top line.\nLOOP:   CALL    WAIT            ; the turn's pause, so the game runs at a steady speed\n        CALL    MOVEBASE        ; the base moves if an arrow is down\n        CALL    MOVESHOT        ; the shot, if there is one, rises a row\n        CALL    FIRE            ; the space bar starts a shot, if there is none\n        CALL    MARCH           ; every SPEED turns, the row steps\n        LD      DE,INVADER      ; the row, drawn every turn: a shot passing\n        CALL    DRAWROW         ; through an invader erases a cell, and this mends it\n        CALL    MOVEBOMB        ; the bomb falls, or a new one drops\n        CALL    SHOWSCORE       ; the lives and the score, on row 0\n        JR      LOOP            ; and round again, until the game is over\n\n; OVER: no lives left, or an invader has landed. The top line and the\n; invaders once more, then the words, and the program stops here: Run\n; starts a new game.\nOVER:   CALL    SHOWSCORE\n        LD      DE,INVADER      ; MARCH may have erased the invaders: draw them\n        CALL    DRAWROW\n        LD      HL,OVERTXT      ; GAME OVER\n        LD      DE,3C1BH        ; to row 0, column 27, between the two\n        LD      BC,9\n        LDIR\nSTOP:   JR      STOP            ; the game is over: wait here for good\n\n; The game's data: below the loop, where the processor never runs into it.\nBASE:   DB      0BCH,0BFH,0BCH  ; the base: a wing, a block, a wing\nBLANK:  DB      '   '           ; three spaces, to erase it\nINVADER:\n        DB      9EH,0ADH        ; an invader: two cells, a head, arms and legs\n; HOME: where a wave's invaders start, a row's start plus a column, rows 2,\n; 4 and 6. NINV is how many: EQU names a number, and takes no memory.\nNINV:   EQU     24\nHOME:   DW      3C80H+14,3C80H+18,3C80H+22,3C80H+26\n        DW      3C80H+30,3C80H+34,3C80H+38,3C80H+42\n        DW      3D00H+14,3D00H+18,3D00H+22,3D00H+26\n        DW      3D00H+30,3D00H+34,3D00H+38,3D00H+42\n        DW      3D80H+14,3D80H+18,3D80H+22,3D80H+26\n        DW      3D80H+30,3D80H+34,3D80H+38,3D80H+42\n; INVS: the invaders' places in this wave, copied from HOME by PLACE; 0 is\n; shot down. DS sets aside two bytes a place, and puts nothing in them.\nINVS:   DS      2*NINV\nBASEX:  DB      30              ; the base's column, 0 to 61\nSHOT:   DW      0               ; the shot's screen address, or 0 for none\nSTEP:   DW      1               ; the row's step: 1 right, or -1 (0FFFFH) left\nTICK:   DB      4               ; turns to the row's next step\nSPEED:  DB      4               ; turns between steps: 4, then 3, 2, and 1\nLEFT:   DB      NINV            ; invaders left to shoot down in this wave\nSCORETXT:\n        DB      'SCORE '        ; the score's word, and a space\nSCORE:  DB      0,0             ; in BCD: the last two digits, then the first two\nLIVESTXT:\n        DB      'LIVES '        ; the lives' word, and a space\nLIVES:  DB      3               ; lives left\nBOMBCH: EQU     99H             ; the bomb: a zigzag, three blocks\nBOMB:   DW      0               ; the bomb's screen address, or 0 for none\nOVERTXT:\n        DB      'GAME OVER'\nGOTXT:  DB      'PRESS SPACE TO START'\n\n        END     START           ; the program starts at START\n","; Build Space Invaders, chapter 11: bombs, and lives. Chapter 10's game,\n; and now the invaders bomb the base. MOVEBOMB drops a bomb from an invader\n; that LD A,R picks, one at a time. A bomb in the base costs a life; with\n; none left, or with an invader down on row 13, the game is over. And the\n; game waits for the space bar before it starts.\n\n        ORG     7000H           ; the program lives at 7000H, in free memory\n\n; WAIT: a counted delay, 5,120 passes of 26 T-states, about 66 ms a turn at\n; 2.03 MHz. The routines come first, and the game itself after them, from\n; START, where END START says the program begins.\nWAIT:   PUSH    BC              ; save BC, which the loop uses\n        LD      BC,1400H        ; 1400H is 5,120 passes of 26 T-states\nDELAY:  DEC     BC\n        LD      A,B\n        OR      C\n        JR      NZ,DELAY\n        POP     BC              ; BC as the caller left it\n        RET\n\n; DRAWBASE: copy the three bytes HL points at to the base's place on row 15,\n; 3FC0H plus BASEX. BC is kept on the stack; A, DE and HL are not.\nDRAWBASE:\n        PUSH    BC              ; save BC, which LDIR counts down\n        LD      A,(BASEX)       ; the base's column ...\n        ADD     A,0C0H          ; ... plus 3FC0H, row 15: the base's left cell\n        LD      E,A\n        LD      D,3FH\n        LD      BC,3            ; three bytes\n        LDIR                    ; from (HL) to (DE), one byte at a time\n        POP     BC              ; BC as the caller left it\n        RET\n\n; MOVEBASE: with the left or right arrow down, erase the base, move it a\n; column, and draw it again. Columns 0 and 61 are the edges: there it stays.\nMOVEBASE:\n        LD      A,(3840H)       ; the arrows' row: bit 5 left, 6 right\n        AND     60H             ; keep those two bits (0110 0000)\n        RET     Z               ; neither is down: nothing to do\n        LD      C,A             ; keep the keys in C, so A is free\n        LD      HL,BLANK        ; erase the base where it is\n        CALL    DRAWBASE\n        LD      A,(BASEX)       ; the base's column, from memory\n        BIT     5,C             ; left?\n        JR      Z,NOTLT\n        CP      0               ; column 0, the left edge: stay\n        JR      Z,NOTLT\n        DEC     A\nNOTLT:\n        BIT     6,C             ; right?\n        JR      Z,NOTRT\n        CP      61              ; column 61, the right edge for three cells: stay\n        JR      Z,NOTRT\n        INC     A\nNOTRT:\n        LD      (BASEX),A       ; the new column, back to memory\n        LD      HL,BASE         ; draw the base there\n        CALL    DRAWBASE\n        RET\n\n; MOVESHOT: the shot, if there is one, moves up a row; past row 0 it is gone,\n; and moving into an invader it shoots it down, and is gone too.\nMOVESHOT:\n        LD      HL,(SHOT)       ; where the shot is: two bytes from memory into HL\n        LD      A,H             ; 0 is no shot: test HL as the clear tests BC\n        OR      L\n        RET     Z               ; no shot: nothing to do\n        LD      (HL),20H        ; erase the shot where it is\n        LD      DE,-64          ; up a row\n        ADD     HL,DE\n        BIT     2,H             ; still on the screen? 3C00H-3FFFH all have bit 2 of H set\n        JR      NZ,SHOTUP\n        LD      HL,0            ; off the top: no shot\nSHOTUP: LD      (SHOT),HL       ; keep where it is, or 0\n        LD      A,H\n        OR      L\n        RET     Z               ; gone: nothing to draw\n        LD      A,(HL)          ; what is in the cell the shot moves into?\n        CP      9EH             ; an invader's left cell: HL is its address\n        JR      Z,HIT\n        CP      0ADH            ; its right cell: its address is one before\n        JR      Z,HITRT\n        LD      (HL),21H        ; the shot is an exclamation mark\n        RET\nHITRT:  DEC     HL              ; back to the invader's left cell\nHIT:    LD      (HL),20H        ; erase the invader's two cells\n        INC     HL\n        LD      (HL),20H\n        DEC     HL              ; HL is the invader's address again\n        CALL    KILL            ; its place in the table becomes 0\n        CALL    ADDSCORE        ; 10 points\n        LD      HL,0            ; and the shot is gone too\n        LD      (SHOT),HL\n        RET\n\n; FIRE: with the space bar down and no shot in the air, a shot starts on\n; row 14, just above the base's middle cell.\nFIRE:   LD      A,(3840H)       ; the space bar is bit 7 of the arrows' row\n        BIT     7,A\n        RET     Z               ; not down: nothing to do\n        LD      HL,(SHOT)\n        LD      A,H\n        OR      L\n        RET     NZ              ; a shot is in the air already: one at a time\n        LD      A,(BASEX)       ; row 14 starts at 3F80H; the base's middle\n        ADD     A,81H           ; is its column plus one: 3F81H plus BASEX\n        LD      L,A\n        LD      H,3FH\n        LD      (HL),21H        ; draw the shot\n        LD      (SHOT),HL       ; and remember where it is\n        RET\n\n; DRAWROW: copy the two bytes DE points at to every invader's place: INVS\n; holds their screen addresses, a word each. IX walks the table and B counts\n; the invaders; BC is kept on the stack, and DE ends where it began.\nDRAWROW:\n        PUSH    BC              ; save BC: B counts the invaders\n        LD      IX,INVS         ; IX points at the table's first word\n        LD      B,NINV          ; NINV invaders, counted down by DJNZ\nDRAWONE:\n        LD      L,(IX+0)        ; this invader's screen address: the low byte\n        LD      H,(IX+1)        ; first, then the high, as a DW stores them\n        LD      A,H             ; 0 is an invader shot down: nothing to draw\n        OR      L\n        JR      Z,DRAWNEXT\n        LD      A,(DE)          ; the shape's left cell\n        LD      (HL),A\n        INC     HL\n        INC     DE\n        LD      A,(DE)          ; and its right cell\n        LD      (HL),A\n        DEC     DE              ; back to the shape's start, for the next one\nDRAWNEXT:\n        INC     IX              ; on to the next word of the table\n        INC     IX\n        DJNZ    DRAWONE         ; B minus 1, and the next invader until it is 0\n        POP     BC              ; BC as the caller left it\n        RET\n\n; MARCH: every fourth turn, erase the row and move it a column, the way STEP\n; says. At the edge the row is marching to, it turns round, and that step\n; goes down a row instead. An invader that comes down to row 13 has landed,\n; and the game is over.\nMARCH:  LD      HL,TICK         ; one turn fewer to the row's next step\n        DEC     (HL)\n        RET     NZ              ; not 0 yet: not this turn\n        LD      (HL),4          ; 0: a step now, and the next in four turns\n        LD      DE,BLANK        ; erase the row where it is\n        CALL    DRAWROW\n        LD      DE,(STEP)       ; the step: 1 right, or -1 left\n        CALL    EDGE            ; Z: an invader is at the edge ahead\n        JR      NZ,SIDE\n        LD      HL,0            ; turn round: the step becomes 0 minus the step\n        OR      A               ; clear the carry flag, which SBC takes off too\n        SBC     HL,DE\n        LD      (STEP),HL\n        LD      DE,64           ; a row is 64 cells: this step goes down\nSIDE:   CALL    MOVEROW         ; every invader moves by DE\n        CALL    LANDED          ; one on row 13 ends the game\n        RET\n\n; EDGE: is an invader at the edge the row is marching to? E is the step's\n; low byte, 1 or 0FFH. The low six bits of an invader's address are its\n; column; the next column would be 63 at the right edge, and -1 at the left,\n; and both are 3FH in six bits. Z if an invader is there, NZ if none is.\nEDGE:   LD      IX,INVS         ; IX walks the table, as in DRAWROW\n        LD      B,NINV\nEDGEONE:\n        LD      A,(IX+1)        ; 0 is an invader shot down: skip it\n        OR      (IX+0)\n        JR      Z,EDGENEXT\n        LD      A,(IX+0)        ; the low byte of the invader's address\n        ADD     A,E             ; plus the step\n        AND     3FH             ; its low six bits: the column it would move to\n        CP      3FH             ; 63, or -1: past an edge\n        RET     Z               ; this one is at the edge: return with Z\nEDGENEXT:\n        INC     IX\n        INC     IX\n        DJNZ    EDGEONE\n        OR      1               ; none is: A is not 0 after OR 1, so NZ\n        RET\n\n; MOVEROW: add DE to every invader's address in INVS: 1 moves the row a\n; column right, -1, the word 0FFFFH, a column left, and 64 a row down.\nMOVEROW:\n        LD      IX,INVS\n        LD      B,NINV\nMOVEONE:\n        LD      L,(IX+0)        ; the invader's address into HL\n        LD      H,(IX+1)\n        LD      A,H             ; 0 is an invader shot down: it stays 0\n        OR      L\n        JR      Z,MOVENEXT\n        ADD     HL,DE           ; plus the step\n        LD      (IX+0),L        ; and back into the table\n        LD      (IX+1),H\nMOVENEXT:\n        INC     IX\n        INC     IX\n        DJNZ    MOVEONE\n        RET\n\n; KILL: the invader at screen address HL is shot down: its place in INVS\n; becomes 0, which DRAWROW, EDGE and MOVEROW skip.\nKILL:   LD      IX,INVS\n        LD      B,NINV\nKILLONE:\n        LD      A,(IX+0)        ; this place's low byte: HL's?\n        CP      L\n        JR      NZ,KILLNEXT\n        LD      A,(IX+1)        ; and its high byte: HL's too?\n        CP      H\n        JR      NZ,KILLNEXT\n        LD      (IX+0),0        ; this is the invader: its place becomes 0\n        LD      (IX+1),0\n        RET\nKILLNEXT:\n        INC     IX\n        INC     IX\n        DJNZ    KILLONE\n        RET\n\n; ADDSCORE: 10 points more. SCORE is two bytes of BCD, two decimal digits in\n; each, four bits a digit: the low byte holds the last two digits. DAA after\n; each add puts the digits right, and its carry goes on to the high byte.\nADDSCORE:\n        LD      HL,SCORE        ; the last two digits\n        LD      A,(HL)\n        ADD     A,10H           ; plus 10: a 1 in the tens digit\n        DAA                     ; back to two decimal digits, carry past 99\n        LD      (HL),A\n        INC     HL              ; the first two digits (LD and INC HL keep C)\n        LD      A,(HL)\n        ADC     A,0             ; plus the carry from the last two\n        DAA\n        LD      (HL),A\n        RET\n\n; SHOWSCORE: print the lives at the end of row 0, and SCORE and its four\n; digits at the start. It runs every turn, so where a shot passes through\n; the top line, it is mended at once.\nSHOWSCORE:\n        LD      HL,LIVESTXT     ; the lives' word\n        LD      DE,3C39H        ; to row 0, column 57\n        LD      BC,6\n        LDIR                    ; DE ends on column 63, the row's last\n        LD      A,(LIVES)       ; the lives, 0 to 3: one digit\n        ADD     A,'0'\n        LD      (DE),A\n        LD      HL,SCORETXT     ; the word, from the table\n        LD      DE,3C00H        ; to row 0, column 0\n        LD      BC,6\n        LDIR                    ; DE ends on column 6, where the digits go\n        LD      HL,SCORE+1      ; the first two digits, in the high byte\n        CALL    TWODIG\n        DEC     HL              ; then the last two, in the low byte, and\n                                ; TWODIG's RET returns from SHOWSCORE\n; TWODIG: print the two digits of the byte HL points at, at DE, and move DE on.\nTWODIG: LD      A,(HL)          ; the byte: two digits\n        RRCA                    ; four rotates bring its high digit down\n        RRCA                    ; to the low four bits\n        RRCA\n        RRCA\n        AND     0FH             ; just that digit, 0 to 9\n        ADD     A,'0'           ; its character: '0' is 30H, '9' is 39H\n        LD      (DE),A\n        INC     DE\n        LD      A,(HL)          ; the byte again, for its low digit\n        AND     0FH\n        ADD     A,'0'\n        LD      (DE),A\n        INC     DE\n        RET\n\n; MOVEBOMB: the bomb, if there is one, falls a row. It is erased only if it\n; is still what is in its cell, and drawn only in a blank cell: anything else\n; there, an invader or the shot, it passes behind. Falling into the base, it\n; costs a life. With no bomb, DROP drops one.\nMOVEBOMB:\n        LD      HL,(BOMB)       ; where the bomb is, or 0 for none\n        LD      A,H\n        OR      L\n        JR      Z,DROP          ; no bomb: drop one\n        LD      A,(HL)          ; is the bomb still what is in its cell?\n        CP      BOMBCH\n        JR      NZ,FALL         ; no: something was drawn over it, leave that\n        LD      (HL),20H        ; yes: erase it\nFALL:   LD      DE,64           ; down a row\n        ADD     HL,DE\n        BIT     2,H             ; still on the screen, as for the shot?\n        JR      NZ,BOMBON\n        LD      HL,0            ; off the bottom: no bomb\nBOMBON: LD      (BOMB),HL       ; keep where it is, or 0\n        LD      A,H\n        OR      L\n        RET     Z               ; gone: nothing to draw\n        LD      A,(HL)          ; what is in the cell the bomb falls into?\n        CP      20H             ; a blank: draw the bomb there\n        JR      Z,BOMBDRAW\n        CP      0BCH            ; the base's wing\n        JR      Z,BASEHIT\n        CP      0BFH            ; or its middle\n        JR      Z,BASEHIT\n        RET                     ; anything else: the bomb passes behind it\nBOMBDRAW:\n        LD      (HL),BOMBCH\n        RET                     ; the bomb is in its new cell\nBASEHIT:\n        LD      HL,0            ; the bomb is gone\n        LD      (BOMB),HL\n        LD      HL,LIVES        ; and a life\n        DEC     (HL)\n        RET     NZ              ; lives left: play on\n        JP      OVER            ; none: the game is over\n\n; DROP: pick an invader with the R register, and drop a bomb from the cell\n; under its left cell. If the invader is shot down, no bomb this turn.\nDROP:   LD      A,R             ; R counts instruction fetches: 0 to 127 here\nWHICH:  CP      NINV            ; the remainder by NINV: carry when A is less\n        JR      C,PICKED\n        SUB     NINV            ; otherwise take NINV away and compare again\n        JR      WHICH\nPICKED: ADD     A,A             ; times 2: a word a place\n        LD      E,A\n        LD      D,0\n        LD      IX,INVS\n        ADD     IX,DE           ; IX points at the invader's place\n        LD      L,(IX+0)\n        LD      H,(IX+1)\n        LD      A,H             ; 0 is shot down: no bomb\n        OR      L\n        RET     Z\n        JR      FALL            ; the bomb starts a row below the invader\n\n; LANDED: has an invader come down to row 13? Then the game is over. Rows 12\n; to 15 are 3F00H to 3FFFH, and row 13 starts at 3F40H, so an address with\n; the high byte 3FH and the low byte 40H or more is on row 13 or below. A\n; place that is 0, shot down, has the high byte 0, and never has.\nLANDED: LD      IX,INVS\n        LD      B,NINV\nLANDONE:\n        LD      A,(IX+1)        ; the high byte: 3FH is rows 12 to 15\n        CP      3FH\n        JR      NZ,LANDNEXT\n        LD      A,(IX+0)        ; the low byte: 40H or more is row 13 or lower\n        CP      40H\n        JP      NC,OVER         ; landed: the game is over\nLANDNEXT:\n        INC     IX\n        INC     IX\n        DJNZ    LANDONE\n        RET\n\nSTART:  LD      HL,3C00H        ; HL points at the top-left cell\n        LD      BC,400H         ; BC counts the cells: 400H is 1024\nCLEAR:  LD      (HL),20H        ; a space: the cell is blank\n        INC     HL              ; on to the next cell\n        DEC     BC              ; one cell fewer to go (DEC BC sets no flags ...)\n        LD      A,B             ; ... so test BC by hand: copy B into A\n        OR      C               ; and OR in C: zero only when B and C both are\n        JR      NZ,CLEAR        ; not zero yet: clear the next cell\n        LD      HL,BASE         ; draw the base where BASEX says\n        CALL    DRAWBASE\n        CALL    SHOWSCORE       ; the top line,\n        LD      DE,INVADER      ; and the invaders where they start\n        CALL    DRAWROW\n        LD      HL,GOTXT        ; PRESS SPACE TO START\n        LD      DE,3E96H        ; to row 10, column 22\n        LD      BC,20\n        LDIR\nGO:     CALL    WAIT            ; a turn's pause, then look at the space bar\n        LD      A,(3840H)       ; the space bar is bit 7 of the arrows' row\n        BIT     7,A\n        JR      Z,GO            ; not down: wait another turn\n        LD      HL,3E96H        ; down: erase the words\n        LD      B,20\nUNSAY:  LD      (HL),20H\n        INC     HL\n        DJNZ    UNSAY\n\n; The game loop: one turn of the game, then round again. A game's turn\n; reads the keys, moves things and draws them: the base, the shot, the\n; invaders, the bomb, and the top line.\nLOOP:   CALL    WAIT            ; the turn's pause, so the game runs at a steady speed\n        CALL    MOVEBASE        ; the base moves if an arrow is down\n        CALL    MOVESHOT        ; the shot, if there is one, rises a row\n        CALL    FIRE            ; the space bar starts a shot, if there is none\n        CALL    MARCH           ; every fourth turn, the row steps\n        LD      DE,INVADER      ; the row, drawn every turn: a shot passing\n        CALL    DRAWROW         ; through an invader erases a cell, and this mends it\n        CALL    MOVEBOMB        ; the bomb falls, or a new one drops\n        CALL    SHOWSCORE       ; the lives and the score, on row 0\n        JR      LOOP            ; and round again, until the game is over\n\n; OVER: no lives left, or an invader has landed. The top line and the\n; invaders once more, then the words, and the program stops here: Run\n; starts a new game.\nOVER:   CALL    SHOWSCORE\n        LD      DE,INVADER      ; MARCH may have erased the invaders: draw them\n        CALL    DRAWROW\n        LD      HL,OVERTXT      ; GAME OVER\n        LD      DE,3C1BH        ; to row 0, column 27, between the two\n        LD      BC,9\n        LDIR\nSTOP:   JR      STOP            ; the game is over: wait here for good\n\n; The game's data: below the loop, where the processor never runs into it.\nBASE:   DB      0BCH,0BFH,0BCH  ; the base: a wing, a block, a wing\nBLANK:  DB      '   '           ; three spaces, to erase it\nINVADER:\n        DB      9EH,0ADH        ; an invader: two cells, a head, arms and legs\n; INVS: the invaders' places, a row's start plus a column, rows 2, 4 and 6;\n; 0 is shot down. NINV is how many: EQU names a number, and takes no memory.\nNINV:   EQU     24\nINVS:   DW      3C80H+14,3C80H+18,3C80H+22,3C80H+26\n        DW      3C80H+30,3C80H+34,3C80H+38,3C80H+42\n        DW      3D00H+14,3D00H+18,3D00H+22,3D00H+26\n        DW      3D00H+30,3D00H+34,3D00H+38,3D00H+42\n        DW      3D80H+14,3D80H+18,3D80H+22,3D80H+26\n        DW      3D80H+30,3D80H+34,3D80H+38,3D80H+42\nBASEX:  DB      30              ; the base's column, 0 to 61\nSHOT:   DW      0               ; the shot's screen address, or 0 for none\nSTEP:   DW      1               ; the row's step: 1 right, or -1 (0FFFFH) left\nTICK:   DB      4               ; turns to the row's next step\nSCORETXT:\n        DB      'SCORE '        ; the score's word, and a space\nSCORE:  DB      0,0             ; in BCD: the last two digits, then the first two\nLIVESTXT:\n        DB      'LIVES '        ; the lives' word, and a space\nLIVES:  DB      3               ; lives left\nBOMBCH: EQU     99H             ; the bomb: a zigzag, three blocks\nBOMB:   DW      0               ; the bomb's screen address, or 0 for none\nOVERTXT:\n        DB      'GAME OVER'\nGOTXT:  DB      'PRESS SPACE TO START'\n\n        END     START           ; the program starts at START\n",1792464471375]