[{"data":1,"prerenderedAt":605},["ShallowReactive",2],{"learn:invaders:10-rows-score":3},{"item":4,"marksAgainst":604},{"slug":5,"number":6,"title":7,"idea":8,"models":9,"body":12,"tryIt":483,"game":534,"cites":587,"program":603},"10-rows-score",10,"Three rows, and a score","three rows in one table, EQU, and a score kept in BCD with DAA",[10,11],3,1,[13,24,28,32,161,165,168,198,201,221,243,247,250,261,263,308,310,355,358,386,388,444,452,455,469,472,476,479],{"kind":14,"inlines":15},"p",[16,19,22],{"kind":17,"text":18},"text","Now there are three rows of eight, on rows 2, 4 and 6, and every invader shot down is worth 10 points. The score is printed in decimal at the top left, ",{"kind":20,"text":21},"code","SCORE 0000",{"kind":17,"text":23},". The rows cost almost nothing new: they are 16 more places in the table, and every loop over the table counts 24 instead of 8. The score is new: a number kept as decimal digits, so that printing it is easy.",{"kind":25,"id":26,"text":27},"h2","what-changed","What changed",{"kind":14,"inlines":29},[30],{"kind":17,"text":31},"Chapter 9's program, with these changes:",{"kind":33,"items":34},"ul",[35,41,58,85,95,113,121,133,142,149],[36,39],{"kind":37,"text":38},"strong","~",{"kind":17,"text":40}," The comment at the top, lines 1 to 5, now describes chapter 10.",[42,44,46,48,50,52,54,56],{"kind":37,"text":43},"+",{"kind":17,"text":45}," In ",{"kind":20,"text":47},"HIT",{"kind":17,"text":49},", line 91: ",{"kind":20,"text":51},"CALL ADDSCORE",{"kind":17,"text":53},", after ",{"kind":20,"text":55},"CALL KILL",{"kind":17,"text":57},".",[59,60,62,64,66,68,70,72,74,76,78,80,82,84],{"kind":37,"text":38},{"kind":17,"text":61}," ",{"kind":20,"text":63},"LD B,8",{"kind":17,"text":65}," becomes ",{"kind":20,"text":67},"LD B,NINV",{"kind":17,"text":69}," in all four loops over the table: line 119 in ",{"kind":20,"text":71},"DRAWROW",{"kind":17,"text":73},", 171 in ",{"kind":20,"text":75},"EDGE",{"kind":17,"text":77},", 192 in ",{"kind":20,"text":79},"MOVEROW",{"kind":17,"text":81}," and 211 in ",{"kind":20,"text":83},"KILL",{"kind":17,"text":57},[86,87,88,90,92,93],{"kind":37,"text":38},{"kind":17,"text":61},{"kind":20,"text":89},"MARCH",{"kind":17,"text":91},"'s comment, line 142, and two lines in ",{"kind":20,"text":89},{"kind":17,"text":94},", 157 and 158: the invaders come down until the top row is on row 9, so the lowest is on row 13.",[96,97,98,100,102,104,106,108,110,111],{"kind":37,"text":43},{"kind":17,"text":61},{"kind":20,"text":99},"ADDSCORE",{"kind":17,"text":101},", ",{"kind":20,"text":103},"SHOWSCORE",{"kind":17,"text":105}," and ",{"kind":20,"text":107},"TWODIG",{"kind":17,"text":109},", new routines with their comments, after ",{"kind":20,"text":83},{"kind":17,"text":112},", lines 228 to 271: they add 10 to the score and print it.",[114,115,117,119],{"kind":37,"text":43},{"kind":17,"text":116}," In the game loop, line 292: ",{"kind":20,"text":118},"CALL SHOWSCORE",{"kind":17,"text":120},", after the rows are drawn.",[122,123,125,127,129,131],{"kind":37,"text":38},{"kind":17,"text":124}," The comment above ",{"kind":20,"text":126},"INVS",{"kind":17,"text":128},": line 300 is changed and line 301 is new; they say which rows the places are on, and what ",{"kind":20,"text":130},"NINV",{"kind":17,"text":132}," is.",[134,135,136,137,139,140],{"kind":37,"text":43},{"kind":17,"text":61},{"kind":20,"text":130},{"kind":17,"text":138},", line 302, and four lines of ",{"kind":20,"text":126},{"kind":17,"text":141},", 305 to 308: the places of rows 4 and 6.",[143,144,145,147],{"kind":37,"text":38},{"kind":17,"text":61},{"kind":20,"text":146},"INVROW",{"kind":17,"text":148},"'s comment, line 313: it is now the top row's row, 2 to 9.",[150,151,152,154,155,157,158,159],{"kind":37,"text":43},{"kind":17,"text":61},{"kind":20,"text":153},"SCORETXT",{"kind":17,"text":105},{"kind":20,"text":156},"SCORE",{"kind":17,"text":53},{"kind":20,"text":146},{"kind":17,"text":160},", lines 314 to 316: the score's word, and the score.",{"kind":14,"inlines":162},[163],{"kind":17,"text":164},"Nothing is taken out.",{"kind":25,"id":166,"text":167},"three-rows-are-a-longer-table","Three rows are a longer table",{"kind":14,"inlines":169},[170,171,173,175,177,179,181,183,185,187,189,190,191,192,193,194,195,196],{"kind":20,"text":126},{"kind":17,"text":172}," held row 2's eight places; now four more lines hold row 4's, which starts at ",{"kind":20,"text":174},"3D00H",{"kind":17,"text":176},", and row 6's, at ",{"kind":20,"text":178},"3D80H",{"kind":17,"text":180},". A row is 64 cells, ",{"kind":20,"text":182},"40H",{"kind":17,"text":184},", so two rows down is ",{"kind":20,"text":186},"80H",{"kind":17,"text":188}," more. ",{"kind":20,"text":71},{"kind":17,"text":101},{"kind":20,"text":75},{"kind":17,"text":101},{"kind":20,"text":79},{"kind":17,"text":105},{"kind":20,"text":83},{"kind":17,"text":197}," never cared which row a place was on: they walk the table and do the same thing to every place. So they need only count to the table's new length, and the count gets a name:",{"kind":20,"lang":199,"text":200},"z80","NINV:   EQU     24",{"kind":14,"inlines":202},[203,205,207,209,211,212,214,216,218,219],{"kind":20,"text":204},"EQU",{"kind":17,"text":206}," names a number for the assembler, like ",{"kind":20,"text":208},"const NINV = 24",{"kind":17,"text":210}," in JavaScript. It puts nothing in memory: ",{"kind":20,"text":67},{"kind":17,"text":213}," assembles to the same two bytes as ",{"kind":20,"text":215},"LD B,24",{"kind":17,"text":217},". Change the table's length and ",{"kind":20,"text":130},{"kind":17,"text":220}," with it, and the four loops follow.",{"kind":14,"inlines":222},[223,225,226,228,229,230,231,232,233,235,238,240,241],{"kind":17,"text":224},"The three rows march as one, since ",{"kind":20,"text":79},{"kind":17,"text":227}," moves every place by the same step, and the row turns when any invader, in any row, reaches an edge. ",{"kind":20,"text":71},{"kind":17,"text":101},{"kind":20,"text":89},{"kind":17,"text":105},{"kind":20,"text":79},{"kind":17,"text":234}," keep their names and comments from when there was one row; ",{"kind":236,"text":237},"em","the row",{"kind":17,"text":239}," in them now means all three. ",{"kind":20,"text":146},{"kind":17,"text":242}," is now the top row's row, and the invaders stop coming down when it reaches row 9, which puts the lowest row on row 13 as before. That is so even when the lowest row has all been shot down: the two rows left stop on rows 9 and 11.",{"kind":14,"inlines":244},[245],{"kind":17,"text":246},"A shot rising at a column meets the lowest invader there first, so the invaders on row 6 protect the ones above them. As in chapter 9, a shot that has risen into a gap can still have an invader step or come down onto it, and go behind; with three rows, it has three rows of gaps to rise through.",{"kind":25,"id":248,"text":249},"a-score-in-decimal","A score in decimal",{"kind":14,"inlines":251},[252,254,256,258,259],{"kind":17,"text":253},"The score has to be printed as decimal digits, and the Z80 has no instruction that divides by 10, so turning a binary number into digits is a loop of subtractions, as lesson 4's remainder by 3 was. The other way is to keep the score in decimal from the start. That is BCD, binary-coded decimal: four bits a digit, so a byte holds two digits, and the byte ",{"kind":20,"text":255},"47H",{"kind":17,"text":257}," means forty-seven. ",{"kind":20,"text":156},{"kind":17,"text":260}," is two bytes, four digits, the last two in the first byte:",{"kind":20,"lang":199,"text":262},"SCORE:  DB      0,0             ; in BCD: the last two digits, then the first two",{"kind":14,"inlines":264},[265,267,269,271,272,274,276,278,280,282,284,286,288,290,292,294,296,298,300,302,303,304,306],{"kind":17,"text":266},"Adding in BCD needs one more instruction. ",{"kind":20,"text":268},"ADD A,10H",{"kind":17,"text":270}," adds 1 to the tens digit, and from ",{"kind":20,"text":182},{"kind":17,"text":273}," that gives ",{"kind":20,"text":275},"50H",{"kind":17,"text":277},", right; but from ",{"kind":20,"text":279},"90H",{"kind":17,"text":281}," it gives ",{"kind":20,"text":283},"0A0H",{"kind":17,"text":285},", and A is no digit. ",{"kind":20,"text":287},"DAA",{"kind":17,"text":289},", decimal adjust A, puts it right. It looks at A and at two flags the ",{"kind":20,"text":291},"ADD",{"kind":17,"text":293}," left: H, the half carry, set when the add carried out of the low four bits, and C, the carry. If the low four bits are over 9, or H is set, it adds 6; if A is over ",{"kind":20,"text":295},"99H",{"kind":17,"text":297},", or C is set, it adds ",{"kind":20,"text":299},"60H",{"kind":17,"text":301}," and sets C. So ",{"kind":20,"text":283},{"kind":17,"text":65},{"kind":20,"text":305},"00H",{"kind":17,"text":307}," with C set: 90 and 10 are 100, which is 00 and a carry into the next two digits.",{"kind":20,"lang":199,"text":309},"        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",{"kind":14,"inlines":311},[312,314,316,318,320,322,324,326,328,330,331,333,334,336,337,339,340,341,343,345,347,348,350,352,353],{"kind":17,"text":313},"These ",{"kind":20,"text":315},"LD",{"kind":17,"text":317},"s set no flags, and nor does ",{"kind":20,"text":319},"INC HL",{"kind":17,"text":321},", since 16-bit ",{"kind":20,"text":323},"INC",{"kind":17,"text":325}," never does, so C is still the carry from the last two digits when ",{"kind":20,"text":327},"ADC A,0",{"kind":17,"text":329}," adds it to the first two. The second ",{"kind":20,"text":287},{"kind":17,"text":332}," puts those right too. After ",{"kind":20,"text":287},{"kind":17,"text":335}," the carry out of the high byte is dropped, so the score goes from 9990 back to 0000. ",{"kind":20,"text":287},{"kind":17,"text":338}," works after ",{"kind":20,"text":291},{"kind":17,"text":105},{"kind":20,"text":342},"ADC",{"kind":17,"text":344},"; after ",{"kind":20,"text":346},"SUB",{"kind":17,"text":105},{"kind":20,"text":349},"SBC",{"kind":17,"text":351}," it subtracts its 6 and ",{"kind":20,"text":299},{"kind":17,"text":354}," instead, which it knows from the flag N that those set.",{"kind":25,"id":356,"text":357},"printing-the-digits","Printing the digits",{"kind":14,"inlines":359},[360,362,364,365,367,369,371,373,375,377,378,379,381,383,384],{"kind":17,"text":361},"In BCD, a digit's character is the digit plus ",{"kind":20,"text":363},"'0'",{"kind":17,"text":101},{"kind":20,"text":366},"30H",{"kind":17,"text":368},", since the digit characters ",{"kind":20,"text":370},"0",{"kind":17,"text":372}," to ",{"kind":20,"text":374},"9",{"kind":17,"text":376}," are ",{"kind":20,"text":366},{"kind":17,"text":372},{"kind":20,"text":380},"39H",{"kind":17,"text":382},". ",{"kind":20,"text":107},{"kind":17,"text":385}," prints a byte's two digits:",{"kind":20,"lang":199,"text":387},"TWODIG: 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",{"kind":14,"inlines":389},[390,392,394,395,396,398,400,402,404,406,407,409,410,411,413,415,417,419,421,422,424,426,428,429,431,433,435,436,438,440,442,443],{"kind":20,"text":391},"RRCA",{"kind":17,"text":393}," rotates A one bit right, the low bit going round to the top, so four of them swap A's two halves: ",{"kind":20,"text":255},{"kind":17,"text":65},{"kind":20,"text":397},"74H",{"kind":17,"text":399},", and ",{"kind":20,"text":401},"AND 0FH",{"kind":17,"text":403}," keeps the 4. In JavaScript the two digits are ",{"kind":20,"text":405},"(byte >> 4) + 48",{"kind":17,"text":105},{"kind":20,"text":408},"(byte & 15) + 48",{"kind":17,"text":382},{"kind":20,"text":103},{"kind":17,"text":412}," copies ",{"kind":20,"text":414},"SCORE ",{"kind":17,"text":416}," to row 0 with ",{"kind":20,"text":418},"LDIR",{"kind":17,"text":420},", which leaves DE on column 6, then has ",{"kind":20,"text":107},{"kind":17,"text":423}," print the high byte, the first two digits, and the low byte after it. For the low byte it does not ",{"kind":20,"text":425},"CALL TWODIG",{"kind":17,"text":427},": it steps HL back and runs on into ",{"kind":20,"text":107},{"kind":17,"text":430},", whose ",{"kind":20,"text":432},"RET",{"kind":17,"text":434}," then returns from ",{"kind":20,"text":103},{"kind":17,"text":437},", as ",{"kind":20,"text":439},"HITRT",{"kind":17,"text":441}," runs on into ",{"kind":20,"text":47},{"kind":17,"text":57},{"kind":14,"inlines":445},[446,447,449,450],{"kind":20,"text":103},{"kind":17,"text":448}," runs every turn, in the loop after the rows are drawn. Only a hit changes the score, but a shot that rises through row 0 erases a cell as it goes, as it erased invaders' cells before chapter 9, and printing the score every turn mends it at once, as ",{"kind":20,"text":71},{"kind":17,"text":451}," mends the invaders.",{"kind":25,"id":453,"text":454},"the-cost","The cost",{"kind":14,"inlines":456},[457,459,460,462,463,465,467],{"kind":17,"text":458},"Drawing 16 more invaders every turn costs 132 T-states each in ",{"kind":20,"text":71},{"kind":17,"text":461},", 2,112 in all, and ",{"kind":20,"text":103},{"kind":17,"text":464}," with its ",{"kind":20,"text":466},"CALL",{"kind":17,"text":468}," costs 389, so a turn with no key down is now 137,014 T-states. The rows step every fourth turn, about every 275 ms on the Model III.",{"kind":25,"id":470,"text":471},"keys-go-to-the-screen-not-the-editor","Keys go to the screen, not the editor",{"kind":14,"inlines":473},[474],{"kind":17,"text":475},"Click the TRS-80's screen before you press the arrows or the space bar, so that it has the keyboard.",{"kind":25,"id":477,"text":478},"on-the-model-i","On the Model I",{"kind":14,"inlines":480},[481],{"kind":17,"text":482},"The Model I has the same keyboard row and the same screen, and the score is all capitals and digits, 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.",[484,494],{"kind":14,"inlines":485},[486,488,489,490,492],{"kind":17,"text":487},"Press Run ▶, click the screen, and shoot an invader: the score goes from ",{"kind":20,"text":21},{"kind":17,"text":372},{"kind":20,"text":491},"SCORE 0010",{"kind":17,"text":493},", and 10 more with each one after.",{"kind":14,"inlines":495},[496,498,499,500,502,504,506,508,510,512,513,515,516,518,519,521,523,525,526,528,529,531,532],{"kind":17,"text":497},"Then make each invader worth 50: change ",{"kind":20,"text":268},{"kind":17,"text":372},{"kind":20,"text":501},"ADD A,50H",{"kind":17,"text":503},". The first hit shows ",{"kind":20,"text":505},"0050",{"kind":17,"text":507},", and the second ",{"kind":20,"text":509},"0100",{"kind":17,"text":511},": ",{"kind":20,"text":287},{"kind":17,"text":514}," carried into the first two digits. Then take out the ",{"kind":20,"text":287},{"kind":17,"text":517}," under it: the first hit still shows ",{"kind":20,"text":505},{"kind":17,"text":520},", but the second shows ",{"kind":20,"text":522},"00:0",{"kind":17,"text":524},", because A is ",{"kind":20,"text":283},{"kind":17,"text":527},", its high digit is 10, and ",{"kind":20,"text":363},{"kind":17,"text":530}," plus 10 is the character after ",{"kind":20,"text":374},{"kind":17,"text":533},", a colon.",{"example":535,"addr":536,"name":537,"blocks":538},"sea-dragon",24972,"ADD_SCORE",[539],{"kind":14,"inlines":540},[541,543,544,546,547,549,550,552,554,555,556,558,560,561,562,564,566,567,568,570,572,574,576,578,579,581,582,584,585],{"kind":17,"text":542},"Sea Dragon's ",{"kind":20,"text":537},{"kind":17,"text":545}," adds points kept in BCD to ",{"kind":20,"text":156},{"kind":17,"text":548},", six digits in three bytes, as ",{"kind":20,"text":99},{"kind":17,"text":551}," adds 10: ",{"kind":20,"text":553},"ADD A,L",{"kind":17,"text":105},{"kind":20,"text":287},{"kind":17,"text":557}," for the last two digits, then ",{"kind":20,"text":559},"ADC A,H",{"kind":17,"text":105},{"kind":20,"text":287},{"kind":17,"text":563}," for the next two, and when that carries, ",{"kind":20,"text":565},"INC A",{"kind":17,"text":105},{"kind":20,"text":287},{"kind":17,"text":569}," for the first two. An Expert gets the points twice, by a ",{"kind":20,"text":571},"DJNZ",{"kind":17,"text":573}," loop round the adds. Then it prints the score on the bottom row a digit at a time, ",{"kind":20,"text":575},"ADD A,30H",{"kind":17,"text":577}," as ",{"kind":20,"text":107},{"kind":17,"text":580}," adds ",{"kind":20,"text":363},{"kind":17,"text":583},", but leaving out the zeros in front, which ",{"kind":20,"text":107},{"kind":17,"text":586}," prints.",[588,592,596,600],{"source":589,"page":590,"about":591},"m3-trm","14","the Model III's keyboard is memory at 3800H-3BFFH, and its screen at 3C00H",{"source":593,"page":594,"about":595},"m1-trh","12","the Model I's keyboard is memory from 3800H, and its screen at 3C00H",{"source":597,"page":598,"about":599},"decker","64","the keyboard's rows are read at 3801H to 3840H, one address a row",{"source":601,"page":602,"about":599},"paay","26","; 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","; 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",1792464471346]