### Question 1 - 16 Points

The code is in q1.c in case you want to test it for yourself.
• Line 1: This shifts two hex digits to the right: 0x0084b3a0.
• Line 2: This shifts two hex digits to the left. Because the number is already 8 hex digits, this will "shift off" the two leftmost digits: 0xb3a03e00.
• Line 3: Since c is the last two hex digits of x, the XOR will clear out the last two digits: 0x84b3a000.
• Line 4: c is the last byte of x: 0x3e. So, the OR will set the first digit to f and do nothing to the last digit: 0x000000fe
• Line 5: The AND will clear the last digit, and keep the first digit the same: 0x00000030.
• Line 6: This shifts the first digit off the left of the char: 0x000000e0.
• Line 7: pcl and pdl point to the same location. pdh is three doubles ahead of pdl, so it is 24 bytes ahead. This line prints 24.
• Line 8: And this line prints 3.

Two points per part.

### Question 2 - 16 Points

The code for the complete program is in q2_real.c. The idea is to keep track of the first file that you see with at least 6 links, as denoted by the st_nlink field of the file's stat() buffer. You keep track of its name in p1 and its inode in number. When you see a second file with the same inode number, you print out the stored filename, and the new file's filename.

 ```#include #include #include #include #include #include void find_6l(char *dir, char **p1, ino_t *number) { DIR *d; struct dirent *de; struct stat buf; char *pn; int length; length = strlen(dir); pn = (char *) malloc(sizeof(char) * (length + 258)); strcpy(pn, dir); d = opendir(dir); if (d == NULL) { perror(dir); exit(1); } for (de = readdir(d); de != NULL; de = readdir(d)) { sprintf(pn+length, "/%s", de->d_name); if (stat(pn, &buf) != 0) { perror(pn); exit(1); } printf("pn: %s\n", pn); if (buf.st_mode & S_IFDIR) { if (strcmp(de->d_name, ".") != 0 && strcmp(de->d_name, "..") != 0) { find_6l(pn, p1, number); } } else { if (buf.st_nlink >= 6) { if (*p1 == NULL) { *p1 = strdup(pn); *number = buf.st_ino; } else if (buf.st_ino == *number) { printf("%s\n%s\n", *p1, pn); exit(0); } } } } closedir(d); free(pn); } int main() { ino_t num; char *firstpn; firstpn = NULL; find_6l(".", &firstpn, &num); return 0; } ```

Now the questions:

• Question A: d holds the pointer to each open directory, so that you can traverse the directory for files. It is initialized with opendir().

• Question B: de holds information about each file in the directory. You fill it in with readdir().

• Question C: buf holds the metadata for each file. You fill it in with the stat() system call.

• Question D: You use three fields of buf:
1. You use st_mode to test whether the file is a directory.
2. You use st_inode to store the inode number of the first file you see with 6+ links, and you also use it to test whether subsequent files with 6+ links are the same as the stored one.
3. You use st_nlinks to test whether a file has 6+ links.

• Question E: The program calls malloc() and free on pn. This is the file name that you construct with the directory and each file name from the readdir() call. You need to call malloc on it at the beginning of the procedure, and free at the end.

• Question F: You have to call strdup() when you store the name of the file in *p1. You need to do this because you reuse the memory in pn when you construct file names.

2 points per part, with the exception of part D, which was worth 6 points.

### Question 3 - 10 Points

Program B makes two system calls, read() and write(), for each character on standard input. If there are a lot of characters on standard input, this will make Program B slow, because system calls are expensive. On the other hand, fread() and fwrite() use buffers to mitigate the overhead of the system calls. For example, fread() will read 8K characters at a time into a buffer, with a single read() call, and it will satisfy the next 8K-1 fread() calls from the buffer. Fwrite() copies each character to a buffer, and only calls write() on the buffer when it is full, or when the program ends.

10 points.

### Question 4 - 20 Points

These are all straightforward from the "Assembler 3" lecture.

In b(), you need to load the pointer, add 12 to it, and dereference it into r0:

