COMP1521 24T3 M15B tutorial code

Here you'll find all the code I've written in the M15B tutorials, as well as some possibly useful information:

Week #9

print_diary.c

// Write a C program, print_diary.c, which prints the contents
// of the file $HOME/.diary to stdout

// $HOME/.diary
// /home/xav/.diary

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main(void) {
    char *home_environment = getenv("HOME");
    printf("home_environment = \"%s\"\n", home_environment);

    int path_size = strlen(home_environment) + strlen("/.diary") + 1;
    char *path = malloc(path_size);

    snprintf(path, path_size, "%s/.diary", home_environment);

    printf("path = \"%s\"\n", path);

    FILE *stream = fopen(path, "r");
    free(path);

    int byte;
    while ((byte = fgetc(stream)) != EOF) {
        putchar(byte);
    }

    fclose(stream);
}

chmod_if_public_write.c

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>

// ./chmod_if_public_write print_diary.c file code.txt

void chmod_if_public_write(char *path);

int main(int argc, char *argv[]) {
    for (int i = 1; i < argc; i++) {
        chmod_if_public_write(argv[i]);
    }
}

void chmod_if_public_write(char *path) {
    // 1. determine the permissions

    struct stat statbuf;
    if (stat(path, &statbuf) != 0) {
        perror(path);
        return;
    }

    // 2. check if the public write bit is set

    // rwxrwxrwx
    //        ^
    // 000000010


    if ((statbuf.st_mode & S_IWOTH) == 0) {
        printf("%s does not have public write bit set!\n", path);
        return;
    }

    // 3. if it is set...
    //     4. remove public write bit

    // ~2 = 111111101
    if (chmod(path, statbuf.st_mode & ~S_IWOTH) == 0) {
        //     5. and maybe print out a message
        printf("public write bit removed from %s!\n", path);
    } else {
        perror(path);
    }

}

Week #8

first_line.c

// Write a C program, first_line.c, which is given one command-line argument,
// the name of a file, and which prints the first line of that file to stdout.
// If given an incorrect number of arguments, or if there was an error opening
// the file, it should print a suitable error message.

#include <stdio.h>
#include <stdlib.h>

// $ ./first_line first_line.c
// argv[0] = "./first_line"
// argv[1] = "first_line.c"
// argc = 2

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: ./first_line <file>\n");
        return 1;
    }

    char *path = argv[1];
    FILE *stream = fopen(path, "r");

    if (stream == NULL) {
        perror(path);
        return 1;
    }

    // functions to read from files: fscanf, fread, fgetc, fgets
    // functions to writes from files: fprintf, fwrite, fputc

    int byte;
    while ((byte = fgetc(stream)) != EOF) {
        putchar(byte);

        if (byte == '\n') {
            break;
        }
    }

    if (fclose(stream) != 0) {
        perror(path);
    }

    return 0;
}

Week #7

Here's the presentation

get_bit_groups.c

#include <stdint.h>

typedef struct six_bit_groups {
    uint8_t middle_bits;
    uint8_t lower_bits;
} six_bit_groups_t;

/*
Example:

    01100010   10101101   11001100   10010011
                    ^^^   ^^^          ^^^^^^
                      middle            lower
                       = 46             = 19

    01100010   10101101   11001100   10010011 <-- value
    00000000   00000000   00000000   00111111 <-- mask
    00000000   00000000   00000000   01000000 <-- 1 << 6

    00000000   00000000   00000000   00010011 <-- mask & value

*/


six_bit_groups_t get_bit_groups(uint32_t value) {
    struct six_bit_groups answer;

    uint32_t mask = 0x3F;
    // uint32_t mask = 0b111111;
    // uint32_t mask = 077;
    // uint32_t mask = (1 << 6) - 1;
    answer.lower_bits = value & mask;
    answer.middle_bits = (value >> 13) & mask;
    // answer.middle_bits = (value & (mask << 13)) >> 13;

    return answer;
}

float_weird.c

#include <stdio.h>
#include <math.h>

