[{"data":1,"prerenderedAt":338},["ShallowReactive",2],{"learn:invaders:07-march":3},{"item":4,"marksAgainst":337},{"slug":5,"number":6,"title":7,"idea":8,"models":9,"body":12,"tryIt":285,"game":319,"cites":320,"program":336},"07-march",7,"The row marches left and right","a word that is 1 or -1, a turn counter in memory, and SBC to turn round",[10,11],3,1,[13,19,23,27,83,87,90,100,103,121,124,129,131,144,146,164,167,182,184,206,209,216,218,241,261,264,271,274,278,281],{"kind":14,"inlines":15},"p",[16],{"kind":17,"text":18},"text","The row moves. Every fourth turn it steps a column to the right, and when its last invader reaches the right edge of the screen it turns round and marches back to the left, and at the left edge turns again. Because chapter 6 put every invader's place in a table, moving the row is one loop that adds the same step to every place.",{"kind":20,"id":21,"text":22},"h2","what-changed","What changed",{"kind":14,"inlines":24},[25],{"kind":17,"text":26},"Chapter 6's program, with these changes:",{"kind":28,"items":29},"ul",[30,36,58,69],[31,34],{"kind":32,"text":33},"strong","~",{"kind":17,"text":35}," The comment at the top, lines 1 to 5, now describes chapter 7.",[37,39,41,44,46,48,50,52,54,56],{"kind":32,"text":38},"+",{"kind":17,"text":40}," ",{"kind":42,"text":43},"code","MARCH",{"kind":17,"text":45},", ",{"kind":42,"text":47},"EDGE",{"kind":17,"text":49}," and ",{"kind":42,"text":51},"MOVEROW",{"kind":17,"text":53},", three new routines with their comments, below ",{"kind":42,"text":55},"DRAWROW",{"kind":17,"text":57},", lines 120 to 172: the row's step, the test for an edge, and the move.",[59,60,61,63,65,67],{"kind":32,"text":38},{"kind":17,"text":40},{"kind":42,"text":62},"CALL MARCH",{"kind":17,"text":64}," in the loop, line 190, after ",{"kind":42,"text":66},"CALL FIRE",{"kind":17,"text":68},".",[70,71,72,74,75,77,79,81],{"kind":32,"text":38},{"kind":17,"text":40},{"kind":42,"text":73},"STEP",{"kind":17,"text":49},{"kind":42,"text":76},"TICK",{"kind":17,"text":78},", after ",{"kind":42,"text":80},"SHOT",{"kind":17,"text":82},", lines 205 and 206: the row's step and its turn counter.",{"kind":14,"inlines":84},[85],{"kind":17,"text":86},"Nothing is taken out.",{"kind":20,"id":88,"text":89},"every-fourth-turn","Every fourth turn",{"kind":14,"inlines":91},[92,94,95,97,98],{"kind":17,"text":93},"A turn is about 66 ms, and a row stepping every turn would cross the screen in about two seconds. ",{"kind":42,"text":76},{"kind":17,"text":96}," counts turns down from 4, and ",{"kind":42,"text":43},{"kind":17,"text":99}," does its work only when the count reaches 0:",{"kind":42,"lang":101,"text":102},"z80","MARCH:  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",{"kind":14,"inlines":104},[105,107,109,111,113,115,117,119],{"kind":42,"text":106},"DEC (HL)",{"kind":17,"text":108}," takes 1 from the byte HL points at, in memory, and sets the zero flag when the result is 0, so ",{"kind":42,"text":110},"RET NZ",{"kind":17,"text":112}," returns on three turns of four. On the fourth, ",{"kind":42,"text":114},"LD (HL),4",{"kind":17,"text":116}," starts the count again. Lesson 7 counted turns in B with ",{"kind":42,"text":118},"DJNZ",{"kind":17,"text":120},"; a byte in memory is free for the rest of the turn to use B, and a later chapter can change how fast the row marches by changing the 4.",{"kind":20,"id":122,"text":123},"a-step-of-1-or-1","A step of 1 or -1",{"kind":14,"inlines":125},[126,127],{"kind":42,"text":73},{"kind":17,"text":128}," is the row's direction, a word:",{"kind":42,"lang":101,"text":130},"STEP:   DW      1               ; the row's step: 1 right, or -1 (0FFFFH) left",{"kind":14,"inlines":132},[133,135,136,138,139,141,142],{"kind":17,"text":134},"Moving the row adds the step to every invader's address. ",{"kind":42,"text":51},{"kind":17,"text":137}," walks the table as ",{"kind":42,"text":55},{"kind":17,"text":140}," does, with IX and ",{"kind":42,"text":118},{"kind":17,"text":143},", and for each invader loads its address into HL, adds DE, and stores it back:",{"kind":42,"lang":101,"text":145},"        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",{"kind":14,"inlines":147},[148,150,152,154,155,157,158,160,162],{"kind":17,"text":149},"Going right, DE is 1. Going left it is -1, which as a word is ",{"kind":42,"text":151},"0FFFFH",{"kind":17,"text":153},": adding ",{"kind":42,"text":151},{"kind":17,"text":156}," to an address wraps past ",{"kind":42,"text":151},{"kind":17,"text":159}," and comes out one less, as adding -64 came out a row up in chapter 4. One ",{"kind":42,"text":161},"ADD HL,DE",{"kind":17,"text":163}," moves the invader either way.",{"kind":20,"id":165,"text":166},"the-edge-ahead","The edge ahead",{"kind":14,"inlines":168},[169,171,172,174,176,178,180],{"kind":17,"text":170},"The row must turn when an invader is at the edge it is marching to: column 62 going right, since an invader is two cells wide and column 63 is the last, or column 0 going left. ",{"kind":42,"text":47},{"kind":17,"text":173}," finds the column without dividing anything. A row is 64 cells, ",{"kind":42,"text":175},"40H",{"kind":17,"text":177},", and every row starts at an address whose low six bits are 0, so the low six bits of an invader's address are its column. Add the step, and the column it would move to is 63 at the right edge and -1 at the left, and in six bits both are ",{"kind":42,"text":179},"3FH",{"kind":17,"text":181},", all ones:",{"kind":42,"lang":101,"text":183},"        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",{"kind":14,"inlines":185},[186,188,190,192,194,196,197,199,201,203,204],{"kind":17,"text":187},"E is the step's low byte, 1 or ",{"kind":42,"text":189},"0FFH",{"kind":17,"text":191},". ",{"kind":42,"text":193},"CP 3FH",{"kind":17,"text":195}," compares A with ",{"kind":42,"text":179},{"kind":17,"text":198}," and sets only the flags; A is unchanged. If no invader is at an edge the loop ends, and ",{"kind":42,"text":200},"OR 1",{"kind":17,"text":202}," makes the zero flag clear for certain, since A is not 0 after it: ",{"kind":42,"text":47},{"kind":17,"text":205}," returns Z for an edge and NZ for none.",{"kind":20,"id":207,"text":208},"turning-round","Turning round",{"kind":14,"inlines":210},[211,213,214],{"kind":17,"text":212},"At an edge, ",{"kind":42,"text":43},{"kind":17,"text":215}," makes the step 0 minus the step, 1 into -1 or -1 into 1:",{"kind":42,"lang":101,"text":217},"        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",{"kind":14,"inlines":219},[220,222,224,226,228,230,232,234,236,238,239],{"kind":17,"text":221},"The Z80 has no 16-bit ",{"kind":42,"text":223},"SUB",{"kind":17,"text":225},"; ",{"kind":42,"text":227},"SBC HL,DE",{"kind":17,"text":229}," is HL minus DE minus the carry flag. ",{"kind":42,"text":231},"OR A",{"kind":17,"text":233}," leaves A as it is and clears the carry, so the subtraction is exact. ",{"kind":42,"text":235},"EX DE,HL",{"kind":17,"text":237}," swaps DE and HL, putting the new step in DE for ",{"kind":42,"text":51},{"kind":17,"text":240},", so the row steps back the other way on the same turn it turns.",{"kind":14,"inlines":242},[243,245,246,248,250,252,253,255,256,258,259],{"kind":17,"text":244},"Before moving, ",{"kind":42,"text":43},{"kind":17,"text":247}," erases the whole row with ",{"kind":42,"text":249},"BLANK",{"kind":17,"text":251},"'s spaces, ",{"kind":42,"text":55},{"kind":17,"text":254}," with DE at ",{"kind":42,"text":249},{"kind":17,"text":257},". The loop's ",{"kind":42,"text":55},{"kind":17,"text":260}," after it draws the row in its new place in the same turn.",{"kind":20,"id":262,"text":263},"the-cost","The cost",{"kind":14,"inlines":265},[266,268,269],{"kind":17,"text":267},"On three turns of four, ",{"kind":42,"text":62},{"kind":17,"text":270}," costs 49 T-states, and a turn with no key down is 134,393. On the fourth, the erase, the edge test and the move add 2,709 more, for a turn of 137,102 T-states. Four turns are 540,281 T-states; Fugløy runs the Model III at 2.03 MHz, so the row steps about every 266 ms, a little under four steps a second.",{"kind":20,"id":272,"text":273},"keys-go-to-the-screen-not-the-editor","Keys go to the screen, not the editor",{"kind":14,"inlines":275},[276],{"kind":17,"text":277},"Click the TRS-80's screen before you press the arrows or the space bar, so that it has the keyboard.",{"kind":20,"id":279,"text":280},"on-the-model-i","On the Model I",{"kind":14,"inlines":282},[283],{"kind":17,"text":284},"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, so the row steps about every 305 ms there.",[286,290],{"kind":14,"inlines":287},[288],{"kind":17,"text":289},"Press Run ▶ and watch: the row starts at column 14, its last invader reaches the right edge after 20 steps, about five seconds, and it marches back to the left edge, 34 steps more, and turns again.",{"kind":14,"inlines":291},[292,294,295,297,299,300,301,303,305,306,308,310,311,312,314,315,317],{"kind":17,"text":293},"Then make it hurry: change ",{"kind":42,"text":114},{"kind":17,"text":296}," to ",{"kind":42,"text":298},"LD (HL),1",{"kind":17,"text":49},{"kind":42,"text":76},{"kind":17,"text":302},"'s ",{"kind":42,"text":304},"DB 4",{"kind":17,"text":296},{"kind":42,"text":307},"DB 1",{"kind":17,"text":309},". The row steps every turn, as fast as the base moves. Or change ",{"kind":42,"text":73},{"kind":17,"text":302},{"kind":42,"text":313},"DW 1",{"kind":17,"text":296},{"kind":42,"text":316},"DW -1",{"kind":17,"text":318},", and the row starts by marching left.",null,[321,325,329,333],{"source":322,"page":323,"about":324},"m3-trm","14","the Model III's keyboard is memory at 3800H-3BFFH, and its screen at 3C00H",{"source":326,"page":327,"about":328},"m1-trh","12","the Model I's keyboard is memory from 3800H, and its screen at 3C00H",{"source":330,"page":331,"about":332},"decker","64","the keyboard's rows are read at 3801H to 3840H, one address a row",{"source":334,"page":335,"about":332},"paay","26","; Build Space Invaders, chapter 7: the row marches. Chapter 6's base, shot\n; and row of eight invaders, and now every fourth turn MARCH moves the row a\n; column, the way STEP says, right or left. When an invader reaches the edge\n; the row is marching to, EDGE says so, and the row turns round and marches\n; back. The invaders still let a shot go behind them.\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 first.\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\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: a step of 1 moves the\n; row a column right, and -1, the word 0FFFFH, a column left.\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\n\n        END     START           ; the program starts at START\n","; Build Space Invaders, chapter 6: a row of invaders. Chapter 5's base and\n; shot, and on row 2 eight invaders, four cells apart. Where each one is, is\n; a word in a table, INVS: its screen address. DRAWROW walks the table with\n; IX and draws each; the loop draws the row every turn, so a shot still goes\n; behind the invaders. They stand still for now, and marching comes next.\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\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        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\n\n        END     START           ; the program starts at START\n",1792464471303]