; Play Conway's Game of Life on a monochrome ST.  This program was
; developed with the AssemPro assembler from Abacus Software.
;
; Designed and programmed by Eric Bergman-Terrell, 1987
;
; Press left mouse button to quit the program.
; Press right mouse button to get a new cell generation.

     text

     ilabel     c:\assemble\tos\tos.l

; Pixels in x-dimension
y_pixels  = 400

; Pixels in y-dimension
x_pixels  = 640

; Width of rectangle
pix_x_len =   4

; Height of rectangle
pix_y_len =   4

; Cells in x-dimension
x_cells   = x_pixels / pix_x_len

; Cells in y-dimension
y_cells   = y_pixels / pix_y_len

start:       gem_init
             ; Get a local stack for use by this program.
             lea       stack_top, sp
             bsr       init_gr
             ; Make the first generation of cells.
restart:     bsr       randomize
             bsr       init_old
             ; Plot the current generation to the screen.
loop_2:      bsr       plot
             bsr       new_to_old
             ; Generate the next generation.
             bsr       update
             ; If right mouse button is pressed, quit.
             ; If left mouse button is pressed, create a new
             ; random generation.
             vq_mouse  gr_handle, d0, d1, d2
             and.w     #3,        d0
             beq.s     loop_2
             and.w     #1,        d0
             beq.s     restart
quit:        gem_exit
             rts


; Copy new generation into old generation
new_to_old:  lea    oldgen, a0
             lea    newgen, a1
             lea    oldend, a2
assign_lp:   move.w (a1)+,  (a0)+
             cmp.l  a2,     a0
             ble.s  assign_lp
             rts

; Give the old generation an initial value totally different than
; the initial value of the new generation.
init_old:    lea     oldgen, a0
             lea     oldend, a1
init_lp:     move.b  #2,     (a0)+
             cmp.l   a1,     a0
             ble.s   init_lp
             rts


; Generate a new generation of cells.  For each nonempty cell, if it
; has two or three neighbors, it survives, otherwise it dies.  If an
; empty cell has three neighbors, it becomes nonempty.  A cell can
; have up to eight neighbors:
;
; -------
; |N|N|N|
; -------
; |N|*|N|   * denotes a cell
; -------   N denotes a cell's neighbors
; |N|N|N|
; -------
;

update:      lea    oldgen - 1, a0
             lea    newgen - 1, a1

             clr.w  d0 ; x-coordinate
             clr.w  d1 ; y-coordinate

loop_5:      addq.l #1,         a0
             addq.l #1,         a1

             clr.b  d3 ; Compute number of oldgen[x, y]'s
                       ; neighbors.

             ; Don't compute neighbors of a cell on a border.
             cmp.w  #0,         d0
             beq                update_cell

             cmp.w  #0,         d1
             beq                update_cell

             cmp.w  #(x_cells - 1), d0
             beq                update_cell

             cmp.w  #(y_cells - 1), d1
             beq                update_cell

             ; Count the neighbors of the current cell.
             move.l a0,         a4
             subq.l #1,         a4
             add.b  (a4),       d3
             move.l a0,         a4
             addq.l #1,         a4
             add.b  (a4),       d3

             move.l a0,             a4
             sub.l  #(x_cells + 1), a4

             add.b  (a4)+,          d3
             add.b  (a4)+,          d3
             add.b  (a4),           d3

             move.l a0,             a4
             add.l  #(x_cells - 1), a4

             add.b  (a4)+,          d3
             add.b  (a4)+,          d3
             add.b  (a4),           d3

update_cell: tst.b    (a0)
             beq.s    empty
             cmp.b    #2,       d3
             beq.s    do_set
             cmp.b    #3,       d3
             beq.s    do_set
             bra.s    do_clear
do_set:      move.b   #1,       (a1)
             bra.s    next_x2
do_clear:    clr.b    (a1)
             bra.s    next_x2
empty:       cmp.b    #3,       d3
             beq.s    do_set
             bra.s    do_clear
next_x2:     addq.w   #1,             d0
             cmp.w    #(x_cells - 1), d0
             bgt.s    next_y2
             bra.s    loop_5
