[{"data":1,"prerenderedAt":620},["ShallowReactive",2],{"learn:invaders:11-bombs-lives":3},{"item":4,"marksAgainst":619},{"slug":5,"number":6,"title":7,"idea":8,"models":9,"body":12,"tryIt":532,"game":558,"cites":602,"program":618},"11-bombs-lives",11,"Invaders drop bombs, and the base has lives","a second thing falling, a choice from R, and a game that can end",[10,11],3,1,[13,32,36,40,204,207,235,252,259,262,276,279,291,293,326,329,343,345,362,369,372,382,384,400,403,419,421,455,458,481,483,508,511,518,521,525,528],{"kind":14,"inlines":15},"p",[16,19,22,24,26,28,30],{"kind":17,"text":18},"text","Now the invaders fight back. One of them, picked with the R register as lesson 4 picked a move, drops a bomb, a zigzag that falls a row every turn. A bomb that falls into the base costs a life; the lives, 3 to begin with, are at the top right, ",{"kind":20,"text":21},"code","LIVES 3",{"kind":17,"text":23},". With none left, ",{"kind":20,"text":25},"GAME OVER",{"kind":17,"text":27}," appears in the top line and the program stops. There is one bomb at a time, as there is one shot, and the next drops as soon as the last is gone. The invaders no longer stop above the base: one that comes down to row 13 has landed, and that ends the game too, as in the arcade game. And now that the game can be lost, it waits for you: ",{"kind":20,"text":29},"PRESS SPACE TO START",{"kind":17,"text":31},".",{"kind":33,"id":34,"text":35},"h2","what-changed","What changed",{"kind":14,"inlines":37},[38],{"kind":17,"text":39},"Chapter 10's program, with these changes:",{"kind":41,"items":42},"ul",[43,49,57,78,93,100,111,130,141,145,168,175],[44,47],{"kind":45,"text":46},"strong","~",{"kind":17,"text":48}," The comment at the top, lines 1 to 5, now describes chapter 11.",[50,51,53,55],{"kind":45,"text":46},{"kind":17,"text":52}," ",{"kind":20,"text":54},"MARCH",{"kind":17,"text":56},"'s comment: line 142 is changed and line 143 is new; an invader that comes down to row 13 ends the game.",[58,60,62,63,65,67,69,71,73,76],{"kind":45,"text":59},"−",{"kind":17,"text":61}," Six lines of ",{"kind":20,"text":54},{"kind":17,"text":64},", from ",{"kind":20,"text":66},"EX DE,HL",{"kind":17,"text":68}," to ",{"kind":20,"text":70},"LD (INVROW),A",{"kind":17,"text":72},", are taken out: the invaders no longer stop at a floor. Below the program, under ",{"kind":74,"text":75},"em","lines taken out",{"kind":17,"text":77},", they show struck through, before line 157.",[79,81,83,84,86,88,90,92],{"kind":45,"text":80},"+",{"kind":17,"text":82}," In ",{"kind":20,"text":54},{"kind":17,"text":85},", line 159: ",{"kind":20,"text":87},"CALL LANDED",{"kind":17,"text":89},", after ",{"kind":20,"text":91},"CALL MOVEROW",{"kind":17,"text":31},[94,95,96,98],{"kind":45,"text":46},{"kind":17,"text":52},{"kind":20,"text":97},"SHOWSCORE",{"kind":17,"text":99},"'s comment: lines 240 and 241 are changed and line 242 is new; it now prints the lives too.",[101,102,104,105,107,109],{"kind":45,"text":80},{"kind":17,"text":103}," Seven lines at the start of ",{"kind":20,"text":97},{"kind":17,"text":106},", lines 244 to 250: they print ",{"kind":20,"text":108},"LIVES ",{"kind":17,"text":110}," and the lives at the end of row 0.",[112,113,114,116,118,120,122,124,126,128],{"kind":45,"text":80},{"kind":17,"text":52},{"kind":20,"text":115},"MOVEBOMB",{"kind":17,"text":117},", ",{"kind":20,"text":119},"DROP",{"kind":17,"text":121}," and ",{"kind":20,"text":123},"LANDED",{"kind":17,"text":125},", new routines with their comments, after ",{"kind":20,"text":127},"TWODIG",{"kind":17,"text":129},", lines 276 to 354: the bomb falls, hits the base, or a new one drops; and an invader on row 13 ends the game.",[131,132,134,136,138,139],{"kind":45,"text":80},{"kind":17,"text":133}," At the end of ",{"kind":20,"text":135},"START",{"kind":17,"text":137},", lines 365 to 380: the top line and the invaders are drawn, and the program waits for the space bar under ",{"kind":20,"text":29},{"kind":17,"text":140},", then erases the words.",[142,143],{"kind":45,"text":46},{"kind":17,"text":144}," The game loop's comment: line 383 is changed and line 384 is new; a turn now moves the bomb and draws the top line too.",[146,147,149,151,153,155,157,159,161,163,164,166],{"kind":45,"text":46},{"kind":17,"text":148}," At the loop's end, lines 392 and 393 are changed and 394 to 406 are new: ",{"kind":20,"text":150},"CALL MOVEBOMB",{"kind":17,"text":152}," comes in before ",{"kind":20,"text":154},"CALL SHOWSCORE",{"kind":17,"text":156},", whose comment now names the lives, ",{"kind":20,"text":158},"JR LOOP",{"kind":17,"text":160},"'s comment says the loop goes round until the game is over, and after the loop come ",{"kind":20,"text":162},"OVER",{"kind":17,"text":121},{"kind":20,"text":165},"STOP",{"kind":17,"text":167},", the end of the game.",[169,170,171,173],{"kind":45,"text":59},{"kind":17,"text":52},{"kind":20,"text":172},"INVROW",{"kind":17,"text":174}," is taken out, with the floor; it shows struck through before line 426.",[176,177,178,180,181,183,184,186,187,189,190,192,193,195,196,198,200,201,202,203],{"kind":45,"text":80},{"kind":17,"text":52},{"kind":20,"text":179},"LIVESTXT",{"kind":17,"text":117},{"kind":20,"text":182},"LIVES",{"kind":17,"text":117},{"kind":20,"text":185},"BOMBCH",{"kind":17,"text":117},{"kind":20,"text":188},"BOMB",{"kind":17,"text":117},{"kind":20,"text":191},"OVERTXT",{"kind":17,"text":121},{"kind":20,"text":194},"GOTXT",{"kind":17,"text":89},{"kind":20,"text":197},"SCORE",{"kind":17,"text":199},", lines 429 to 436: the lives' word, the lives, the bomb's character, where the bomb is, and the words ",{"kind":20,"text":25},{"kind":17,"text":121},{"kind":20,"text":29},{"kind":17,"text":31},{"kind":33,"id":205,"text":206},"a-bomb-falls-as-the-shot-rises","A bomb falls as the shot rises",{"kind":14,"inlines":208},[209,210,212,214,216,217,219,221,223,225,227,229,231,233],{"kind":20,"text":188},{"kind":17,"text":211}," holds the bomb's screen address, or 0 for none, as ",{"kind":20,"text":213},"SHOT",{"kind":17,"text":215}," does for the shot, and ",{"kind":20,"text":115},{"kind":17,"text":218}," is ",{"kind":20,"text":220},"MOVESHOT",{"kind":17,"text":222}," turned upside down: it adds 64 instead of -64. The test for leaving the screen is the same ",{"kind":20,"text":224},"BIT 2,H",{"kind":17,"text":226},", because the address after the bottom row, ",{"kind":20,"text":228},"4000H",{"kind":17,"text":230},", has bit 2 of its high byte clear, as ",{"kind":20,"text":232},"3BFFH",{"kind":17,"text":234}," before the top row does.",{"kind":14,"inlines":236},[237,239,241,242,243,245,247,249,251],{"kind":17,"text":238},"The bomb's character is ",{"kind":20,"text":240},"99H",{"kind":17,"text":117},{"kind":20,"text":185},{"kind":17,"text":244},", a graphics byte lighting bits 0, 3 and 4: the top left block, the middle right and the bottom left, a zigzag (as Fugløy's screen draws them). ",{"kind":20,"text":246},"EQU",{"kind":17,"text":248}," names it, as it named ",{"kind":20,"text":250},"NINV",{"kind":17,"text":31},{"kind":14,"inlines":253},[254,256,257],{"kind":17,"text":255},"The bomb has to get past things the shot never met: it starts under an invader on row 2, 4 or 6, and the rows' invaders stand in the same columns, so a bomb from row 2 falls through rows 4 and 6. So ",{"kind":20,"text":115},{"kind":17,"text":258}," is careful in two ways. It erases the bomb only if the bomb is still what is in its cell, and it draws the bomb only in a blank cell:",{"kind":20,"lang":260,"text":261},"z80","        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\n...\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",{"kind":14,"inlines":263},[264,266,267,269,271,273,274],{"kind":17,"text":265},"So a bomb that falls into an invader's cell is not drawn, and the next turn, finding the invader there and not the bomb, it leaves the invader alone and falls on: it passes behind. The same happens with the shot. A shot and a bomb pass through each other; here a shot cannot stop a bomb. ",{"kind":20,"text":150},{"kind":17,"text":268}," comes after ",{"kind":20,"text":270},"CALL DRAWROW",{"kind":17,"text":272}," in the loop, so the invaders are on the screen when ",{"kind":20,"text":115},{"kind":17,"text":275}," looks.",{"kind":33,"id":277,"text":278},"a-bomb-in-the-base","A bomb in the base",{"kind":14,"inlines":280},[281,283,285,287,289],{"kind":17,"text":282},"If the cell the bomb falls into holds ",{"kind":20,"text":284},"0BCH",{"kind":17,"text":286},", a wing, or ",{"kind":20,"text":288},"0BFH",{"kind":17,"text":290},", the middle, the bomb has hit the base: nothing else on the screen uses those two characters, so, as in chapter 9, the screen says what was hit:",{"kind":20,"lang":260,"text":292},"        CP      0BCH            ; the base's wing\n        JR      Z,BASEHIT\n        CP      0BFH            ; or its middle\n        JR      Z,BASEHIT\n...\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",{"kind":14,"inlines":294},[295,297,299,300,302,304,306,307,309,311,313,314,316,318,320,321,323,324],{"kind":20,"text":296},"DEC (HL)",{"kind":17,"text":298}," takes 1 from the byte in memory and sets Z when it reaches 0, as ",{"kind":20,"text":296},{"kind":17,"text":301}," on ",{"kind":20,"text":303},"TICK",{"kind":17,"text":305}," does in ",{"kind":20,"text":54},{"kind":17,"text":308},". The base is not damaged; only the lives show the hit. A bomb is a hit when it falls into the base. A bomb that lands on the bottom row beside the base, and that the base then moves onto, is not: ",{"kind":20,"text":310},"MOVEBASE",{"kind":17,"text":312}," draws the base over it, and the next turn the bomb falls off the screen. ",{"kind":20,"text":97},{"kind":17,"text":315}," prints the lives as one digit, ",{"kind":20,"text":317},"'0'",{"kind":17,"text":319}," plus ",{"kind":20,"text":182},{"kind":17,"text":322},", the way ",{"kind":20,"text":127},{"kind":17,"text":325}," prints a digit.",{"kind":33,"id":327,"text":328},"which-invader-the-r-register","Which invader: the R register",{"kind":14,"inlines":330},[331,333,334,336,338,340,341],{"kind":17,"text":332},"When no bomb is falling, ",{"kind":20,"text":119},{"kind":17,"text":335}," picks an invader. R, as in lesson 4, is the Z80's refresh register: the processor adds 1 to its low seven bits at every opcode fetch, once for most instructions and twice for those with a prefix byte, as the ones using IX have. Bit 7 is left alone, and it is 0 here, so ",{"kind":20,"text":337},"LD A,R",{"kind":17,"text":339}," gives a number from 0 to 127. Lesson 4's remainder loop, with ",{"kind":20,"text":250},{"kind":17,"text":342}," for 3, makes it a place in the table, 0 to 23:",{"kind":20,"lang":260,"text":344},"DROP:   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",{"kind":14,"inlines":346},[347,349,351,353,355,357,359,360],{"kind":20,"text":348},"ADD IX,DE",{"kind":17,"text":350}," adds a pair to IX as ",{"kind":20,"text":352},"ADD HL,DE",{"kind":17,"text":354}," adds one to HL. If that place is 0, the invader is shot down and there is no bomb this turn; R will be different the next. Otherwise HL is the invader's address, and ",{"kind":20,"text":356},"JR FALL",{"kind":17,"text":358}," jumps into ",{"kind":20,"text":115},{"kind":17,"text":361}," to move it down a row and draw the bomb there, so a new bomb starts under the invader's left cell. As the invaders are shot down, more picks find a 0, and bombs come less often.",{"kind":14,"inlines":363},[364,366,367],{"kind":17,"text":365},"None of this is random: R is a count. What makes it change from game to game is you. Before the game starts, ",{"kind":20,"text":135},{"kind":17,"text":368}," waits for the space bar a turn at a time, so how many turns you take to press it changes R when the first bomb drops; after that, every key you press changes how many instructions run, and so which invader R picks next. Fugløy's checks on this chapter press the space bar at the same moment every time, and see the same game every time.",{"kind":33,"id":370,"text":371},"waiting-for-the-space-bar","Waiting for the space bar",{"kind":14,"inlines":373},[374,376,377,379,380],{"kind":17,"text":375},"Before the first turn, ",{"kind":20,"text":135},{"kind":17,"text":378}," draws the top line and the invaders, prints ",{"kind":20,"text":29},{"kind":17,"text":381}," on row 10, and waits:",{"kind":20,"lang":260,"text":383},"GO:     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",{"kind":14,"inlines":385},[386,388,390,392,394,396,398],{"kind":17,"text":387},"It is lesson 4's wait for a key, with ",{"kind":20,"text":389},"CALL WAIT",{"kind":17,"text":391}," in it, so it looks once a turn. Then a ",{"kind":20,"text":393},"DJNZ",{"kind":17,"text":395}," loop writes 20 spaces over the words, and the game starts. If you are still holding the space bar when the first turn comes round, ",{"kind":20,"text":397},"FIRE",{"kind":17,"text":399}," fires a shot at once.",{"kind":33,"id":401,"text":402},"landing","Landing",{"kind":14,"inlines":404},[405,407,408,410,411,413,414,416,417],{"kind":17,"text":406},"Chapter 10's invaders stopped coming down when the top row reached row 9, and marched along there for ever. Now there is no floor: ",{"kind":20,"text":54},{"kind":17,"text":409}," loses the six lines that kept ",{"kind":20,"text":172},{"kind":17,"text":412},", and ",{"kind":20,"text":172},{"kind":17,"text":415}," goes too, so every step at an edge goes down a row. After every step, ",{"kind":20,"text":123},{"kind":17,"text":418}," walks the table and looks for an invader on row 13:",{"kind":20,"lang":260,"text":420},"        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",{"kind":14,"inlines":422},[423,425,427,428,430,432,434,436,438,440,442,444,446,448,449,451,453],{"kind":17,"text":424},"Rows 12 to 15 are ",{"kind":20,"text":426},"3F00H",{"kind":17,"text":68},{"kind":20,"text":429},"3FFFH",{"kind":17,"text":431},", and row 13 starts at ",{"kind":20,"text":433},"3F40H",{"kind":17,"text":435},", so an address on row 13 has the high byte ",{"kind":20,"text":437},"3FH",{"kind":17,"text":439}," and the low byte ",{"kind":20,"text":441},"40H",{"kind":17,"text":443}," or more. ",{"kind":20,"text":445},"CP 40H",{"kind":17,"text":447}," sets the carry when A is less than ",{"kind":20,"text":441},{"kind":17,"text":450},", so ",{"kind":20,"text":452},"JP NC",{"kind":17,"text":454}," jumps when it is not. A place that is 0 has the high byte 0 and is never on row 13, so the shot-down invaders need no test of their own. It is the lowest invader still standing that lands: shoot the bottom row away and the two above it have further to come.",{"kind":33,"id":456,"text":457},"the-end-of-the-game","The end of the game",{"kind":14,"inlines":459},[460,462,464,466,467,469,470,472,473,475,476,478,479],{"kind":17,"text":461},"The game ends two ways: ",{"kind":20,"text":463},"BASEHIT",{"kind":17,"text":465}," with no lives left, and ",{"kind":20,"text":123},{"kind":17,"text":468},". Both jump to ",{"kind":20,"text":162},{"kind":17,"text":471},", which prints the top line once more, so that it shows the lives, draws the invaders, because ",{"kind":20,"text":54},{"kind":17,"text":474}," erases them before it moves them and ",{"kind":20,"text":123},{"kind":17,"text":477}," runs before they are drawn again, then copies ",{"kind":20,"text":25},{"kind":17,"text":480}," into the middle of row 0, and stops:",{"kind":20,"lang":260,"text":482},"STOP:   JR      STOP            ; the game is over: wait here for good",{"kind":14,"inlines":484},[485,487,489,490,491,492,494,495,497,499,501,503,505,506],{"kind":20,"text":486},"JR STOP",{"kind":17,"text":488}," jumps to itself, for ever, as lesson 1's program parked at its end. ",{"kind":20,"text":115},{"kind":17,"text":121},{"kind":20,"text":123},{"kind":17,"text":493}," are called, and jump to ",{"kind":20,"text":162},{"kind":17,"text":496}," with ",{"kind":20,"text":498},"JP",{"kind":17,"text":500},", not ",{"kind":20,"text":502},"CALL",{"kind":17,"text":504},", so return addresses are left on the stack; nothing will ever return, so they do no harm. To play again, press Run ▶: after ",{"kind":20,"text":25},{"kind":17,"text":507}," the program stays where it stopped.",{"kind":33,"id":509,"text":510},"the-cost","The cost",{"kind":14,"inlines":512},[513,515,516],{"kind":17,"text":514},"A falling bomb adds about 200 T-states to a turn, and printing the lives 178, so a turn with no key down is about 137,400 T-states. ",{"kind":20,"text":123},{"kind":17,"text":517}," runs only when the invaders step.",{"kind":33,"id":519,"text":520},"keys-go-to-the-screen-not-the-editor","Keys go to the screen, not the editor",{"kind":14,"inlines":522},[523],{"kind":17,"text":524},"Click the TRS-80's screen before you press the arrows or the space bar, so that it has the keyboard.",{"kind":33,"id":526,"text":527},"on-the-model-i","On the Model I",{"kind":14,"inlines":529},[530],{"kind":17,"text":531},"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.",[533,546],{"kind":14,"inlines":534},[535,537,539,540,542,543,544],{"kind":17,"text":536},"Press Run ▶, click the screen, and press the space bar: the words go (and if you held the bar down, a shot fires), and the first bomb drops. Stand still and the bombs find the base sooner or later, three times: ",{"kind":20,"text":538},"LIVES 2",{"kind":17,"text":117},{"kind":20,"text":541},"LIVES 1",{"kind":17,"text":412},{"kind":20,"text":25},{"kind":17,"text":545},". When that happens depends on when you pressed the space bar, so run it again and press it a little later, and the bombs fall somewhere else. Then play: dodge with the arrows.",{"kind":14,"inlines":547},[548,550,551,553,554,556,557],{"kind":17,"text":549},"Then take out the line ",{"kind":20,"text":150},{"kind":17,"text":552}," in the loop, and run it standing still: no bombs fall, and about 64 seconds after the space bar, on the Model III, the lowest row lands on row 13 and the top line says ",{"kind":20,"text":25},{"kind":17,"text":555},", with ",{"kind":20,"text":21},{"kind":17,"text":31},{"example":559,"addr":560,"name":561,"blocks":562},"sea-dragon",24336,"LOSE_SHIP",[563],{"kind":14,"inlines":564},[565,567,568,570,572,574,576,578,579,581,582,583,584,586,588,590,592,594,596,598,600],{"kind":17,"text":566},"Sea Dragon's ",{"kind":20,"text":561},{"kind":17,"text":569}," runs when the sub is lost. It takes one from ",{"kind":20,"text":571},"SHIPS",{"kind":17,"text":573},", adds ",{"kind":20,"text":575},"30H",{"kind":17,"text":577}," to make the count a digit, as ",{"kind":20,"text":97},{"kind":17,"text":580}," adds ",{"kind":20,"text":317},{"kind":17,"text":68},{"kind":20,"text":182},{"kind":17,"text":585},", and prints it on the bottom row, column 6 for player 1 or 55 for player 2. Then ",{"kind":20,"text":587},"CP 30H",{"kind":17,"text":589},": if the digit is ",{"kind":20,"text":591},"0",{"kind":17,"text":593},", it jumps to ",{"kind":20,"text":595},"GAME_OVER",{"kind":17,"text":597},", which prints ",{"kind":74,"text":599},"Game Over Player",{"kind":17,"text":601}," and the player's number.",[603,607,611,615],{"source":604,"page":605,"about":606},"m3-trm","14","the Model III's keyboard is memory at 3800H-3BFFH, and its screen at 3C00H",{"source":608,"page":609,"about":610},"m1-trh","12","the Model I's keyboard is memory from 3800H, and its screen at 3C00H",{"source":612,"page":613,"about":614},"decker","64","the keyboard's rows are read at 3801H to 3840H, one address a row",{"source":616,"page":617,"about":614},"paay","26","; 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","; Build Space Invaders, chapter 10: three rows, and a score. Chapter 9's\n; game, with 24 invaders in INVS, three rows of eight, which every loop over\n; the table counts with NINV. Each invader shot down is worth 10 points:\n; ADDSCORE adds them to SCORE, kept in BCD with DAA, and SHOWSCORE prints it\n; on row 0 every turn, four decimal digits.\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, until the lowest row is on row 13.\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        EX      DE,HL           ; and the new step into DE\n        LD      A,(INVROW)      ; the top row's row: the lowest is 4 below\n        CP      9               ; row 9, so the lowest is on row 13?\n        JR      Z,SIDE          ; then they only turn, and step sideways\n        INC     A               ; one row lower\n        LD      (INVROW),A\n        LD      DE,64           ; a row is 64 cells: this step goes down\nSIDE:   CALL    MOVEROW         ; every invader moves by DE\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 SCORE and its four digits at the start of row 0. It runs\n; every turn, so where a shot passes through the score, it is mended at once.\nSHOWSCORE:\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\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\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 invaders.\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    SHOWSCORE       ; the score, on row 0\n        JR      LOOP            ; and round again, for ever\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\nINVROW: DB      2               ; the top row's row, 2 to 9\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\n\n        END     START           ; the program starts at START\n",1792464471356]