From 98cef5e9a772602d42acfcf233838c760424db9a Mon Sep 17 00:00:00 2001 From: Nicolas James Date: Thu, 13 Feb 2025 18:00:17 +1100 Subject: initial commit --- comp1521/cellular/cellular.s | 362 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 362 insertions(+) create mode 100644 comp1521/cellular/cellular.s (limited to 'comp1521/cellular/cellular.s') diff --git a/comp1521/cellular/cellular.s b/comp1521/cellular/cellular.s new file mode 100644 index 0000000..3bdd7aa --- /dev/null +++ b/comp1521/cellular/cellular.s @@ -0,0 +1,362 @@ +######################################################################## +# COMP1521 20T2 --- assignment 1: a cellular automaton renderer + +# Complex logical statements are marked with horizontal bars for readability. + +# Maximum and minimum values for the 3 parameters. + +MIN_WORLD_SIZE = 1 +MAX_WORLD_SIZE = 128 +MIN_GENERATIONS = -256 +MAX_GENERATIONS = 256 +MIN_RULE = 0 +MAX_RULE = 255 + +# Characters used to print alive/dead cells. + +ALIVE_CHAR = '#' +DEAD_CHAR = '.' + +# Maximum number of bytes needs to store all generations of cells. + +MAX_CELLS_BYTES = (MAX_GENERATIONS + 1) * MAX_WORLD_SIZE + + .data + +# `cells' is used to store successive generations. Each byte will be 1 +# if the cell is alive in that generation, and 0 otherwise. + +cells: .space MAX_CELLS_BYTES +#cells: .byte 1:MAX_CELLS_BYTES # Very useful for debugging. + +# Some strings you'll need to use: + +prompt_world_size: .asciiz "Enter world size: " +error_world_size: .asciiz "Invalid world size\n" +prompt_rule: .asciiz "Enter rule: " +error_rule: .asciiz "Invalid rule\n" +prompt_n_generations: .asciiz "Enter how many generations: " +error_n_generations: .asciiz "Invalid number of generations\n" + + .text + + # REGISTERS IN MAIN + # $s0: int world_size (runtime input) + # $s1: int rule (runtime input) + # $s2: int n_generations (runtime input) + # $s3: int reverse (0 or 1) + # $s4: int g (loop counters) + # $t0 to $t2 (temp variables) + # $v0 to $v2 (syscalls and functions) + +#--------------------------------BEGIN_MAIN------------------------------------- + +main: + # No need to store $ra on the stack until we call our own functions. + + la $a0, prompt_world_size # printf("Enter world size: "); + li $v0, 4 + syscall + + move $s0, $zero # $s0: int world_size = 0; + li $v0, 5 # scanf("%d", &world_size); + syscall + move $s0, $v0 + + blt $s0, MIN_WORLD_SIZE, if0 # if (world_size < MIN_WORLD_SIZE) + bgt $s0, MAX_WORLD_SIZE, if0 # ^|| (world_size > MAX_WORLD_SIZE); + b skip0 +if0: + la $a0, error_world_size # printf("Invalid world size\n"); + li $v0, 4 + syscall + li $v0, 1 # return 1; + jr $ra + # Despite returning 1, echo $? says '0'. I suspect this is due to spim + # returning 0 on the "successful" execution of a program. I am unaware + # of how to extract the return value of an emulated mips program. + +skip0: + la $a0, prompt_rule # printf("Enter rule: "); + li $v0, 4 + syscall + + move $s1, $zero # $s1: int rule = 0; + li $v0, 5 # scanf("%d", &world_size); + syscall + move $s1, $v0 + + blt $s1, MIN_RULE, if1 # if (world_size < MIN_RULE) + bgt $s1, MAX_RULE, if1 # ^|| (world_size > MAX_RULE); + b skip1 +if1: + la $a0, error_rule # printf("Invalid world size\n"); + li $v0, 4 + syscall + li $v0, 1 # return 1; + jr $ra + +skip1: + la $a0, prompt_n_generations # printf("Enter how many generations "); + li $v0, 4 + syscall + + move $s2, $zero # $s2: n_generations = 0; + li $v0, 5 # scanf("%d", &world_size); + syscall + move $s2, $v0 + + blt $s2, MIN_GENERATIONS, if2 # if (n_generations < MIN_GENERATIONS) + bgt $s2, MAX_GENERATIONS, if2 # ^|| (n_generations > MAX_GENERATIONS); + b skip2 +if2: + la $a0, error_n_generations # printf("Invalid world size\n"); + li $v0, 4 + syscall + li $v0, 1 # return 1; + jr $ra + +skip2: + li $a0, '\n' # putchar('\n'); + li $v0, 11 + syscall + + move $s3, $zero # $s3: int reverse = 0; + blt $s2, 0, if3 # if (n_generations < 0); + b skip3 +if3: + li $s3, 1 # reverse = 1; + mul $s2, $s2, -1 # n_generations = -n_generations; + +skip3: + la $t0, cells # cells[0][world_size / 2] = 1; + li $t1, 2 + div $s0, $t1 + mflo $t1 + add $t0, $t0, $t1 + li $t2, 1 + sb $t2, ($t0) + + # After this point we begin to use our functions, so save the stack pointer. + sub $sp, $sp, 4 + sw $ra, ($sp) + + +#-------------------------------FIRST_LOOP_START-------------------------------- + + li $s4, 1 # for (int g = 1; ($s4: int g = 1) +loop0: + bgt $s4, $s2, end0 # ^g <= n_generations; (test condition) + + move $a0, $s0 #run_generation(world_size, g, rule); + move $a1, $s4 + move $a2, $s1 + jal run_generation + + add $s4, $s4, 1 # g++;); (increment) + b loop0 +end0: + +#--------------------------------FIRST_LOOP_END--------------------------------- +#--------------------------------------IF--------------------------------------- + + beqz $s3, skip4 # if (reverse) + +#------------------------------SECOND_LOOP_START-------------------------------- + move $s4, $s2 # for (int g = n_generations; + # ($s4: int g = n_generations) + +loop1: + blt $s4, 0, end1 # ^g >= 0; (test condition) + + move $a0, $s0 # print_generation(world_size, g); + move $a1, $s4 + jal print_generation + + sub $s4, $s4, 1 # g--;); (decrement) + b loop1 +end1: +#------------------------------SECOND_LOOP_END---------------------------------- + b skip5 +#------------------------------------ELSE--------------------------------------- +skip4: # else +#------------------------------THIRD_LOOP_START--------------------------------- + li $s4, 0 # for (int g = 0; ($s4: int g = 0) +loop2: + bgt $s4, $s2, end2 # ^g <= n_generations; (test_condition) + + move $a0, $s0 # print_generation(world_size, g); + move $a1, $s4 + jal print_generation + + add $s4, $s4, 1 # g++;) (increment) + b loop2 + +end2: +#-------------------------------THIRD_LOOP_END---------------------------------- +skip5: +#----------------------------------ELSE_END------------------------------------- + + lw $ra, ($sp) # Load $ra from the stack. + add $sp, $sp, 4 # Deallocate from the stack, now empty. + li $v0, 0 # Return 0. + jr $ra +#----------------------------------MAIN_END------------------------------------- + + + # Given `world_size', `which_generation', and `rule', calculate + # a new generation according to `rule' and store it in `cells'. + + # REGISTERS IN RUN_GENERATION + # $a0: int world_size (passed to function) + # $a1: int which_generation (passed to function) + # $a2: int rule (passed to function) + # $t0: int x (copy of arguments) CHANGED! + # $t1: int left (copy of arguments) CHANGED! + # $t2: int centre (copy of arguments) CHANGED! + # $t3: int right (increment variable) CHANGED! + # $t4 to $t5: (temp variables) CHANGED! + # $v0: int (syscalls) CHANGED! + +#-----------------------------BEGIN_FUNCTION_0---------------------------------- +run_generation: + + # In this function we are pressed for variables. We will modify $a variables + # but continue to preserve them without using the stack as they are + # easily reversible changes which we may keep track of and fix before we + # return. + +#---------------------------------BEGIN_LOOP------------------------------------ + li $t0, 0 # for(int x = 0; ($t0: int x = 0) +f0loop0: + bge $t0, $a0, f0end0 # ^x < world_size + + li $t1, 0 # $t1: int left = 0; + + sub $a1, $a1, 1 # which_generation-- + sub $t0, $t0, 1 # x-- + + la $t2, cells # $t3 = &cells[which_generation-1][x-1] + mul $t3, $a1, MAX_WORLD_SIZE + add $t3, $t2, $t3 + add $t3, $t3, $t0 + + add $t0, $t0, 1 # x++ + ble $t0, 0, f0skip0 # if (x > 0) + lb $t1, ($t3) # left = cells[which_generation-1][x-1] + +f0skip0: + add $t3, $t3, 1 # ++t3, access [x] not [x-1] + lb $t2, ($t3) # $t2 = cells[which_generation-1][x] + + add $t4, $t3, 1 # copy useful ++$t3 before we use $t3 + + li $t3, 0 # $t3: int right = 0 + sub $a0, $a0, 1 # world_size-- + bge $t0, $a0, f0skip1 + + lb $t3, ($t4) # right = cells[which_generation-1][x+1] +f0skip1: + add $a0, $a0, 1 # world_size++ + add $a1, $a1, 1 # which_generation++ + + sll $t1, $t1, 2 # int state = left << 2 | centre << 1 + sll $t2, $t2, 1 # ^ | right << 0; + or $t5, $t1, $t2 + or $t5, $t5, $t3 + + li $t1, 1 + sll $t5, $t1, $t5 # int bit = 1 << state; + and $t5, $a2, $t5 # int set = rule & bit; + + la $t2, cells # $t2 = &cells[which_generation][x] + mul $t3, $a1, MAX_WORLD_SIZE + add $t3, $t2, $t3 + add $t3, $t3, $t0 + +#--------------------------------------IF--------------------------------------- + beqz $t5, f0skip2 # if (set) + + li $t1, 1 # cells[which_generation][x] = 1; + sb $t1, ($t3) + + b f0skip3 +f0skip2: +#------------------------------------ELSE--------------------------------------- + li $t1, 0 # cells[which_generation][x] = 0; + sb $t1, ($t3) +f0skip3: + + add $t0, $t0, 1 # x++ (increment) + b f0loop0 +#-----------------------------------END_ELSE------------------------------------ +f0end0: +#-----------------------------------END_LOOP------------------------------------ + jr $ra # return; +#-------------------------------END_FUNCTION_0---------------------------------- + + # Given `world_size', and `which_generation', print out the + # specified generation. + + # REGISTERS IN PRINT_GENERATION + # $a0: int world_size (passed to function) CHANGED! + # $a1: int which_generation (passed to function) + # $t0: int world_size (copy of arguments) CHANGED! + # $t1: int which_generation (copy of arguments) CHANGED! + # $t2: int x (0++, loop counter) CHANGED! + # $t3 to $t4: (temp variables) CHANGED! + # $v0 (syscalls) CHANGED! + +#-----------------------------BEGIN_FUNCTION_1---------------------------------- +print_generation: + move $t0, $a0 # Copy arguments, to use syscalls. + move $t1, $a1 + + move $a0, $t1 # printf("%d", which_generation); + li $v0, 1 + syscall + + li $a0, '\t' # putchar('\t'); + li $v0, 11 + syscall + +#-------------------------------FOR_LOOP_START---------------------------------- + li $t2, 0 # for (int x = 0; ($t2: int x = 0) +f1loop0: + bge $t2, $t0, f1end0 # ^x < world_size; (test condition) + +#-------------------------------------IF---------------------------------------- + la $t3, cells # if(cells[which_generation][x]) + mul $t4, $t1, MAX_WORLD_SIZE + add $t4, $t4, $t2 + add $t4, $t4, $t3 + lb $t4, ($t4) + beqz $t4, f1skip0 + + li $a0, ALIVE_CHAR # putchar(ALIVE_CHAR); + li $v0, 11 + syscall + b f1skip1 + + +f1skip0: +#------------------------------------ELSE--------------------------------------- + li $a0, DEAD_CHAR # putchar(DEAD_CHAR); + li $v0, 11 + syscall + + +f1skip1: +#----------------------------------ELSE_END------------------------------------- + add $t2, $t2, 1 # x++;); (increment) + b f1loop0 +f1end0: + +#--------------------------------FOR_LOOP_END----------------------------------- + + li $a0, '\n' # putchar('\n'); + li $v0, 11 + syscall + + jr $ra # return; +#-------------------------------END_FUNCTION_1---------------------------------- -- cgit v1.2.3