next_y2:     clr.w    d0
             addq.w   #1,             d1
             cmp.w    #(y_cells - 1), d1
             bgt.s    quit2
             bra      loop_5
quit2:       rts


; Prepare to plot to screen.
init_gr:     vs_clip       gr_handle, #1, px
             vswr_mode     gr_handle, #1, d0
             vsf_color     gr_handle, #1, d0
             vsl_color     gr_handle, #1, d0
             v_hide_c      gr_handle
             ; Make sure that rectangle perimeters are not drawn.
             vsf_perimeter gr_handle, #0, d0
             bsr           clear
             rts

; Clear the screen.
clear:       vsf_interior gr_handle,       #8,         d1
             lea          pxy_array,       a3
             clr.w        (a3)+
             clr.w        (a3)+
             move.w       #(x_pixels - 1), (a3)+
             move.w       #(y_pixels - 1), (a3)
             v_bar        gr_handle,       pxy_array
             rts

; Generate a random cell pattern.
randomize:   lea     newgen,         a3
             lea     newend,         a4
loop:        random
             move.w  d0,             d3
             and.w   #7,             d3
             asr.b   d3,             d0
             and.l   #1,             d0
             move.b  d0,             (a3)+
             cmp.l   a4,             a3
             bgt.s   trim
             bra.s   loop
             ; Remove cells from the border of the rectangle.
trim:        lea     newgen,         a3
             subq.l  #1,             a3
             clr.w   d0                  ; x-coordinate
             clr.w   d1                  ; y-coordinate
             ; Clear all cells on borders.
trim_loop:   addq.l  #1,             a3
             tst.w   d0
             beq     clear_cell
             cmp.w   #(x_cells - 1), d0
             beq     clear_cell
             cmp.w   #0,             d1
             beq     clear_cell
             cmp.w   #(y_cells - 1), d1
             beq     clear_cell
next_x_2:    addq.w  #1,             d0
             cmp.w   #(x_cells - 1), d0
             bgt.s   next_y_2
             bra.s   trim_loop
next_y_2:    clr.w   d0
             addq.w  #1,             d1
             cmp.w   #(y_cells - 1), d1
             bgt.s   return_3
             bra     trim_loop
return_3:    rts
             ; Remove one cell from a border of the rectangle.
clear_cell:  clr.b   (a3)
             bra.s   next_x_2


; Plot a cell pattern to the screen.
plot:        lea          newgen,           a0
             lea          oldgen,           a2
             clr.w        d0       ; x-coordinate
             clr.w        d1       ; y-coordinate
draw_loop:   clr.w        d3
             move.b       (a0)+,            d3
             move.b       (a2)+,            d4
             cmp.b        d3,               d4
             beq          next_x

             ; Draw the rectangle.
             lea          pxy_array,        a1
             move.w       d0,               (a1)+
             move.w       d1,               (a1)+
             move.w       d0,               d5
             move.w       d1,               d6
             addq.w       #(pix_x_len - 1), d5
             addq.w       #(pix_y_len - 1), d6
             move.w       d5,               (a1)+
             move.w       d6,               (a1)

             ; Save registers.
             movem.l      d0-d3/a0-a2,      -(sp)

             vsf_interior gr_handle,        d3,        d2
             v_bar        gr_handle,        pxy_array

             ; Restore registers.
             movem.l      (sp)+,            d0-d3/a0-a2

next_x:      addq.w       #pix_x_len,       d0
             cmp.w        #x_pixels,        d0
             bge.s        next_y
             bra          draw_loop
next_y:      clr.w        d0
             addq.w       #pix_y_len,       d1
             cmp.w        #y_pixels,        d1
             bge.s        return_2
             bra          draw_loop
return_2:    rts

     data

     align
newgen:    ds.b (x_cells * y_cells)
     align
newend:    ds.b 0
     align
oldgen:    ds.b (x_cells * y_cells)
     align
oldend:    ds.b 0
     align
pxy_array: ds.w 4
px:        dc.w 0, 0, x_pixels, y_pixels
stack_bot:     ds.w 512
stack_top:     ds.b 0
     end 