COMP1521 Revision 11a Revision Questions

Getting Started

Set up for the revision by creating a new directory called revision11a and changing to this directory.
mkdir revision11a
cd revision11a

There are some provided files for this revision which you can fetch with this command:

1521 fetch revision11a

If you're not working at CSE, you can download the provided files as a zip file or a tar file.

Revision Exercise: MIPS print odd (PS)

You have been given print_odd.s, which contains a MIPS function print_odd, that takes 2 arguments: a source array and its length.

Add code to print_odd so that it is equivalent to this C function:

void print_odd(int *src, int n) {   
    for (int i = 0; i < n; i++) {
        if (src[i] % 2 != 0) {
            printf("%d ", src[i]);
        }
    }
    printf("\n");
}

The function print_odd prints out every odd number in the array.

For example:

1521 mipsy test_print_odd.s print_odd.s
Enter 10 integers: 1
2
3
4
5
6
7
8
9
10

1 3 5 7 9
1521 mipsy test_print_odd.s print_odd.s
Enter 10 integers: 99
6
4
1
-4
0
2
87
3
5

99 1 87 3 5
1521 mipsy test_print_odd.s print_odd.s
Enter 10 integers: 2
4
6
8
10
12
14
16
18
20


You can use make(1) to build the provided C code:

make print_odd

You can use 1521 mipsy to run your MIPS code:

1521 mipsy test_print_odd.s print_odd.s

When you think your program is working you can use autotest to run some simple automated tests:

1521 autotest mips_revision_print_odd

Revision Exercise: MIPS every kth (PS)

You have been given every_kth.s, which contains a MIPS function every_kth, that takes 4 arguments: a source array, its length, an integer k and a destination array and returns the value 42.

Add code to every_kth so that it is equivalent to this C function:

int every_kth(int *src, int n, int k, int *dest) {   
    int count = 0;
    for (int i = 0; i < n; i++) {
        if (i % k == 0) {
            dest[count] = src[i];
            count++;
        }
    }
   return count;
}

The function every_kth copies every k-th element of src into dest and returns the number of elements copied.

It is assumed that destination array is large enough and k > 0.

For example:

1521 mipsy test_every_kth.s every_kth.s
Enter k: 3
Enter 10 integers: 1
2
3
4
5
6
7
8
9
10

1 4 7 10
1521 mipsy test_every_kth.s every_kth.s
Enter k: 2
Enter 10 integers: 99
6
4
1
-4
0
2
87
3
5

99 4 -4 2 3
1521 mipsy test_every_kth.s every_kth.s
Enter k: 10
Enter 10 integers: 99
98
97
96
95
94
93
92
91
90

99

You can use make(1) to build the provided C code:

make every_kth

You can use 1521 mipsy to run your MIPS code:

1521 mipsy test_every_kth.s every_kth.s

When you think your program is working you can use autotest to run some simple automated tests:

1521 autotest mips_revision_every_kth

Revision Exercise: MIPS no vowels (PS)

You have been given mips_no_vowels.s, which contains a MIPS function mips_no_vowels, that takes 2 arguments: a source array and a destination array and returns the value 42.

Add code to mips_no_vowels so that it is equivalent to this C function:

// Copy all characters from the src array to the dest array
// except for upper case or lower case vowels
// src and dest are both '\0' terminated strings
// return the number of vowels found
int no_vowels(char *src, char *dest) {
    int j = 0;
    int n = 0;  
    char ch;
    for (int i = 0; src[i] != '\0';  i++) {
        ch = src[i];
        if (is_vowel(ch)) {
            n++;
        } else {
            dest[j] = ch;
            j++;
        }
    }
    dest[j] = '\0';
    return n;
}

The function mips_no_vowels copies all characters from the src array to the dest array except for upper or lower case vowels ie. ('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U')

Note that src and dest are both '\0' terminated strings

The function must return the number of vowels found.

It is assumed that destination array is large enough to store the resulting string.

You must also implement the following helper function in MIPS too:

// returns 1 if the ascii character in c represents 
// an upper or lower case vowel
// returns 0 otherwise
int is_vowel(char c) {
    // Trick to convert case from Text Representation Lectures
    char lower_case_c = c | 0x20;
    if (lower_case_c == 'a' ||
        lower_case_c == 'e' ||
        lower_case_c == 'i' ||
        lower_case_c == 'o' ||
        lower_case_c == 'u') {
            return 1;
    }
    return 0;
}

This function is_vowel takes a char and if it represents an upper case or lower case vowel in ASCII it returns 1 and returns 0 otherwise.

When both funcitons are implemented correctly you can run and test your code as follows:

