[{"data":1,"prerenderedAt":381},["ShallowReactive",2],{"learn:invaders:09-hit":3},{"item":4,"marksAgainst":380},{"slug":5,"number":6,"title":7,"idea":8,"models":9,"body":12,"tryIt":349,"game":362,"cites":363,"program":379},"09-hit",9,"The shot hits an invader","a collision read off the screen, and a hole in the table that loops skip",[10,11],3,1,[13,19,23,27,126,130,133,140,143,166,168,182,185,203,205,219,222,239,241,273,284,287,313,316,335,338,342,345],{"kind":14,"inlines":15},"p",[16],{"kind":17,"text":18},"text","Now the shot hits. A shot that moves into an invader's cell shoots it down: the invader vanishes, and so does the shot, and the space bar can fire again. The invader stays gone, a gap in the marching row. The test for a hit reads the screen, and the invader's place in the table becomes 0, which every loop over the table now steps over.",{"kind":20,"id":21,"text":22},"h2","what-changed","What changed",{"kind":14,"inlines":24},[25],{"kind":17,"text":26},"Chapter 8's program, with these changes:",{"kind":28,"items":29},"ul",[30,36,45,53,75,87,98,109,118],[31,34],{"kind":32,"text":33},"strong","~",{"kind":17,"text":35}," The comment at the top, lines 1 to 5, now describes chapter 9.",[37,38,40,43],{"kind":32,"text":33},{"kind":17,"text":39}," ",{"kind":41,"text":42},"code","MOVESHOT",{"kind":17,"text":44},"'s comment: line 61 is changed and line 62 is new; it now says that a shot moving into an invader shoots it down.",[46,48,50,51],{"kind":32,"text":47},"+",{"kind":17,"text":49}," Five lines in ",{"kind":41,"text":42},{"kind":17,"text":52},", lines 78 to 82, before it draws the shot: they look at the cell the shot moves into.",[54,55,56,58,60,62,64,65,67,69,71,73],{"kind":32,"text":47},{"kind":17,"text":39},{"kind":41,"text":57},"HITRT",{"kind":17,"text":59}," and ",{"kind":41,"text":61},"HIT",{"kind":17,"text":63},", after ",{"kind":41,"text":42},{"kind":17,"text":66},"'s last ",{"kind":41,"text":68},"RET",{"kind":17,"text":70},", lines 85 to 93: they erase the invader, call ",{"kind":41,"text":72},"KILL",{"kind":17,"text":74},", and end the shot.",[76,77,79,81,83,85],{"kind":32,"text":47},{"kind":17,"text":78}," In ",{"kind":41,"text":80},"DRAWROW",{"kind":17,"text":82},", three lines, 122 to 124, and the label ",{"kind":41,"text":84},"DRAWNEXT",{"kind":17,"text":86}," on line 132: a place that is 0 is not drawn.",[88,89,90,92,94,96],{"kind":32,"text":47},{"kind":17,"text":78},{"kind":41,"text":91},"EDGE",{"kind":17,"text":93},", three lines, 172 to 174, and the label ",{"kind":41,"text":95},"EDGENEXT",{"kind":17,"text":97}," on line 180: a place that is 0 is never at an edge.",[99,100,101,103,105,107],{"kind":32,"text":47},{"kind":17,"text":78},{"kind":41,"text":102},"MOVEROW",{"kind":17,"text":104},", three lines, 195 to 197, and the label ",{"kind":41,"text":106},"MOVENEXT",{"kind":17,"text":108}," on line 201: a place that is 0 does not move.",[110,111,112,113,115,116],{"kind":32,"text":47},{"kind":17,"text":39},{"kind":41,"text":72},{"kind":17,"text":114},", a new routine with its comment, after ",{"kind":41,"text":102},{"kind":17,"text":117},", lines 207 to 226: it finds the invader's place in the table and makes it 0.",[119,120,122,124],{"kind":32,"text":33},{"kind":17,"text":121}," The comment above ",{"kind":41,"text":123},"INVS",{"kind":17,"text":125},", line 254: a place can now be 0, an invader shot down.",{"kind":14,"inlines":127},[128],{"kind":17,"text":129},"Nothing is taken out.",{"kind":20,"id":131,"text":132},"a-hit-read-off-the-screen","A hit, read off the screen",{"kind":14,"inlines":134},[135,137,138],{"kind":17,"text":136},"Before ",{"kind":41,"text":42},{"kind":17,"text":139}," draws the shot in its new cell, it looks at what is there:",{"kind":41,"lang":141,"text":142},"z80","        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",{"kind":14,"inlines":144},[145,147,149,151,153,155,157,159,160,162,164],{"kind":17,"text":146},"The screen is memory, so the cell holds whatever was last put there, and nothing but an invader puts ",{"kind":41,"text":148},"9EH",{"kind":17,"text":150}," or ",{"kind":41,"text":152},"0ADH",{"kind":17,"text":154}," on the screen. The loop drew the row at the end of the turn before, so the screen is up to date. ",{"kind":41,"text":156},"CP",{"kind":17,"text":158}," compares A with a byte and sets only the flags; A is unchanged, so the second ",{"kind":41,"text":156},{"kind":17,"text":161}," tests the same character. In JavaScript the test is ",{"kind":41,"text":163},"if (screen[hl] === 0x9E || screen[hl] === 0xAD)",{"kind":17,"text":165},", but with the two answers kept apart, because they say different things about where the invader starts: at HL for its left cell, at HL minus 1 for its right.",{"kind":41,"lang":141,"text":167},"HITRT:  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        LD      HL,0            ; and the shot is gone too\n        LD      (SHOT),HL",{"kind":14,"inlines":169},[170,172,173,175,176,178,180],{"kind":17,"text":171},"A hit on the right cell steps HL back one and falls through into ",{"kind":41,"text":61},{"kind":17,"text":174},", so both cases end with HL at the invader's address, which is what the table holds. The shot was erased from its old cell at the start of ",{"kind":41,"text":42},{"kind":17,"text":177}," and is never drawn in the new one; ",{"kind":41,"text":179},"SHOT",{"kind":17,"text":181}," becomes 0, as when a shot leaves the top of the screen.",{"kind":20,"id":183,"text":184},"kill-makes-a-hole-in-the-table","KILL makes a hole in the table",{"kind":14,"inlines":186},[187,188,190,191,193,195,197,198,200,201],{"kind":41,"text":72},{"kind":17,"text":189}," walks ",{"kind":41,"text":123},{"kind":17,"text":192}," with IX and ",{"kind":41,"text":194},"DJNZ",{"kind":17,"text":196},", as ",{"kind":41,"text":80},{"kind":17,"text":199}," does, comparing each place with HL a byte at a time, since ",{"kind":41,"text":156},{"kind":17,"text":202}," compares only A with a byte:",{"kind":41,"lang":141,"text":204},"        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",{"kind":14,"inlines":206},[207,209,211,214,216,217],{"kind":41,"text":208},"LD (IX+0),0",{"kind":17,"text":210}," stores a number straight into memory through IX, 19 T-states. 0 is never on the screen, so it means ",{"kind":212,"text":213},"em","no invader here",{"kind":17,"text":215},", as 0 in ",{"kind":41,"text":179},{"kind":17,"text":218}," means no shot.",{"kind":20,"id":220,"text":221},"every-loop-skips-the-hole","Every loop skips the hole",{"kind":14,"inlines":223},[224,226,227,229,231,232,234,236,237],{"kind":17,"text":225},"A place of 0 must not be drawn, must not count for an edge, and must not move: ",{"kind":41,"text":102},{"kind":17,"text":228}," would add the step to 0, making 1, ",{"kind":41,"text":230},"0FFFFH",{"kind":17,"text":150},{"kind":41,"text":233},"0040H",{"kind":17,"text":235},", none of them on the screen, and the next ",{"kind":41,"text":80},{"kind":17,"text":238}," would write an invader there. So each of the three loops tests the place first and jumps to the next one if it is 0:",{"kind":41,"lang":141,"text":240},"        LD      A,H             ; 0 is an invader shot down: nothing to draw\n        OR      L\n        JR      Z,DRAWNEXT",{"kind":14,"inlines":242},[243,244,245,246,248,249,251,252,254,256,257,259,261,262,264,265,266,267,269,271],{"kind":41,"text":80},{"kind":17,"text":59},{"kind":41,"text":102},{"kind":17,"text":247}," have the place in HL already and test it as ",{"kind":41,"text":42},{"kind":17,"text":250}," tests the shot; ",{"kind":41,"text":91},{"kind":17,"text":253}," reads it with ",{"kind":41,"text":255},"LD A,(IX+1)",{"kind":17,"text":59},{"kind":41,"text":258},"OR (IX+0)",{"kind":17,"text":260},". The labels ",{"kind":41,"text":84},{"kind":17,"text":263},", ",{"kind":41,"text":95},{"kind":17,"text":59},{"kind":41,"text":106},{"kind":17,"text":268}," mark each loop's ",{"kind":41,"text":270},"INC IX",{"kind":17,"text":272},", where the next place starts.",{"kind":14,"inlines":274},[275,277,278,280,282],{"kind":17,"text":276},"Because ",{"kind":41,"text":91},{"kind":17,"text":279}," skips the holes, the row turns when a ",{"kind":212,"text":281},"living",{"kind":17,"text":283}," invader reaches an edge. Shoot down the invaders at the end of the row and the rest march farther before they turn, as in the arcade game.",{"kind":20,"id":285,"text":286},"when-an-invader-still-covers-the-shot","When an invader still covers the shot",{"kind":14,"inlines":288},[289,290,292,294,296,298,300,301,303,305,307,308,310,311],{"kind":41,"text":42},{"kind":17,"text":291}," looks at the cell the shot moves into before the invaders move: in the loop, ",{"kind":41,"text":293},"CALL MOVESHOT",{"kind":17,"text":295}," comes before ",{"kind":41,"text":297},"CALL MARCH",{"kind":17,"text":299},". So on a turn when the row steps, a shot that has just risen into a gap on the invaders' row can have the neighbour beside it step onto it, and a row coming down can land on a shot just below it. Then ",{"kind":41,"text":80},{"kind":17,"text":302}," draws the invader over the ",{"kind":41,"text":304},"!",{"kind":17,"text":306},"; on the next turn ",{"kind":41,"text":42},{"kind":17,"text":309}," erases the shot's cell, now part of the invader, and the shot flies on, and ",{"kind":41,"text":80},{"kind":17,"text":312}," mends the invader. That shot goes behind, as every shot did before this chapter. Sideways, it happens to about one shot in eight of those that rise through a gap: the row steps on one turn in four, and only one of the gap's two cells is stepped onto. Looking at the shot's own cell, too, before it moves would catch it; this chapter keeps to the one test.",{"kind":20,"id":314,"text":315},"the-cost","The cost",{"kind":14,"inlines":317},[318,320,321,323,325,327,329,331,333],{"kind":17,"text":319},"Looking at a place costs 15 T-states in ",{"kind":41,"text":80},{"kind":17,"text":322}," (",{"kind":41,"text":324},"LD A,H",{"kind":17,"text":326}," 4, ",{"kind":41,"text":328},"OR L",{"kind":17,"text":330}," 4, and ",{"kind":41,"text":332},"JR Z",{"kind":17,"text":334}," 7 when it does not jump), so drawing the row every turn costs 120 more, and a turn with no key down is now 134,513 T-states.",{"kind":20,"id":336,"text":337},"keys-go-to-the-screen-not-the-editor","Keys go to the screen, not the editor",{"kind":14,"inlines":339},[340],{"kind":17,"text":341},"Click the TRS-80's screen before you press the arrows or the space bar, so that it has the keyboard.",{"kind":20,"id":343,"text":344},"on-the-model-i","On the Model I",{"kind":14,"inlines":346},[347],{"kind":17,"text":348},"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.",[350,354],{"kind":14,"inlines":351},[352],{"kind":17,"text":353},"Press Run ▶, click the screen, and fire without moving: the shot rises in column 31. If an invader is over that column when the shot reaches row 2, it is shot down, and a gap marches in the row where it was; if the shot arrives between two invaders, it goes on up past them, and you can fire again. Then shoot down the invader at the row's left end: once it is gone, the row marches four columns farther left before it turns.",{"kind":14,"inlines":355},[356,358,360],{"kind":17,"text":357},"Then take out the line ",{"kind":41,"text":359},"JR Z,HITRT",{"kind":17,"text":361},". Now only an invader's left cell stops the shot: a shot moving into a right cell is drawn over by the invader in the same turn, so the shot seems to pass behind it, and the invader is not shot down.",null,[364,368,372,376],{"source":365,"page":366,"about":367},"m3-trm","14","the Model III's keyboard is memory at 3800H-3BFFH, and its screen at 3C00H",{"source":369,"page":370,"about":371},"m1-trh","12","the Model I's keyboard is memory from 3800H, and its screen at 3C00H",{"source":373,"page":374,"about":375},"decker","64","the keyboard's rows are read at 3801H to 3840H, one address a row",{"source":377,"page":378,"about":375},"paay","26","; Build Space Invaders, chapter 9: the shot hits. Chapter 8's base, shot\n; and descending row, and now a shot that moves into an invader's cell shoots\n; it down: MOVESHOT sees the invader's character there, erases the invader,\n; and KILL makes its place in the table 0. DRAWROW, EDGE and MOVEROW skip a\n; place that is 0, so the invader is gone for good, and so is the shot.\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        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,8             ; eight 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, as far as 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 row the invaders are on\n        CP      13              ; row 13, the lowest they go for now?\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,8\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,8\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,8\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\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        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, row 2's start plus a column; 0 is shot down.\nINVS:   DW      3C80H+14,3C80H+18,3C80H+22,3C80H+26\n        DW      3C80H+30,3C80H+34,3C80H+38,3C80H+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 row the invaders are on, 2 to 13\n\n        END     START           ; the program starts at START\n","; Build Space Invaders, chapter 8: the row inches down. Chapter 7's base,\n; shot and marching row, and now, when the row reaches an edge, it turns\n; round and steps down a row instead of sideways, so each crossing of the\n; screen brings the invaders a row nearer the base. INVROW keeps the screen\n; row they are on; for now they go no lower than row 13.\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.\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      (HL),21H        ; the shot is an exclamation mark\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,8             ; eight 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,(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\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, as far as 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 row the invaders are on\n        CP      13              ; row 13, the lowest they go for now?\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,8\nEDGEONE:\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\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,8\nMOVEONE:\n        LD      L,(IX+0)        ; the invader's address into HL\n        LD      H,(IX+1)\n        ADD     HL,DE           ; plus the step\n        LD      (IX+0),L        ; and back into the table\n        LD      (IX+1),H\n        INC     IX\n        INC     IX\n        DJNZ    MOVEONE\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        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, each row 2's start, 3C80H, plus a column.\nINVS:   DW      3C80H+14,3C80H+18,3C80H+22,3C80H+26\n        DW      3C80H+30,3C80H+34,3C80H+38,3C80H+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 row the invaders are on, 2 to 13\n\n        END     START           ; the program starts at START\n",1792464471336]