int main(void) {
    float a = 0.1;
    float b = 0.2;
    float sum = a + b;

    printf("a = %.20g, b = %.20g and sum = %.20g\n", a, b, sum);
    printf("And sum == 0.3 is: %d\n", fabsf(sum - 0.3f) < 0.0001);
}

Week #5

reverse_bits.c

#include <assert.h>
#include <stdio.h>
#include <stdint.h>


// Reverse a 32 bit value
uint32_t reverse_bits(uint32_t value) {
    uint32_t answer = 0;

    for (int i = 0; i < 32; i++) {
        // want to check if 'bit i' in `value` is set to a 1?
        if ((value & (1u << i)) != 0) {
            // ==> if it is: we set the 'opposite' bit in answer to a 1
            // printf("there's a 1 in bit %d\n", i);

            int opposite_bit_position = 31 - i;
            answer |= 1ull << opposite_bit_position;
        }
    }

    return answer;
}

int main(void) {
    // reverse_bits(42);
    assert(reverse_bits(0xFFFFFFFF) == 0xFFFFFFFF);
    assert(reverse_bits(0x00000000) == 0x00000000);
    assert(reverse_bits(0x1) == 0x80000000);
    assert(reverse_bits(0x2) == 0x40000000);
    assert(reverse_bits(0x01234567) == 0xE6A2C480);
    printf("All tests passed!\n");
    return 0;
}

examples.txt

uint16_t a = 0x5566, b = 0xAAAA, c = 0x0001;


   a) a | b
   b) a & b
   c) a ^ b
   d) a & ~b

   e) c << 6
   f) a >> 4
   g) a & (b << 1)
   h) b | c
   i) a & ~c

Week #4

Here's the spreadsheet I used

rec_max.c

#include <stdio.h>

// C function to find the largest element in an array, recursively.
// Returns the value of the largest element in the array.
//
// array:  Array to search
// length: Number of elements in the array
int rec_max(int array[], int length) {
    int first_element = array[0];
    if (length == 1) {
        // Handle the base-case of the recursion, at the end of the array.
        return first_element;
    } else {
        // Recurse on the rest of the array.
        // Finds the largest element after first_element in the array.
        int max_so_far = rec_max(&array[1], length - 1);

        // Compare this element with the largest element after it in the array.
        if (first_element > max_so_far) {
            max_so_far = first_element;
        }
        return max_so_far;
    }
}

int main(void) {
    int data[10] = {2, -3, 5, 4, 42, 2, 8, 4, 1, 9};

    printf("%d\n", rec_max(data, 10));

    return 0;
}

rec_max.s

########################################################################
# COMP1521 24T3 -- ...
#
#
# !!! IMPORTA first_element;NT !!!
# Before starting work on the assignment, make sure you set your tab-width to 8!
# It is also suggested to indent with tabs only.
# Instructions to configure your text editor can be found here:
#   https://cgi.cse.unsw.edu.au/~cs1521/24T3/resources/mips-editors.html
# !!! IMPORTANT !!!
#
#
# This program was written by YOUR-NAME-HERE (z5555555)
# on INSERT-DATE-HERE. INSERT-SIMPLE-PROGRAM-DESCRIPTION-HERE
#
# Version 1.0 (12-06-2023): Team COMP1521 <cs1521@cse.unsw.edu.au>
#
########################################################################

	.text
rec_max:
	# Args:
	#    - $a0: int array[]
	#    - $a1: int length
	#
	# Returns:
	#    - $v0: const char *
	#
	# Frame:    [$ra, $s0]
	# Uses:     [$a0, $a1, $s0, $t1, $t2, $t3, $ra, $v0]
	# Clobbers: [$s0, $t1, $t2, $t3, $a0, $a1, $v0]
	#
	# Locals:
	#   - $s0: first_element
	#   - $t1: array
	#   - $t2: length
	#   - $t3: max_so_far
	#
	# Structure:
	#   current_player_str
	#   -> [prologue]
	#       -> body
	#   -> [epilogue]

	# int rec_max(int array[], int length) {

rec_max__prologue:
	begin
	push	$ra
	push	$s0