```b:
ld [fp+12] -> %r0
mov #12 -> %r1
ld [r0] -> %r0.
ret
```
In f(), you first call "push #16" to allocate g. The first byte of g will be (fp-12). So, you need to calculate fp-12, then add four times i to it, and store 10 at that memory location. Here's an expression tree:

If you process this right to left and bottom up, you don't need to spill r2:

```f:
push #16
mv #4 -> %r0
ld [fp+12] -> %r1
mul %r0, %r1 -> %r0
mov #-12 -> %r1
mov #10 - %r1
st %r1 -> [r0]
ret
```
In d(), we have the following:
• e is [fp+12]
• i is [fp+16]
• e[i] is [ [fp+12] + 4*[fp+16] ]
• & (e[i][3]) is [ [fp+12] + 4*[fp+16] ] + 12
• So, calculate that, and store 5 there.
Here's an expression tree.

Again, if you process this right to left and bottom up, you don't need to spill r2:

```d:
mov #4 -> %r0
ld [fp+16] -> %r1
mul %r0, %r1 -> %r0
ld [fp+12] -> %r1
ld [r0] -> %r0
mov #12 -> %r1
mov #5 -> %r1
st %r1 -> [r0]
ret
```

• 5 points for b().
• 7 points for f().
• 8 points for d().

### Question 5 - 10 Points

First, the following statement forces you to spill r2:

 ```k = c(e(i)+e(j)); ```

So, the first four statements will be:

1. push #8: Allocate the local variables on the stack.
2. st %r2 -> [sp]--: Spill r2.
3. mov #5 -> %r0: This and the next statement push the argument to f(5) onto the stack.
4. st %r0 -> [sp]--.
The last two statements are going to be to move 3 to r0 and return. You need to unspill r2, so you should do that, either just before or just after moving 3 to r0. The two statments previous to that are going to be the ones after the jsr call in "l = f(k)" -- one to pop the argument off the stack, and one to store the return value into l:
```    pop #4
st %r0 -> [fp]
mov #3 -> %r0
ld ++[sp] -> %r2
ret
```

 push #8 1 point st %r2 -> [sp]-- 1.5 points mov #5 -> %r0 1 point st %r0 -> [sp]-- 1 point pop #4 1.5 points %r0 -> [fp] 1 point mov #3 -> %r0 1 point ld ++[sp] -> %r2 1.5 points ret 0.5 points

### Question 6

• The easiest one here is the frame pointer. The question says its value is 0x7ff800.

• In k() the first thing that we do is call "push #8" to allocate n and m. So the stack pointer is (fp-8) = 0x7ff7f8.

• The program counter is at the last instruction of k. k starts at 0x1450, so add 12*4 to it: 0x1480.
• Finally, r0 holds the return value of k(): 60.
As for the rest, this is a matter of starting with the current fp equaling 0x7ff800 and unwinding the stack to put in all the values and labels for h(). Just a note -- we know that the jsr statement in h is at memory location 0x161c. So, the stored pc on the stack should be four bytes after that, at 0x1620.

• r0 = 60: 1 point
• pc = 0x1480: 1 point
• sp = 0x7ff7f8: 1 point
• fp = 0xff800: 1 point
• location of n and m in k correct: 2 point
• values of n and m in k = 38 and 22: 1 point
• location of FP and PC in H are beneath n and m in k: 2 point
• value of FP in H is 16 bytes lower than its location: 1 point
• value of PC in H is 0x1620: 1 point
• location of i and j in k below the pc/fp in h: 2 point
• values of i and j in k are 35 and 20: 1 point
• location of n in h is below i and j or if things are wrong, it's at the value of "fp in h": 1 point
• location of FP and PC in h's caller are beneath n in h: 1 point
• location of i and j in h are 8 bytes below n in h: 1 point
• values of i and j in h are 20 and 30: 1 point
• The empty labels have dashes: 1 point
• The values of n in h, and fp/pc in h's caller are blank: 1 point