[{"data":1,"prerenderedAt":299},["ShallowReactive",2],{"learn:invaders:05-one-invader":3},{"item":4,"marksAgainst":298},{"slug":5,"number":6,"title":7,"idea":8,"models":9,"body":12,"tryIt":248,"game":280,"cites":281,"program":297},"05-one-invader",5,"One invader","a second thing drawn by a routine like the first, and redrawn every turn",[10,11],3,1,[13,19,23,27,96,100,103,114,117,139,142,146,148,178,180,190,193,197,199,216,234,237,241,244],{"kind":14,"inlines":15},"p",[16],{"kind":17,"text":18},"text","The first invader arrives: two cells on row 2, near the top, straight above where the base starts. It does not move, and a shot passes behind it; it is here to be drawn, the way everything else in the game is drawn, by a routine, from a few bytes, at a place kept in memory. The next chapters give it company, then make the company march.",{"kind":20,"id":21,"text":22},"h2","what-changed","What changed",{"kind":14,"inlines":24},[25],{"kind":17,"text":26},"Chapter 4's program, with these changes:",{"kind":28,"items":29},"ul",[30,36,50,59,75,86],[31,34],{"kind":32,"text":33},"strong","~",{"kind":17,"text":35}," The comment at the top, lines 1 to 5, now describes chapter 5.",[37,39,41,44,46,48],{"kind":32,"text":38},"+",{"kind":17,"text":40}," ",{"kind":42,"text":43},"code","DRAWINV",{"kind":17,"text":45},", a new routine with its comment, below ",{"kind":42,"text":47},"FIRE",{"kind":17,"text":49},": it copies two bytes to the invader's place on row 2.",[51,52,54,57],{"kind":32,"text":33},{"kind":17,"text":53}," The game loop's comment: its last line now ends ",{"kind":55,"text":56},"em","the base, the shot, the invader",{"kind":17,"text":58},".",[60,61,63,65,67,69,71,73],{"kind":32,"text":38},{"kind":17,"text":62}," Two lines in the loop, after ",{"kind":42,"text":64},"CALL FIRE",{"kind":17,"text":66},": ",{"kind":42,"text":68},"LD HL,INVADER",{"kind":17,"text":70}," and ",{"kind":42,"text":72},"CALL DRAWINV",{"kind":17,"text":74},", which draw the invader every turn.",[76,77,78,80,82,84],{"kind":32,"text":38},{"kind":17,"text":40},{"kind":42,"text":79},"INVADER",{"kind":17,"text":81},", after ",{"kind":42,"text":83},"BLANK",{"kind":17,"text":85},": the invader's two cells, on two lines, the label on the first.",[87,88,89,91,92,94],{"kind":32,"text":38},{"kind":17,"text":40},{"kind":42,"text":90},"INVX",{"kind":17,"text":81},{"kind":42,"text":93},"SHOT",{"kind":17,"text":95},": the invader's column.",{"kind":14,"inlines":97},[98],{"kind":17,"text":99},"Nothing is taken out.",{"kind":20,"id":101,"text":102},"the-invader","The invader",{"kind":14,"inlines":104},[105,107,109,110,112],{"kind":17,"text":106},"The invader is two graphics cells, ",{"kind":42,"text":108},"9EH",{"kind":17,"text":70},{"kind":42,"text":111},"0ADH",{"kind":17,"text":113},":",{"kind":42,"lang":115,"text":116},"z80","INVADER:\n        DB      9EH,0ADH        ; an invader: two cells, a head, arms and legs",{"kind":14,"inlines":118},[119,121,122,124,125,127,129,131,133,135,137],{"kind":17,"text":120},"As with the base, the low six bits of a graphics byte light the cell's blocks, two across and three down: bit 0 top left, 1 top right, 2 and 3 the middle, 4 and 5 the bottom (as Fugløy's screen draws them, from the TRS-80's character generator). ",{"kind":42,"text":108},{"kind":17,"text":123}," lights bits 1 to 4, and ",{"kind":42,"text":111},{"kind":17,"text":126}," bits 0, 2, 3 and 5. Side by side, the top row lights the two middle blocks, a head; the middle row all four, arms; and the bottom row the two outer ones, legs. The label ",{"kind":42,"text":128},"INVADER:",{"kind":17,"text":130}," is too long for the space before ",{"kind":42,"text":132},"DB",{"kind":17,"text":134},", so it stands on a line of its own, as ",{"kind":42,"text":136},"DRAWBASE:",{"kind":17,"text":138}," does above its code: either way a label names the address of the next byte the program assembles.",{"kind":20,"id":140,"text":141},"its-place-and-drawinv","Its place, and DRAWINV",{"kind":14,"inlines":143},[144],{"kind":17,"text":145},"The invader's column is a byte, as the base's is:",{"kind":42,"lang":115,"text":147},"INVX:   DB      30              ; the invader's column, 0 to 62",{"kind":14,"inlines":149},[150,151,153,155,157,159,161,162,164,165,167,169,171,173,175,177],{"kind":42,"text":43},{"kind":17,"text":152}," is ",{"kind":42,"text":154},"DRAWBASE",{"kind":17,"text":156}," for a thing two cells wide on row 2. Row 2 starts at ",{"kind":42,"text":158},"3C80H",{"kind":17,"text":160},", two rows of 64 in, so the invader's left cell is ",{"kind":42,"text":158},{"kind":17,"text":163}," plus ",{"kind":42,"text":90},{"kind":17,"text":166},"; the column is at most 62, and ",{"kind":42,"text":168},"80H",{"kind":17,"text":170}," plus 62 is ",{"kind":42,"text":172},"0BEH",{"kind":17,"text":174},", one byte, so the high byte is always ",{"kind":42,"text":176},"3CH",{"kind":17,"text":113},{"kind":42,"lang":115,"text":179},"DRAWINV:\n        PUSH    BC              ; save BC, which LDIR counts down\n        LD      A,(INVX)        ; the invader's column ...\n        ADD     A,80H           ; ... plus 3C80H, row 2: the invader's left cell\n        LD      E,A\n        LD      D,3CH\n        LD      BC,2            ; two bytes\n        LDIR                    ; from (HL) to (DE), one byte at a time\n        POP     BC              ; BC as the caller left it\n        RET",{"kind":14,"inlines":181},[182,184,185,187,188],{"kind":17,"text":183},"As with ",{"kind":42,"text":154},{"kind":17,"text":186},", HL says what to draw, so a later chapter can erase the invader with two of ",{"kind":42,"text":83},{"kind":17,"text":189},"'s spaces before moving it.",{"kind":20,"id":191,"text":192},"drawn-every-turn","Drawn every turn",{"kind":14,"inlines":194},[195],{"kind":17,"text":196},"The base is drawn when it moves, and the shot when it moves. The invader does not move, yet the loop draws it every turn:",{"kind":42,"lang":115,"text":198},"        LD      HL,INVADER      ; the invader, drawn every turn: a shot passing\n        CALL    DRAWINV         ; through it erases a cell, and this mends it",{"kind":14,"inlines":200},[201,203,205,207,208,210,211,213,214],{"kind":17,"text":202},"That is for the shot. With the base where it starts, a shot rises in column 31, the invader's right cell. On the turn it reaches row 2, ",{"kind":42,"text":204},"MOVESHOT",{"kind":17,"text":206}," draws the shot over the invader, and then ",{"kind":42,"text":43},{"kind":17,"text":209}," draws the invader over the shot; on the next turn ",{"kind":42,"text":204},{"kind":17,"text":212}," erases the shot where it was, a space in the invader's right cell, and draws it on row 1, and ",{"kind":42,"text":43},{"kind":17,"text":215}," mends the invader again. Between the two, a few hundred T-states, too short to see. So the shot seems to pass behind the invader. Making it hit is chapter 9's work.",{"kind":14,"inlines":217},[218,220,221,223,225,227,228,230,232],{"kind":17,"text":219},"Drawing it every turn costs 136 T-states a turn: ",{"kind":42,"text":68},{"kind":17,"text":222}," 10, ",{"kind":42,"text":224},"CALL",{"kind":17,"text":226}," 17, and ",{"kind":42,"text":43},{"kind":17,"text":229}," 109, of which the ",{"kind":42,"text":231},"LDIR",{"kind":17,"text":233}," takes 37, 21 for the first byte, which it repeats after, and 16 for the last. A turn with no key down is now 133,470 T-states, still about 66 ms at the Model III's 2.03 MHz.",{"kind":20,"id":235,"text":236},"keys-go-to-the-screen-not-the-editor","Keys go to the screen, not the editor",{"kind":14,"inlines":238},[239],{"kind":17,"text":240},"Click the TRS-80's screen before you press the arrows or the space bar, so that it has the keyboard.",{"kind":20,"id":242,"text":243},"on-the-model-i","On the Model I",{"kind":14,"inlines":245},[246],{"kind":17,"text":247},"The Model I has the same keyboard row and the same screen, so the program runs unchanged, a little slower at the Model I's 1.77 MHz.",[249,265],{"kind":14,"inlines":250},[251,253,254,255,256,258,260,261,263],{"kind":17,"text":252},"Draw the invader once instead of every turn: move the two lines ",{"kind":42,"text":68},{"kind":17,"text":70},{"kind":42,"text":72},{"kind":17,"text":257}," from the loop to just above ",{"kind":42,"text":259},"LOOP:",{"kind":17,"text":81},{"kind":42,"text":262},"CALL DRAWBASE",{"kind":17,"text":264},", and press Run ▶. The invader is there as before. Click the screen and fire without moving the base: the shot passes the invader's right cell, and takes it away, a space where its right half was.",{"kind":14,"inlines":266},[267,269,270,272,274,276,278],{"kind":17,"text":268},"Then put the lines back in the loop, and change ",{"kind":42,"text":90},{"kind":17,"text":271},"'s ",{"kind":42,"text":273},"DB 30",{"kind":17,"text":275}," to ",{"kind":42,"text":277},"DB 10",{"kind":17,"text":279},". The invader stands over column 10; move the base under it with the left arrow and fire.",null,[282,286,290,294],{"source":283,"page":284,"about":285},"m3-trm","14","the Model III's keyboard is memory at 3800H-3BFFH, and its screen at 3C00H",{"source":287,"page":288,"about":289},"m1-trh","12","the Model I's keyboard is memory from 3800H, and its screen at 3C00H",{"source":291,"page":292,"about":293},"decker","64","the keyboard's rows are read at 3801H to 3840H, one address a row",{"source":295,"page":296,"about":293},"paay","26","; Build Space Invaders, chapter 5: one invader. Chapter 4's base and shot,\n; and above them on row 2, at the column INVX holds, an invader: two cells,\n; drawn by DRAWINV as DRAWBASE draws the base. The loop draws it every turn,\n; so that a shot passing through it leaves no hole: the shot goes behind it\n; for now, and hitting it comes later.\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; DRAWINV: copy the two bytes HL points at to the invader's place on row 2,\n; 3C80H plus INVX. BC is kept on the stack, as DRAWBASE keeps it.\nDRAWINV:\n        PUSH    BC              ; save BC, which LDIR counts down\n        LD      A,(INVX)        ; the invader's column ...\n        ADD     A,80H           ; ... plus 3C80H, row 2: the invader's left cell\n        LD      E,A\n        LD      D,3CH\n        LD      BC,2            ; two bytes\n        LDIR                    ; from (HL) to (DE), one byte at a time\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 invader.\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      HL,INVADER      ; the invader, drawn every turn: a shot passing\n        CALL    DRAWINV         ; through it 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\nBASEX:  DB      30              ; the base's column, 0 to 61\nSHOT:   DW      0               ; the shot's screen address, or 0 for none\nINVX:   DB      30              ; the invader's column, 0 to 62\n\n        END     START           ; the program starts at START\n","; Build Space Invaders, chapter 4: the base fires. Chapter 3's base, and the\n; space bar, bit 7 of the keyboard row at 3840H, fires a shot that rises a\n; row each turn and is gone past row 0. One shot at a time: SHOT holds its\n; screen address, 0 when there is none, and FIRE does nothing while it is\n; in the air. MOVESHOT moves it each turn, as MOVEBASE moves the base.\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\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, then the shot.\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        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\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",1792464471268]