rec_max__body:
	move	$t1, $a0
	move	$t2, $a1

	# int first_element = array[0];
	lw	$s0, ($t1)

	bne	$t2, 1, rec_max__length_ne_1				# if (length == 1) {

	# return first_element;
	move	$v0, $s0
	b	rec_max__epilogue

rec_max__length_ne_1:							# } else {

	# int max_so_far = rec_max(&array[1], length - 1);
	addi	$a0, $t1, 4						#   &array[1]
	sub	$a1, $t2, 1						#   length - 1
	jal	rec_max
	move	$t3, $v0

	ble	$s0, $t3, rec_max__ret_max_so_far			#   if (first_element > max_so_far) {

	# max_so_far = first_element;
	move	$t3, $s0

rec_max__ret_max_so_far:						#   }
	# return max_so_far;
	move	$v0, $t3

rec_max__epilogue:							# }
	pop	$s0
	pop	$ra
	end

	jr	$ra



main:
main__prologue:
	push	$ra

main__body:
	la	$a0, test_data
	li	$a1, 10
	jal	rec_max				# result = rec_max(test_data, 10)

	move	$a0, $v0
	li	$v0, 1				# syscall 1: print_int
	syscall					# printf("%d", result);

	li	$v0, 11				# syscall 11: print_char
	li	$a0, '\n'
	syscall					# printf("%c", '\n');

	li	$v0, 0
main__epilogue:
	pop	$ra
	jr	$ra				# return 0;

	.data
test_data:
	.word 2, -3, 5, 4, 42, 2, 8, 4, 1, 9

topography.c

// A topographic map!
// This helpful tool will tell explorers how much they need to climb to
// reach various points of interest.
// Given an array of points, `my_points`, it can look up individual cells
// in the 2D map and print their height.

#include <stdio.h>

#define NUM_ROWS 5
#define NUM_COLS 5
#define N_POINTS 4

// 2D representation of a point, stored as a single struct
struct point2D {
    int row;
    int col;
} typedef point2D_t;

// 2D grid representing the height data for an area.
int topography_grid[NUM_ROWS][NUM_COLS] = {
    { 0, 1, 1, 2, 3 },
    { 1, 1, 2, 3, 4 },
    { 1, 2, 3, 5, 7 },
    { 3, 3, 4, 5, 6 },
    { 3, 4, 5, 6, 7 },
};

// Points of interest to print heights for, as a 1D array.
point2D_t my_points[N_POINTS] = {
    { 1, 2 },
    { 2, 3 },
    { 0, 0 },
    { 4, 4 },
};

int main(void) {
    for (int i = 0; i < N_POINTS; i++) {
        int row = my_points[i].row;
        int col = my_points[i].col;
        int height = topography_grid[row][col];

        printf("Height at %d,%d=%d\n", row, col, height);
    }

    return 0;
}

topography.s

# A topographic map!

# Constants
NUM_ROWS = 5
NUM_COLS = 5
N_POINTS = 4

SIZEOF_INT = 4
SIZEOF_POINT2D = 8
POINT2D_ROW_OFFSET = 0
POINT2D_COL_OFFSET = 4

	.data
# 2D grid representing the height data for an area.
topography_grid:
	.word	0, 1, 1, 2, 3
	.word	1, 1, 2, 3, 4
	.word	1, 2, 3, 5, 7
	.word	3, 3, 4, 5, 6
	.word	3, 4, 5, 6, 7

# Points of interest to print heights for, as a 1D array of point2D_t structs.
# Note the memory layout of this array: each element requires 8 bytes, not 4.
my_points:
	.word	1, 2
	.word	2, 3
	.word	0, 0
	.word	4, 4

height_str:
	.asciiz "Height at "


	.text
main:
	# Registers:
	#   - $t0: int i, the loop counter
	#   - $t1: row of the current point
	#   - $t2: col of the current point
	#   - $t3: height of the current point
	#   - $t4: temporary result for array indexing
	#   - $t5: temporary result for array indexing

					# Loop over all elements, and print their data
points_loop_init:			# for (int i = 0; i < N_POINTS; i++) {
	li	$t0, 0			# $t0 = 0