1521 mipsy test_mips_no_vowels.s mips_no_vowels.s
Enter a string: potato
3 vowels removed
ptt
1521 mipsy test_mips_no_vowels.s mips_no_vowels.s
Enter a string: We love MIPS!
4 vowels removed
W lv MPS!
1521 mipsy test_mips_no_vowels.s mips_no_vowels.s
Enter a string: xyz
0 vowels removed
xyz

1521 mipsy test_mips_no_vowels.s mips_no_vowels.s
Enter a string: AEIOUaeiou
10 vowels removed

You can use make(1) to build the provided C code:
make mips_no_vowels

You can use 1521 mipsy to run your MIPS code:

1521 mipsy test_mips_no_vowels.s mips_no_vowels.s

When you think your program is working you can use autotest to run some simple automated tests:

1521 autotest mips_revision_no_vowels

Revision Exercise: Create An add Instruction (PS)

Your task is to add code to this function in add.c:

// return the MIPS opcode for add $d, $s, $t
uint32_t make_add(uint32_t d, uint32_t s, uint32_t t) {

    return 42; // REPLACE WITH YOUR CODE

}

The function make_add is given the operands for a MIPS add instruction . Add code so that it returns the opcode for that instruction. Reminder the bit pattern for MIPS add instruction is:

Assembler Description C Bit Pattern
add $d, $s, $t add d = s + t 000000ssssstttttddddd00000100000

This is how your code should behave

./add 17 19 3
make_add(17, 19, 3) returned  0x02638820
./add 9 27 12
make_add(9, 27, 12) returned 0x036c4820

Use make(1) to build your code:

make    # or 'make add'
Assumptions/Limitations/Clarifications
  • You may define and call your own functions if you wish.

  • You are not permitted to change the main function you have been given, or to change add' prototype (its return type and argument types).

When you think your program is working you can use autotest to run some simple automated tests:

1521 autotest add

Revision Exercise: Print file contents (PS)

write a c program show_file so that, given a single command line argument containing the path to a file, it reads and prints the contents of that file to standard output. For example:

cat sample.txt
Hello, world!
./show_file sample.txt
Hello, world!
cat three_lines.bin
one
two
three
./show_file three_lines.bin
one
two
three

When you think your program is working you can use autotest to run some simple automated tests:

1521 autotest show_file

Revision Exercise: Print file contents using syscall wrappers (PS)

write a c program show_file_libc so that, given a single command line argument containing the path to a file, it reads and prints the contents of that file to standard output. For example:

cat sample.txt
Hello, world!
./show_file_libc sample.txt
Hello, world!
cat three_lines.bin
one
two
three
./show_file_libc three_lines.bin
one
two
three

When you think your program is working you can use autotest to run some simple automated tests:

1521 autotest show_file_libc

Revision Exercise: Copy File (PS)

Write a C program copy_file so that, given two command line arguments — the first being the path to a source file to be read, and the second being the path to a destination file — it copies the contents of the source file to the destination file.

If the destination file already exists, it should be overwritten.
If the destination file does not exist, it should be created.

For example:

echo "Hello, world!" > sample.txt
cat sample.txt
Hello, world!
./copy_file sample.txt copy.txt
cat copy.txt
Hello, world!

When you think your program is working you can use autotest to run some simple automated tests:

1521 autotest copy_file

Revision Exercise: Remove the first half of a file (PS)

Computer viruses are pieces of code that infect computers and can corrupt data and replicate themselves. An example of this would be a virus that deletes the first half of files. For educational purposes only you'll be writing a c program to do this.

Write a C program halving_virus so that, given one command line argument, a path to a file, it removes the first half of the file. That is to say, if the source file is four bytes long, bytes 2, 3 should be kept in the file and bytes 0, 1 should be removed from the file.
Odd file sizes are to be rounded such that a source file that has 5 bytes should only keep bytes 3, 4, 5.

If the size of the source file is less than or equal to one byte, your program should remove all contents of the file.

For example:

echo "Hello, world!" > sample.txt
cat sample.txt
Hello, world!
./halving_virus sample.txt
Halved sample.txt
cat sample.txt
world!
echo 1234 > five_bytes.txt
cat five_bytes.txt
1234
./halving_virus five_bytes.txt
Halved five_bytes.txt
cat five_bytes.txt
34
echo 123 > four_bytes.txt
cat four_bytes.txt
123
./halving_virus four_bytes.txt
Halved four_bytes.txt
cat four_bytes.txt
3

When you think your program is working you can use autotest to run some simple automated tests:

1521 autotest halving_virus

Revision Exercise: shred (PS)

This is a past exam question from 18s1

You have been given shred.c, which contains a function shred that receives a string in_path and a string out_path as input and does nothing.

}

