Comment  $
        ********************************
        *                              *
        *       The Game of Life       *
        *                              *
        ********************************


        John Conway's mathematical game of life, implemented on
        the IBM/PC, by Simson L. Garfinkel.

        Written in 8088 assembly language using the Microsoft
        Macro Assembler.

        Notes on running the program:

          When program is run:

                1.  Screen clears.
                2.  User enters first generation from keyboard.
                    Arrow keys move the cursor. INS key deposits
                    a live cell, DEL removes a live cell, (in case
                    the user makes a mistake.)
                3.  Pressing ESC starts program.
                4.  For each generation, cells which will have life
                    on the next turn are inverted.
                5.  Screen is updated to next generation.
                6.  Keyboard is interrogated for command.
                7.  If ESC is pressed, program terminates.
                8.  If a number 0-9 is pressed, speed is selected.
                    At speed 0, approx. 2.7 generations/sec are performed.
                    At speed 9, each generation takes 3.5 sec.
                9.  Program loops to #4.

        $

        ;global definitions

live    equ  02         ;character for a live cell
dead    equ  00         ;character for a dead cell

rev     equ  70h        ;reverse video (marks cell to live)
dark    equ   2         ;normal video  (marks cell to die)

time    equ 300         ;time delay base

TERM    equ 27          ;Character to exit mode

cseg    segment para public 'code1'
start   proc far
        assume cs:cseg,ds:nothing,ss:stack,es:nothing
                        ;set up return location
        push  ds
        sub   ax,ax
        push  ax        ;now I can go home when I'm finished


        call  Enter     ;Enter board
        mov   cx,0      ;initial delay, 0
main:
        push  cx        ;save delay variable
        cmp   cx,0
        jz    s13

s1:     push cx
          mov   cx,time
s11:      push  cx
            mov   cx,time
s12:        loop  s12
          pop  cx
          loop s11
        pop  cx
        loop s1         ;what a time delay!

s13:    call count      ;Count up every cell's neighbours,
        call update     ;Update screen
        pop  cx         ;get back the time delay

        mov  ah,1       ;See if user has pushed a key
        int  16h
        jz   main       ;nope - loop back

        mov  ah,0       ;get the character out of the buffer
        int  16h

        cmp  al,TERM
        jnz  s2
        ret             ;finished - go back to ms/dos

s2:     cmp  al,'0'     ;see if it is a speed command
        jb   main
        cmp  al,'9'
        jnbe main
                        ;It's a number
        sub  al,'0'     ;now it goes from 0 to 9
        mov  ah,0
        mov  cx,ax      ;put it in cx
        jmp  main
start   endp


Enter   proc  near      ;Subroutine to enter board
                        ;define scan codes:
left    equ   75
right   equ   77
up      equ   72
down    equ   80
point   equ   82
del     equ   83
esc     equ    1

        call  cls       ;clear the screen

        ;Registers are used as follows:
        ;DH - Y position
        ;DL - X position

        mov  dh,12
        mov  dl,40

e1:     mov  bh,0       ;move the cursor to x,y position
        mov  ah,2       ;code for cursor move
        int  10h        ;interrupt for cursor move

        mov  ah,0       ;set up to read the next keypress
        int  16h        ;keypress read

        cmp  ah,left    ;make a rational decision about the user's
        jz   go_left    ;entry
        cmp  ah,right
        jz   go_right
        cmp  ah,up
        jz   go_up
        cmp  ah,down
        jz   go_down
        cmp  ah,point
        jz   go_point
        cmp  ah,del
        jz   go_del

        cmp  ah,esc
        jnz  e1         ;loop back - unknown command
        mov  dx,23*256  ;put the cursor at lower left hand corner
        mov  ah,2
        int  10h
        ret             ;go back to caller

go_left:                ;move left if I can
        cmp  dl,0       ;in leftmost column?
        jz   e1         ;yes - go back
        sub  dl,1       ;no  - subtract one
        jmp  e1         ;go back

go_right:               ;move right if I can
        cmp  dl,79
        jz   e1
        add  dl,1
        jmp  e1

go_up:                  ;go up if I can
        cmp  dh,0
        jz   e1
        sub  dh,1
        jmp  e1

go_down:                ;go down if I can
        cmp     dh,24
        jz      e1
        add     dh,1
        jmp     e1

go_point:               ;put a live dot where the cursor is -- don't move it
        mov     al,live ;it's the live character
gp2:    mov     cx,1    ;one character to write
        mov     ah,10   ;code to write character
        int     10h     ;do it
        jmp     e1      ;get next command

go_del:                 ;delete character at cursor
        mov     al,dead
        jmp     gp2     ;let go_point do the rest
Enter   endp


Cls     proc    near    ;Subroutine to clear the screen
        mov     ax,6*256
        mov     cx,0
        mov     dx,24*256+79
        mov     bh,2
        int     10h
        ret
cls     endp

Count   proc    near    ;Subroutine to count every cells neighbours
        ;Registers used:
        ;DH,DL: Y,X of current cell being interrogated
        ;DS   : Base offset - into screen memory
        ;DI   : offset for character presently being looked at
        ;
        ;Outline for each character
        ;  1.   Count up number of neighbours
        ;  2.   If three neighbours, or if two and cell is live, put
        ;       a rev on the screen at the attribute position, else
        ;       put a dark
        ;  3.   Go to next character

chk     macro   yy,xx
        local   ch1,offs
offs    equ     (xx+yy*80)*2
        mov     cx,[di+offs]    ;get byte to check
        cmp     cl,live         ;check to see if this cell is alive
        jnz     ch1             ;nope
        add     al,1            ;yes - increase neighbour count
ch1:
        endm
        mov     ax,0B000H
        mov     ds,ax           ;offset value for monochrome display

        mov     dh,1            ;Start at 1,1 and fo to 23,78
        mov     dl,1            ;to prevent wrap-around

c1:     mov     ax,160          ;get true offset from ds into screen memory
        mul     dh

        mov     cx,dx
        mov     ch,00           ;just get dl
        add     ax,cx
        add     ax,cx           ;ax:=(dh*80+dl)*2

        mov     di,ax           ;di:=ax
        mov     ax,0            ;ax will be used for neighbour counting

        chk     -1,-1           ;count number of neighbours
        chk     -1, 0
        chk     -1,+1
        chk      0,-1
        chk      0,+1
        chk     +1,-1
        chk     +1, 0
        chk     +1,+1           ;test all of the neighbours

        mov     cx,[di]         ;get byte to check
        cmp     al,3
        jz      give_life       ;life if it has three neighbours
        cmp     cl,live         ;is it alive?
        jnz     give_death      ;no
        cmp     al,2            ;he lives if he has 2 neighbours and he is already
                                ;alive
        jnz     give_death      ;nope

give_life:                      ;make this one alive
        mov     ch,rev
        jmp     c2

give_death:
        mov     ch,dark
c2:     mov     [di],cx         ;put back on screen

next_cell:
        cmp     dl,78           ;am I at the end of the X line?
        jz      c3              ;yes
        add     dl,1            ;nope
        jmp     c1
c3:     mov     dl,1
        cmp     dh,23           ;am I at the end of the Y line?
        jz      c4              ;yes
        add     dh,1            ;nope
        jmp     c1
c4:     ret                     ;yes - go home!
Count   Endp

Update  proc    near            ;This updates the generation on the screen
        mov     ax,0B000H       ;Get screen offset
        mov     ds,ax

        mov     bx,24*80*2-2    ;loop through all of the screen but last line
u1:     mov     cx,[bx]         ;line
        cmp     ch,rev          ;is it to live?
        jnz     u2              ;no
        mov     cl,live         ;yes
        jmp     u3
u2:     mov     cl,dead         ;no
u3:     mov     ch,dark         ;turn off reverse
        mov     [bx],cx         ;put it back on the screen
        sub     bx,2            ;loop back until done with the screen
        jg      u1
        ret                     ;go back to caller
Update  endp
cseg    ends

stack   segment para stack 'stack'
        db  30  dup('stack  ')
stack   ends

        end












