aboutsummaryrefslogtreecommitdiff
path: root/comp1521/cellular/cellular.s
diff options
context:
space:
mode:
authorNicolas James <Eele1Ephe7uZahRie@tutanota.com>2025-02-13 18:00:17 +1100
committerNicolas James <Eele1Ephe7uZahRie@tutanota.com>2025-02-13 18:00:17 +1100
commit98cef5e9a772602d42acfcf233838c760424db9a (patch)
tree5277fa1d7cc0a69a0f166fcbf10fd320f345f049 /comp1521/cellular/cellular.s
initial commit
Diffstat (limited to 'comp1521/cellular/cellular.s')
-rw-r--r--comp1521/cellular/cellular.s362
1 files changed, 362 insertions, 0 deletions
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----------------------------------