; 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


; Total x-pixels
total_x_pix = 640

; Total y-pixels
total_y_pix = 400

; Width of rectangle
pix_x_len   =   4

; Height of rectangle
pix_y_len   =   4

; Pixels in y-dimension
x_pixels    = (total_x_pix / pix_x_len) * pix_x_len

; Pixels in x-dimension
y_pixels    = (total_y_pix / pix_y_len) * pix_y_len

; Cells in x-dimension
x_cells     = x_pixels / pix_x_len

; Cells in y-dimension
y_cells     = y_pixels / pix_y_len

; Total number of cells
total_cells = x_cells * y_cells

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
             ; Produce 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
             move.w #(total_cells / 2 - 1), d0 ; d0 holds number of
                                               ; longwords to copy.
assign_lp:   move.w (a1)+,                  (a0)+
             dbf    d0,                     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
             move.w  #(total_cells / 2 - 1), d1 ; d1 holds number of
                                                ; longwords to set.
             move.w  #$0202,                 d0
init_lp:     move.w  d0,                     (a0)+
             dbf     d1,                     init_lp
             rts


; Produce 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

             move.w #(x_cells - 1),     d3 ; constant x_cells - 1
             move.w #(y_cells - 1),     d4 ; constant y_cells - 1

loop_5:      addq.l #1,                 a0
             addq.l #1,                 a1

             clr.b  d2 ; Compute number of oldgen[x, y]'s
                       ; neighbors.

             ; Don't compute neighbors of a cell on a border.
             tst.w  d0
             beq.s  update_cell

             tst.w  d1
             beq.s  update_cell

             cmp.w  d3,                 d0
             beq.s  update_cell

             cmp.w  d4,                 d1
             beq.s  update_cell

             ; Count the neighbors of the current cell.
             add.b  (-x_cells - 1)(a0), d2
             add.b  (-x_cells    )(a0), d2
             add.b  (-x_cells + 1)(a0), d2

             add.b  (-1)          (a0), d2
             add.b  (1)           (a0), d2

             add.b  (x_cells - 1) (a0), d2
             add.b  (x_cells)     (a0), d2
             add.b  (x_cells + 1) (a0), d2

update_cell: tst.b    d2
             beq.s    do_clear
             tst.b    (a0)
             beq.s    empty
             cmp.b    #2,               d2
             beq.s    do_set
             cmp.b    #3,               d2
             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,               d2
             beq.s    do_set
             bra.s    do_clear
next_x2:     addq.w   #1,               d0
             cmp.w    d3,               d0
             bgt.s    next_y2
             bra.s    loop_5
next_y2:     clr.w    d0
             addq.w   #1,               d1
             cmp.w    d4,               d1
             bgt.s    quit2
             bra.s    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       #(total_x_pix - 1), (a3)+
             move.w       #(total_y_pix - 1), (a3)
             v_bar        gr_handle,          pxy_array
             rts

; Generate a random cell pattern.
randomize:   lea     newgen,             a3
             move.w  #(total_cells - 1), d1
loop:        ; Save registers.
             movem.l      d1/a3,         -(sp)

             random

             ; Restore registers.
             movem.l      (sp)+,         d1/a3

             move.w  d0,                 d3
             and.w   #7,                 d3
             asr.b   d3,                 d0
             and.l   #1,                 d0
             move.b  d0,                 (a3)+
             dbf     d1,                 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
             move.w  #(x_cells - 1),     d4 ; constant x_cells - 1
             move.w  #(y_cells - 1),     d5 ; constant y_cells - 1
             ; Clear all cells on borders.
trim_loop:   addq.l  #1,                 a3
             tst.w   d0
             beq.s   clear_cell
             cmp.w   d4,                 d0
             beq.s   clear_cell
             tst.w   d1
             beq.s   clear_cell
             cmp.w   d5,                 d1
             beq.s   clear_cell
next_x_2:    addq.w  #1,                 d0
             cmp.w   d4,                 d0
             bgt.s   next_y_2
             bra.s   trim_loop
next_y_2:    clr.w   d0
             addq.w  #1,                 d1
             cmp.w   d5,                 d1
             bgt.s   return_3
             bra.s   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,           a1
             clr.w        d0       ; x-coordinate
             clr.w        d1       ; y-coordinate
             clr.w        d3
draw_loop:   move.b       (a0),             d3
             cmpm.b       (a0)+,            (a1)+
             beq          next_x

             ; Draw the rectangle.
             lea          pxy_array,        a2
             move.w       d0,               (a2)+
             move.w       d1,               (a2)+
             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,               (a2)+
             move.w       d6,               (a2)

             ; Save registers.
             movem.l      d0-d3/a0-a1,      -(sp)

             vsf_interior gr_handle,        d3,        d2
             v_bar        gr_handle,        pxy_array

             ; Restore registers.
             movem.l      (sp)+,            d0-d3/a0-a1

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
px:            dc.w 0, 0, total_x_pix, total_y_pix

     bss

     align
newgen:        ds.w total_cells / 2 + 1
oldgen:        ds.w total_cells / 2 + 1
pxy_array:     ds.w 4
stack_bot:     ds.w 512
stack_top:     ds.w 0
     end 