points_loop_cond:
	bge	$t0, N_POINTS, points_loop_end	# if (i >= N_POINTS)

	# We have i in $t0
	# We want row in $t1
	# We want col in $t2
	# We want height in $t3

	# int row = my_points[i].row;

	# 1. figure out address, get it in a register

	# get &my_points[i]
	la	$t4, my_points
	mul	$t5, $t0, SIZEOF_POINT2D
	add	$t4, $t4, $t5			# &my_points[i]
	# 2. is it a load or store? ==> it's a load
	# 3. byte, half or word? ==> word
	lw	$t1, ($t4)

	# int col = my_points[i].col;

	lw	$t2, POINT2D_COL_OFFSET($t4)

	# int height = topography_grid[row][col];

	# &data[row][col] = base + (row * NUM_COLS + col) * sizeof(element)
	mul	$t4, $t1, NUM_COLS
	add	$t4, $t4, $t2
	mul	$t4, $t4, 4
	la	$t5, topography_grid
	add	$t4, $t4, $t5
	lw	$t3, ($t4)

					# printf("Height at %d,%d=%d\n", row, col, height);

	li	$v0, 4			# $v0 = 4 (print string)
	la	$a0, height_str		# load address of height_str into $a0
	syscall				# print height_str

	li	$v0, 1			# $v0 = 1 (print int)
	move	$a0, $t1		# $a0 = row
	syscall				# print row

	li	$v0, 11			# $v0 = 11 (print ASCII character)
	li	$a0, ','		# $a0 = ','
	syscall				# print ','

	li	$v0, 1			# $v0 = 1 (print int)
	move	$a0, $t2		# $a0 = col
	syscall				# print col

	li	$v0, 11			# $v0 = 11 (print ASCII character)
	li	$a0, '='		# $a0 = '='
	syscall				# print '='

	li	$v0, 1			# $v0 = 1 (print int)
	move	$a0, $t3		# $a0 = height
	syscall				# print height

	li	$v0, 11			# $v0 = 11 (print ASCII character)
	li	$a0, '\n'		# $a0 = '\n'
	syscall				# print '\n'

points_loop_iter:
	addi	$t0, $t0, 1		# i++
	b	points_loop_cond	# branch to points_loop_cond

points_loop_end:

	jr	$ra			# return 0;

Week #3

Here's the spreadsheet I used

print_array.c

// A simple program that will print 10 numbers from an array

#define N_SIZE 10

#include <stdio.h>

// int -> word
// short -> half
// char -> byte

int numbers[N_SIZE] = {
    3, 1, 4, 1, 5, 9, 2, 6, 5, 3
};

int main(void) {
    int i;

    i = 0;
    while (i < N_SIZE) {
        int num = numbers[i];
        printf("%d\n", num);
        i++;
    }
}

print_array.s

	N_SIZE = 10

main:
	# Locals:
	#    i: $t0
	#    num: $t1

loop__init:
	# i = 0;
	li	$t0, 0

loop__cond:
	# while (i < N_SIZE) {
	bge	$t0, N_SIZE, loop__end

loop__body:
        # int num = numbers[i];

	# 1. what is the addrss of the object?
	# &numbers[i] = &numbers + sizeof(element) * i
	la	$t2, numbers		# &numbers
	mul	$t3, $t0, 4		# i * 4
	add	$t2, $t2, $t3		# &numbers + i * 4
					# == &numbers[i]

	# 2. is it a byte, a half or a word?
	#   ==> it's a word
	# 3. is it a load or a store?
	#   ==> it's a load

	lw	$t1, ($t2)

        # printf("%d\n", num);
	li	$v0, 1
	move	$a0, $t1
	syscall

	li	$v0, 11
	li	$a0, '\n'
	syscall

loop__increment:
	#    i++;
	addi	$t0, $t0, 1

	b	loop__cond
	# }
loop__end:
	# return 0;
	li	$v0, 0
	jr	$ra


	.data
numbers:
	.word	3, 1, 4, 1, 5, 9, 2, 6, 5, 3

read_array.c

// A simple program that will read 10 numbers into an array

#define N_SIZE 5

#include <stdio.h>

int numbers[N_SIZE];

int main(void) {
    int i;

    i = 0;
    while (i < N_SIZE) {
        scanf("%d", &numbers[i]);
        i++;
    }

    i = 0;
    while (i < N_SIZE) {
        int num = numbers[i];
        printf("%d\n", num);
        i++;
    }
}

read_array.s

	N_SIZE = 5

main:
	# Locals:
	#    i: $t0
	#    num: $t1

loop_read__init:
	li	$t0, 0

loop_read__cond:
	bge	$t0, N_SIZE, loop_read__end

loop_read__body:
	# scanf("%d", &numbers[i]);
	# numbers[i] = read_int();

	li	$v0, 5
	syscall
	move	$t4, $v0

	# 1. what is the addrss of the object?
	# &numbers[i] = &numbers + sizeof(element) * i
	la	$t2, numbers		# &numbers
	mul	$t3, $t0, 4		# i * 4
	add	$t2, $t2, $t3		# &numbers + i * 4
					# == &numbers[i]

	# 2. is it a byte, a half or a word?
	#   ==> it's a word
	# 3. is it a load or a store?
	#   ==> it's a store

	sw	$t4, ($t2)

loop_read__increment:
	# i++;
	addi	$t0, $t0, 1

	b	loop_read__cond

loop_read__end:

loop_print__init:
	# i = 0;
	li	$t0, 0

loop_print__cond:
	# while (i < N_SIZE) {
	bge	$t0, N_SIZE, loop_print__end

loop_print__body:
        # int num = numbers[i];

	# 1. what is the addrss of the object?
	# &numbers[i] = &numbers + sizeof(element) * i
	la	$t2, numbers		# &numbers
	mul	$t3, $t0, 4		# i * 4
	add	$t2, $t2, $t3		# &numbers + i * 4
					# == &numbers[i]

	# 2. is it a byte, a half or a word?
	#   ==> it's a word
	# 3. is it a load or a store?
	#   ==> it's a load

	lw	$t1, ($t2)

        # printf("%d\n", num);
	li	$v0, 1
	move	$a0, $t1
	syscall

	li	$v0, 11
	li	$a0, '\n'
	syscall

loop_print__increment:
	#    i++;
	addi	$t0, $t0, 1

	b	loop_print__cond
	# }
loop_print__end:
	# return 0;
	li	$v0, 0
	jr	$ra


	.data
numbers:
	# .word	0:N_SIZE
	.align	2
	.space	N_SIZE*4

Week #2

hello.s

# Write a MIPS assembly program that prints out "Hello, world!\n"

main:
	li	$v0, 4			# $v0 = 4
	la	$a0, hello_world_string	# $a0 = address of hello_world_string
	syscall

	li	$v0, 0
	jr	$ra			# return 0;

	.data
hello_world_string:
	.asciiz	"Hello, world!\n"

square.c

// Prints the square of a number

#include <stdio.h>

int main(void) {
    int x, y;

    printf("Enter a number: ");
    scanf("%d", &x);

    y = x * x;
    printf("%d", y);
    putchar('\n');
}

square.s

main:
	# Locals:
	#  - x: $t0
	#  - y: $t1

	li	$v0, 4		# $v0 4: print_string syscall
	la	$a0, enter_a_number_str
	syscall			# printf("Enter a number: ");

	li	$v0, 5		# $v0 5: read_int syscall
	syscall			# scanf("%d", &x);
	move	$t0, $v0	# $t0 = $v0

	mul	$t1, $t0, $t0	# y = x * x

	li	$v0, 1
	move	$a0, $t1
	syscall			# printf("%d", y);

	li	$v0, 11
	li	$a0, '\n'
	syscall			# putchar('\n');

	li	$v0, 0
	jr	$ra		# return 0;


	.data
enter_a_number_str:
	.asciiz	"Enter a number: "

bounded_square.c

// Squares a number, unless its square is too big for a 32-bit integer.
// If it is too big, prints an error message instead.

#include <stdio.h>

#define SQUARE_MAX 46340

int main(void) {
    int x, y;

    printf("Enter a number: ");
    scanf("%d", &x);

    // if (x > SQUARE_MAX) {
    // if (x <= SQUARE_MAX)
    //     goto else_branch;
    if (x > SQUARE_MAX) goto if_branch;
    goto else_branch;

if_branch:

    printf("square too big for 32 bits\n");
    goto epilogue;

    // } else {
else_branch:
        y = x * x;
        printf("%d\n", y);

    // }

epilogue:
    return 0;
}

bounded_square.s

	SQUARE_MAX = 46340

main:
	# Locals:
	#  - x: $t0
	#  - y: $t1

	li	$v0, 4
	la	$a0, enter_a_number_str
	syscall			# printf("Enter a number: ");

	li	$v0, 5
	syscall			# scanf("%d", &x);
	move	$t0, $v0	# $t0 = $v0

	ble	$t0, SQUARE_MAX, else_branch

	li	$v0, 4
	la	$a0, square_too_big
	syscall

	b	epilogue

else_branch:
	mul	$t1, $t0, $t0	# y = x * x

	li	$v0, 1
	move	$a0, $t1
	syscall

	li	$v0, 11
	li	$a0, '\n'
	syscall

epilogue:
	li	$v0, 0
	jr	$ra


	.data
enter_a_number_str:
	.asciiz	"Enter a number: "
square_too_big:
	.asciiz	"square too big for 32 bits\n"

for.c

// Print every third number from 24 to 42.
#include <stdio.h>

int main(void) {
    // for (int x = 24; x < 42; x += 3) {
    //     printf("%d\n", x);
    // }


loop_init:;
    int x =24;

loop_cond:
    if (x >= 42) goto loop_end;

loop_body:
    printf("%d\n", x);

loop_increment:
    x += 3;
    goto loop_init;

loop_end:

}

Week #1

factorial_scanf.c

// Write a program which calculates n!, where
//     n! = n * (n - 1) * (n - 2) * ... * 3 * 2 * 1
// e.g.
//     1! = 1 = 1
//     2! = 2 * 1 = 2
//     3! = 3 * 2 * 1 = 6
//     4! = 4 * 3 * 2 * 1 = 24
//     5! = 5 * 4 * 3 * 2 * 1 = 120

#include <stdio.h>

int main(void) {
    int n;
    if (scanf("%d", &n) != 1) {
        printf("Invalid input!\n");
        return 1;
    }

    int product = 1;

    for (int counter = 1; counter <= n; counter++) {
        product = product * counter;
    }
    // product = 1
    // product = 1
    // product = 2
    // product = 6
    // product = 24
    // product = 120

    printf("product = %d\n", product);

    return 0;
}

factorial.c

// Write a program which calculates n!, where
//     n! = n * (n - 1) * (n - 2) * ... * 3 * 2 * 1
// e.g.
//     1! = 1 = 1
//     2! = 2 * 1 = 2
//     3! = 3 * 2 * 1 = 6
//     4! = 4 * 3 * 2 * 1 = 24
//     5! = 5 * 4 * 3 * 2 * 1 = 120

#include <stdio.h>
#include <stdlib.h>

// ./factorial 5 hiii
//   argv[0] = "./factorial"
//   argv[1] = "5"
//   argv[2] = "hiii"
// argc = 3

// factorial_recurse(n) returns the factorial of n.
int factorial_recurse(int n) {
    if (n == 0) {
        return 1;
    }

    return n * factorial_recurse(n - 1);

    // n! = n * (n - 1) * (n - 2) * ... * 2 * 1;
    // n! = n * ((n - 1) * (n - 2) * ... * 2 * 1);
    // n! = n * (n - 1)!

    // int product = 1;

    // for (int counter = 1; counter <= n; counter++) {
    //     product = product * counter;
    // }

    // return product;
}

int main(int argc, char *argv[]) {
    int n = atoi(argv[1]);
    printf("n = %d\n", n);

    printf("factorial_recurse(n) = %d\n", factorial_recurse(n));

    return 0;
}