When paper documents need to be destroyed, we would use a shredder to slice up the paper so that the text is no longer able to be reconstructed. With digital documents, simply removing them (e.g. via Unix's rm command), leaves the contents of the document on the disk where it could be recovered by scanning the raw disk (i.e. bypassing the file system). To render digital documents less likely to be available for discovery, it would be better to write random bytes over the top of the file containing the data.

In this question, you must write a function which takes the path of an input file and writes 64-byte blocks of "random" bytes for at least all of the bytes in the input file. Ideally, we would write these blocks over the top of the original file. However, we don't want to destroy each input file the first time you test your program, so we write the random blocks onto an output file with the suffix .out.

In this question, you must implement the void my_shred(char *in_path, char *out_path); function, which takes two parameters:

  • in_path a file path you should open for reading
  • out_path a file path you should open for writing
and does the following:

    for the whole in_path file {
       read a 64-byte block into a buffer
       // may have < 64 bytes in buffer at end of file
       overwrite the buffer with random characters from 'A'..'Z'
       write the buffer to out_path
    }
    

Hint: to create random byte from 'A'..'Z' use (rand () % 26) + 'A'

Note you do not need to error check. You can assume that the input files exist and can be opened and that you can open files for writing.

You should try and get your function to work with libc sys call wrappers first and then also try to write another version with stdio library functions too.

For example...

unzip shred_examples.zip
xxd shred_examples/f0
00000000: 4865 6c6c 6f20 6576 6572 796f 6e65 0a    Hello everyone.
dcc -o shred shred.c shred_main.c
./shred shred_examples/f0
xxd shred_examples/f0.out
00000000: 444d 574a 4358 4554 415a 5257 4b4c 504d  DMWJCXETAZRWKLPM
00000010: 465a 4c57 4b43 4858 4946 4657 5046 4653  FZLWKCHXIFFWPFFS
00000020: 5443 4459 4249 5244 4a4a 4154 5752 4945  TCDYBIRDJJATWRIE
00000030: 5154 4144 5648 4145 4f48 4345 4f4a 5949  QTADVHAEOHCEOJYI
00000040: 0a                                       .
  • Note that you will get different random characters if you use different pathnames for the same file eg. ./shred shred_examples/f0 will give different random output to ./shred f0

  • No error checking is required.

  • Your function can assume each argument is always the pathname of an existing ordinary file.

  • Assume that the files could contain any byte.

  • Your program can not assume the file will fit in memory.

  • Your program can not assume the file will fit in an array.

  • Your solution must be in C only.

  • No error checking is necessary or required.

  • You are not permitted to run external programs. You are not permitted to use system, popen, posix_spawn, fork or exec.

When you think your program is working you can use autotest to run some simple automated tests:

1521 autotest shred

Revision Exercise: Create Borts File (PS-CR)

Write a C program, create_borts_file, which takes 3 arguments:

  1. a filename,
  2. the beginning of a range of integers, and
  3. the end of a range of integers;

and which creates a file of this name containing the borts in the range provided. You can assume that the range will always be strictly ascending.

A bort is an unsigned two-byte big-endian integer (bort is a contraction of big-endian short).

The possible bort values are 0..65535, so each bort can be represented as a uint16_t, or unsigned short. As borts are big-endian, they need to be written to the output file with their most significant byte first, followed by the least significant byte.

This means for a number such as 0x1234, the first byte to be written should be 0x12, as it is the most significant byte, followed by 0x34, the least-significant.

As a borts file is not a text file, we cannot use cat to inspect its contents.

The linux utilities, xxd and od are good ways to print a binary file, as hexadecimal and octal respectively. Note that the zero byte is printed first for each number here, as it is the most significant byte in each bort.

For example:

dcc -o create_borts_file create_borts_file.c
./create_borts_file fortytwo.bort 40 42
ls -l fortytwo.bort
-rw-r--r-- 1 andrewt andrewt 6 Nov  1 15:21 fortytwo.bort
xxd fortytwo.bort 
00000000: 0028 0029 002a                           .(.).*
od fortytwo.bort 
0000000 024000 024400 025000
0000006
Another example, creating a file containing the five biggest borts possible:
./create_borts_file biggest.bort 65530 65535
ls -l biggest.bort
-rw-r--r-- 1 andrewt andrewt 12 Nov  1 15:26 biggest.bort
./print_bytes biggest.bort
xxd biggest.bort
00000000: fffa fffb fffc fffd fffe ffff            ............
We can give od command-line options to decode borts, for example:
od --endian=big -t u2 -A d -w2  biggest.bort
0000000 65530
0000002 65531
0000004 65532
0000006 65533
0000008 65534
0000010 65535
0000012

Your program should print a suitable error message if given the wrong number of arguments, or if the file can not be created.

When you think your program is working you can use autotest to run some simple automated tests:

1521 autotest create_borts_file