home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.barnyard.co.uk
/
2015.02.ftp.barnyard.co.uk.tar
/
ftp.barnyard.co.uk
/
cpm
/
walnut-creek-CDROM
/
CPM
/
GENASM
/
FLIFEH89.LBR
/
FASTLIFE.MZC
/
FASTLIFE.MAC
Wrap
Text File
|
2000-06-30
|
18KB
|
442 lines
; 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