CSC231 Developing the Game of Life in Assembly
--D. Thiebaut (talk) 12:25, 27 March 2017 (EDT)
Contents
Python Program Developed in Class (3/27/17)
Source
dish = [ ' ', ' ', ' ', ' ', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', ' ', ' ', ' ', ' ' ] newGen = [' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ', '#', ' ' ] N = len( dish ) # applies the rules of life to the array dish, # and stores the result in the array newGen, which # is returned. N is the number of cells. def life( dish, N ): newGen = [' '] * N # loop through all the cells for i in range( 1, N-1 ): # look at fate of cell neighbors = 0 if dish[i-1] == '#': neighbors += 1 if dish[i+1] == '#': neighbors += 1 if neighbors == 1: newGen[i] = '#' else: newGen[i] = ' ' return newGen def main(): global dish, newGen, N maxGen = 20 print( "".join( dish ) ) # loop through generations for generation in range( maxGen ): newGen = life( dish, N ) # display new generation print( "".join( newGen ) ) # future becomes the present dish = newGen main()
Output
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
Python Program, Version 2
This program uses no tests, and a couple of programming tricks provided by students in class.
# gameOfLife.py # D. Thiebaut # 1-Dimensional Game of Life where cells are maintained # as arrays of 0s and 1s. 0 means dead, 1 means alive. # # This program uses a neat trick provided by Artemis # in class, which recognizes that the fate of a # cell is equal to the xor of its neighbors. # two live neighbors correspond to 1 xor 1 = 0. Cell dies. # two dead neighbors correspond to 0 xor 0 = 0. Cell dies. # only one neighbor alive corresponds to 0 xor 1 = 1. Cell lives. # The other neat trick offered by Emma is to add space (' ') # to the value of a cell before printing. If a cell is dead, # adding 0 to ' ' makes it a space. Adding 1 to ' ' makes it '!' from __future__ import print_function from __future__ import division def life( dish, N ): newGen = [0]*N for i in range( 1, N-1 ): fate = dish[i-1] ^ dish[i+1] # ^ is xor newGen[i] = fate return newGen def printDish( dish ): #print( "".join( [ str(chr(ord(' ') + c)) for c in dish] ) ) for x in dish: s = str( chr( ord(' ') + x ) ) print( s, sep="", end="" ) print() def main(): N = 40 dish = (N//2-10)*[1] + 10*[0,1] + (N//2-10)*[0] dish = dish[0:N] printDish( dish ) # print first generation # repeat, for some number of generations for generation in range( 20 ): newGen = life( dish, N ) printDish( newGen ) dish = newGen main()
Output
!!!!!!!!!! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !! ! ! ! ! ! ! !!!! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !! ! ! ! ! ! ! ! ! ! ! !! !! !! ! ! ! ! ! ! ! ! !!!!!!!!!!!! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !! ! ! ! ! ! ! !! ! ! !
Assembly
Note, at the time we created this program, we didn't have available to us tests or functions, which makes the code slightly more complicated than needed.
Version
Source
;;; ; GameOfLife.asm ;;; ; Authors: D. Thiebaut and CSC231 Class ;;; ; ;;; ; Implements a 1-dimensional version of ;;; ; Conway's Game of life. ;;; ; Note that because we do not "know" yet ;;; ; how to test or to use functions, the code ;;; ; is not as efficient as it could be. ;;; ; ;;; ; to assemble and run: ;;; ; ;;; ; nasm -f elf -F stabs GameOfLife.asm ;;; ; ld -melf_i386 -o GameOfLife GameOfLife.o ;;; ; ./GameOfLife ;;; ; ------------------------------------------------------------------- ;;; ------------------------------------------------------------ ;;; data areas ;;; ------------------------------------------------------------ section .data ;;; the current dish with its cells. We use 0 and 1 to ;;; indicate dead and live cells, respectively. dish db 0,0,0,1,1,1,0,1,0,1,0,1,0,1 db 0,1,0,1,0,1,0,1 db 0,1,0,1,0,1,0,1 db 0,1,0,1,0,1,0,1 db 0,1,0,1,0,1,0,1,0,0,0,0,0 N equ $-dish ; The number of cells ; in the dish ;;; the new generation of cells. That's the "future" of the ;;; cells in the dish. We make it the same size as dish, and ;;; fill it with N dead cells. newGen times N db 0 ;;; dishP is a string that will contain ' ' or '!' for ;;; each 0 or 1 in dish. dishP times N db ' ' db 10 ; add a \n at the end ; of dishP maxGen dd 20 ; the max number of ; generations generation dd 0 ; save ecx from for generation loop ;;; ------------------------------------------------------------ ;;; code area ;;; ------------------------------------------------------------ section .text global _start _start: main: ;;; ------------------------------------------------------------- ;;; print( dish ) ;; first we copy dish into dishP and at the same ;; time replace 0 by ' ' and 1 by '!' mov ecx, N ;for each byte in dish mov esi, dish ;esi points to source mov edi, dishP forPrint: mov al, byte[esi] add al, ' ' ;0 becomes ' ', 1 becomes '!' mov byte[edi], al inc esi ;esi points to next byte in dish inc edi ;edi points to next byte in dispP loop forPrint ;; print dishP as a string. mov eax, 4 ;prints dishP to screen mov ebx, 1 mov ecx, dishP ;dishP address mov edx, N+1 ;+1 to include the \n int 0x80 ;;; ----------------------------------------------------------- ;;; for generation in range( 20 ): mov ecx, 20 ;get ready to loop 20 times forGen: mov dword[generation],ecx ;save ecx in generation temp var ;;; ----------------------------------------------------------- ;;; newGen = life( dish, N ) ;;; newGen[0] = 0 ;;; newGen[N-1] = 0 mov byte[newGen], 0 mov byte[newGen+N-1], 0 ;;; ----------------------------------------------------------- ;;; for i in range( 1, N-1 ): mov ecx, N-2 mov esi, dish+1 mov edi, newGen+1 forLife: ;;; ----------------------------------------------------------- ;;; fate = dish[i-1] ^ dish[i+1] ;;; newGen[i] = fate mov al, byte[esi-1] xor al, byte[esi+1] mov byte[edi], al inc esi inc edi loop forLife ;;; ----------------------------------------------------------- ;;; dish = newGen mov ecx, N mov esi, newGen mov edi, dish forMove: mov al, byte[esi] ;copy newGen[i] to dish[i] mov byte[edi], al inc esi inc edi loop forMove ;;; ----------------------------------------------------------- ;;; print( dish ) ;; first we copy dish into dishP and at the same ;; time replace 0 by ' ' and 1 by '!' mov ecx, N ;for each byte in dish mov esi, dish ;esi points to source mov edi, dishP forPrint2: mov al, byte[esi] add al, ' ' ;0 becomes ' ', 1 becomes '!' mov byte[edi], al inc esi ;esi points to next byte in dish inc edi ;edi points to next byte in dispP loop forPrint2 ;; print dishP as a string. mov eax, 4 ;prints dishP to screen mov ebx, 1 mov ecx, dishP ;dishP address mov edx, N+1 ;+1 to include the \n int 0x80 ;;; ready to loop back to forGen mov ecx,dword[generation] loop forGen ;;; exit() mov eax,1 mov ebx,0 int 0x80 ; final system call
Output
!!! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !! ! ! !!! ! ! ! ! !!! ! ! ! ! ! ! ! ! ! ! ! !! ! ! ! ! !!! ! ! ! ! !!! ! ! ! ! ! ! ! ! ! ! ! ! !! !! ! ! ! ! !!! !!! ! ! ! ! ! ! !!! ! ! !!! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !! ! ! ! ! ! ! ! ! !!! ! ! ! ! !!! ! ! ! ! ! ! ! ! ! ! ! ! !! !! ! ! ! ! !!! !!! ! ! ! ! ! ! !!! ! ! !!! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !