; Fast life program written in Z-80 code by Jim Petersen in 3/85. ; .Z80 ASEG ; ; Constant storage ; BDOS EQU 0005H ; Function calls to BDOS go here DIRCON EQU 6 ; Direct console input/output function # PRSTR EQU 9 ; String output to console, ending with '$' ; ESC EQU 1BH CR EQU 0DH LF EQU 0AH ; UPKEY EQU 05H ; move cursor up (^K) DNKEY EQU 18H ; move cursor down (^J) LFKEY EQU 13H ; move cursor left (^H) RTKEY EQU 04H ; move cursor right (^L) ; CTRLC EQU 03H ; user can abort program with CTRL-C CTRLQ EQU 11H ; user can make program run with CTRL-Q CTRLS EQU 13H ; user can suspend program with CTRL-S CTRLZ EQU 1AH ; user can start program again with CTRL-Z ; EMPTY EQU ' ' ; character to use to imply an empty cell FILL EQU 'o' ; character to use to imply a living cell TOGGLE EQU ' ' ; key to press to toggle life on/off in a cell ; ROWS EQU 24 ; number of rows in board COLS EQU 78 ; number of cols. in board ; ORG 100H ; ; Initialization section ; LD (OLDSTK),SP ; save entry stack pointer LD SP,STACK ; and set a new one for us ; LD DE,BOARD ; give instructions LD C,PRSTR CALL BDOS ; USRRDY: LD C,DIRCON LD E,0FFH CALL BDOS OR A JR Z,USRRDY ; START: LD DE,CLEAR ; clear the screen LD C,PRSTR CALL BDOS ; LD HL,BOARD ; zero out board LD BC,256*(ROWS+1) ZERO: LD (HL),0 INC HL DEC BC LD A,B OR C JR NZ,ZERO ; CENCUR: LD A,ROWS/2 ; start user off at ROWS/2 LD (SCRNX),A LD A,COLS/2 ; and COLS/2 LD (SCRNY),A ; ; End of initialization. Turn cursor on, position cursor at appropriate ; place, update screen cell with appropriate data, and wait for a key press. ; LD DE,ONCRS ; output cursor-on command LD C,PRSTR CALL BDOS DISPLY: LD A,(SCRNX) LD D,A ; current row in D LD A,(SCRNY) LD B,A ; current column in B CALL DISPXY ; position cursor, display data LD C,DIRCON LD E,08H ; back cursor up over cell CALL BDOS WAITKY: LD C,DIRCON ; wait for user to press a key LD E,0FFH CALL BDOS OR A JR Z,WAITKY ; ; They input something. Test for program action keys. ; CP CTRLQ ; start the action? JP NZ,QTEST ; go to quit test if not LD DE,OFFCRS ; output off-cursor command LD C,PRSTR CALL BDOS JP MAKBOX ; start with calculations QTEST: CP CTRLC ; quit the game? JP Z,EXIT CP CTRLZ ; start game over? JP Z,START ; ; Not yet; test for cursor positioning keys. ; CP UPKEY ; move cursor up? JR NZ,STAY1 LD A,(SCRNX) ; valid movement? DEC A JP Z,WAITKY ; ignore if not LD (SCRNX),A ; otherwise, store change JP DISPLY ; and move cursor ; STAY1: CP DNKEY ; move cursor down? JR NZ,STAY2 LD A,(SCRNX) ; valid movement? CP ROWS JP Z,WAITKY ; ignore if not INC A LD (SCRNX),A ; otherwise, store change JP DISPLY ; and move cursor ; STAY2: CP LFKEY ; move cursor left? JR NZ,STAY3 LD A,(SCRNY) ; valid movement? DEC A JP Z,WAITKY ; ignore if not LD (SCRNY),A ; otherwise, store change JP DISPLY ; and move cursor ; STAY3: CP RTKEY ; move cursor right? JR NZ,LIFCMD ; check for life changing command if not LD A,(SCRNY) ; valid movement? CP COLS JP Z,WAITKY ; ignore if not INC A LD (SCRNY),A ; otherwise, store change JP DISPLY ; and move cursor ; ; Last possibility for their key is command to add or remove a cell ; from the board. ; LIFCMD: CP TOGGLE ; is it the toggle key for life? JP NZ,WAITKY ; NO MORE VALID KEYS; IGNORE AND WAIT IF NOT LD A,(SCRNX) LD H,A ; current row # in H LD A,(SCRNY) LD L,A ; current column # in L LD DE,BOARD ADD HL,DE LD A,(HL) ; get cell from board AND A ; is there life here now? LD (HL),01B ; assume there is no life, and add life JP Z,DISPLY ; if OK, go back and show it DEC (HL) ; but if life, take it away JP DISPLY ; ; Beginning of section to determine next generation ; ; We do a nested loop using the borders as limits. We check each ; cell by counting the number of neighbors in the eight surrounding ; positions, and acting accordingly: ; ; If life exists in the current cell: ; ; 0,1 neighbors ---> life dies from isolation ; 2,3 neighbors ---> life lives another generation ; 4-8 neighbors ---> life dies from over-population ; ; If life does not exist in the current cell, and there are 3 neighbors, ; a new cell will be created here in the next generation. ; MAKBOX: LD DE,100H+ROWS ; set D to row 1, E to max. row # LD BC,100H+COLS ; set B to col. 1, C to max. col. # LOOP: PUSH DE ; save current row #, row limit PUSH BC ; save current col. #, col. limit ; ; D contains current row, B contains current column. Check the eight ; neighboring cells, or less if we would fall off the board. ; LD E,D ; save copy of cur. row in E DEC D ; one row above if not off board JP NZ,AREA1 INC D ; top row established ; AREA1: LD A,E ; one row below if not off board CP ROWS JP Z,AREA2 INC E ; bottom row established ; AREA2: LD C,B ; save copy of cur. col in C DEC B ; one col. left if not off board JP NZ,AREA3 INC B ; left col. established ; AREA3: LD A,C ; one col. right if not off board CP COLS JP Z,AREA4 INC C ; right col. established ; AREA4: LD L,0 ; number of lives in neighboring cells AREALP: PUSH BC ; save neighboring cols. limits PUSH DE ; save neighboring rows limits ARLP1: LD E,B ; current row, col. now in DE PUSH HL ; save life count LD HL,BOARD ADD HL,DE LD A,(HL) ; get cell data from table POP HL ; restore life count AND 1 ; keep only bit 0 (life or no life) ADD A,L ; inc. neighbor count if live neighbor LD L,A ; LD A,B ; inner loop: from col. B to col. C CP C JR Z,AREA5 ; go to next row if done with this one INC B ; or next col. if not done with row JP ARLP1 ; AREA5: POP DE ; restore neighboring row limits POP BC ; we'll start at left-most col. again LD A,D ; outer loop: from row D to row E CP E JR Z,EVAL ; evaluate # of neighbors if done INC D ; if not done, do next row... JP AREALP ; EVAL: POP BC ; restore current col. & col. limit POP DE ; restore current row & row limit PUSH DE ; save again for bottom of loop PUSH HL ; save life count LD E,B ; current row, col now in DE LD HL,BOARD ADD HL,DE LD A,(HL) ; get cell from board POP DE ; restore life count AND 1 ; do proper evaluation LD A,E ; # of neighbors to A JR NZ,YESLIF ; CP 3 ; no life here, but 3 neighbors? JR NZ,NXTCEL ; no changes needed if not 3 LD A,10B LD (HL),A ; update for new life in next gen. JP NXTCEL ; on to the next cell ; ; For the evaluation which takes place when life is present at the current ; location, remember that the current cell was also counted along with the ; neigboring cells, so compare numbers have 1 added to them. ; YESLIF: CP 3 ; 0,1 neighbors means life will die JR C,DIE CP 5 ; will live more if 2-3 neighbors JR C,NXTCEL ; otherwise it will die (4-8 nbrs.) ; DIE: LD A,11B LD (HL),A ; life will die in next gen. ; NXTCEL: POP DE ; restore current row, row limit LD A,B ; test current column, CP C ; loop until col. limit reached JR Z,BASE ; go to next row if done with this one INC B ; or next col. if not done with row JP LOOP ; BASE: LD A,D ; outer loop: to bottom row of board CP E JR Z,ADJUST ; if done, go adjust screen display INC D ; if not done, do next row... LD B,1 ; starting at first col. JP LOOP ; ; Update screen for next generation. ; ; We do a nested loop to cover the board. For cells that are empty now ; but will have life next gen. (data=10B), the data is changed to 01B. For ; cells that have life now but will die next gen. (data=11B), the data is ; changed to 00B. In both cases, the screen is updated to show the change. ; ADJUST: LD DE,100H+ROWS ; start at row 1, go to max. row LD BC,100H+COLS ; go from col. 1 to max. col. SHOWLP: PUSH DE ; save current row #, max. row # SHWLP1: LD E,B ; current row now in D, col. in E LD HL,BOARD ADD HL,DE LD A,(HL) ; get cell data from board CP 10B ; if 00 or 01, no changes needed JR C,NOSHOW LD A,01B ; assume new life in next gen. JR Z,NEWNXT ; if so, don't change new life byte LD A,00B ; but change if life will die (11B) NEWNXT: LD (HL),A ; update to reflect change CALL DISPXY ; update display, too ; NOSHOW: LD A,B ; continue with next col., same row CP C JR Z,BASE1 INC B JP SHWLP1 ; BASE1: POP DE ; restore current row # and row limit LD A,D ; or next row, first col. if row done CP E JR Z,DONE ; leave loop if done INC D LD B,1 JP SHOWLP ; DONE: LD C,DIRCON ; check to see if a key is pressed LD E,0FFH CALL BDOS OR A JP Z,MAKBOX ; ON TO NEXT GEN. IF NO KEY CP CTRLS ; is their key CTRL-S? JP Z,CENCUR ; pause game, center cursor CP CTRLC ; is their key CTRL-C? JP NZ,MAKBOX ; exit game if so, next gen. if not ; ; Exit to CP/M routine. ; EXIT: LD DE,ONCRS ; turn cursor back on LD C,PRSTR CALL BDOS LD DE,CLEAR ; and clear the screen LD C,PRSTR CALL BDOS ; LD SP,(OLDSTK) ; restore old stack RET ; and return to CP/M ; ; Subroutine to position cursor at (X,Y) and display the character that ; should be there. (Currently set up for Kaypro 10.) ; ; Entry: D contains row number X ; B contains col. number Y ; ; Exit: DE & BC preserved ; DISPXY: PUSH DE ; save row & col. information PUSH BC LD E,B ; get row, col. in DE LD HL,BOARD ADD HL,DE LD A,(HL) ; get cell from board AND 1 ; is cell blank? LD A,FILL ; assume filled in first JR NZ,LIFEXY LD A,EMPTY ; but change if no life here LIFEXY: LD (CRSCHR),A ; save char. to output ; LD HL,1F1FH ; add offset so that row 1, col. 1 = 20,20 ADD HL,DE LD D,H ; swap H & L... LD H,L ; because cursor positioning format... LD L,D ; is ROW, COL LD (CRSLOC),HL ; LD HL,CRSATN ; position cursor and display char. DISPLP: LD C,DIRCON LD A,(HL) ; end of string? OR A JR Z,DISP1 PUSH HL ; if not, save current string pointer LD E,A ; move to E for BDOS CALL BDOS ; and display POP HL INC HL ; on to next char. in string JR DISPLP ; DISP1: POP BC ; restore row & column information POP DE RET ; ; Storage area for variables ; SCRNX: DS 1 ; current screen X pos. SCRNY: DS 1 ; current screen Y pos. ; CRSATN: DB ESC,'Y' ; ADM-3A cursor addressing attention CRSLOC: DB ' ' ; row & col. for cursor positioning CRSCHR: DB ' ' ; cell data to display goes here DB 0 ; end of string ; CLEAR: DB 1BH,'E' ; clear screen sequence ONCRS: DB ESC,'x5' ; command to turn cursor on OFFCRS: DB ESC,'y5' ; command to turn cursor off ; DS 50 ; stack space STACK EQU $ OLDSTK: DS 2 ; location of old stack saved here ; ; Instructions on how to play. This area is erased when the game begins, ; and is used as storage for the board cell pattern. ; BOARD: DB 1AH DB 'This is a Z80 version of the famous Game of Life, ' DB 'invented by John Conway.',13,10,13,10 DB 'I will start you off at the center of the screen. ' DB 'You use the arrow keys to',13,10 DB 'move the cursor. To place a cell at a certain ' DB 'spot, press the space bar.',13,10 DB 'Pressing the space bar on a location that already has ' DB 'a cell removes that cell.',13,10,13,10 DB 'Action keys are:',13,10,13,10 DB 'CTRL-Z will erase all cells and begin again',13,10 DB 'CTRL-Q will start the action',13,10 DB 'CTRL-S will stop the action so you can edit cells again',13,10 DB 'CTRL-C will return you to CP/M.',13,10,13,10 DB 'Remember, the rules of the Game of Life are:',13,10,13,10 DB 'Each location has 8 neighboring locations. If a cell ' DB 'exists in a neighboring',13,10 DB 'position, it is called a neighbor.',13,10,13,10 DB 'No cell here, but 3 neighbors ---> new cell next gen.',13,10 DB 'Cell here, 2-3 neighbors ---> lives another gen.',13,10 DB 'Cell here, 0-1 neighbors ---> dies from isolation',13,10 DB 'Cell here, 4-8 neighbors ---> dies from overpopulation',13,10 DB 13,10 DB 'Press any key to begin > $' ; END