Assignment 4, due Sep 19

Solutions

Part of the homework for CS:2630, Fall 2019
by Douglas W. Jones
THE UNIVERSITY OF IOWA Department of Computer Science

  1. Background: Chapter 5, exercise 4 suggests experimenting with the Hello World program in both C and Hawk vesions to see what some of the control characters do when output as part of a string using the C Library puts and Hawk Monitor PUTS routines.

    Assignment: The backspace, tab, vertical tab, linefeed and carriage return characters are the most important format effectors among the ASCII control characters. What do they do when you output them to the terminal window under C? What do they do when you output them from a Hawk program? Give your answer as a short sentence for each of these telling what it does and if the behavior is different or the same in the Hawk and C environments. (1.0 points, 0.2 per character)

    Suggestion: You can design a single string that replaces "Hello world" and allows you to see the effect of each of these with a single experiment. You do not need to show your work, but if you show us your test strings for C and the Hawk environments, we can diagnose your errors.

    Using this test string for C "BS\bBS HT\tHT VT\vVT LF\nLF CR\rCR\n" I got this output:

    BBS HT	HT VT
                 VT LF
    CR CR
    

    Translating the string to SMAL Hawk format is a bit ugly because you have to change, for example, "BS\bBS" to "BS",BS,"BS", but in the end, you get exactly the same output, so the following conclusion can be drawn for both programming environments:

  2. Background: PC relative addressing can be confusing, so it's worth working a few examples. Here is a small one, 3 consecutive words of Hawk memory:
    000000: 000230F0
    000004: 30F00200
    000008: FA00FFFA
    

    A Problem: Give equivalent Hawk code, with labels such as A, B and C used instead of numeric memory addresses. First, try it by hand, then use the emulator to check your work, and figure out what your errors were. (0.6 points, 0.1 point per instruction or operand error)

    A:      JUMP    C
    B:      BR      D
    C:      JUMP    B
    D:      BR      A
    

    The above is one of many best answers. At the cost of a small penalty, JUMP X can be rewritten JSR R0,X. These assemble to the same machine code, but using JSR implies an expected return, and there is none here. This little bit of code has both forward and backward versions of each instruction.

  3. Background: One of the routines defined in stdlib.h but not mentioned in Chapter 5 is malloc (the C version) or MALLOC (the Hawk version). Either version takes a single parameter, the number of bytes to allocate, and returns a pointer to a memory region big enough to hold that many bytes. Here is a little C program for playing with malloc:
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdint.h>
    int main() {
        int i;
        for (i = 0; i < 100; i++) {
    	void * p = malloc(i);
    	printf( "%lX\n", (intptr_t)p );
        }
        return 0;
    }
    

    This program just prints out the addresses of 100 blocks of memory. The %lX in the format string asks it to print a long integer in hexadecimal, and the (intptr_t) cast on the pointer forces the pointer to be passed to printf as an integer, using an integer representation large enough to hold one pointer.

    a) When you call malloc on the divisional Linux machines, what alignment guarantee does it give you about the pointer it returns? That is, does it return an arbitrary address, an address divisible by 2, divisible by 4, or what? (0.7 points)

    All addresses returned by malloc are divisible by 16. That is, the hexadecimal value ends in 0.

    b) When you call MALLOC on the Hawk, what alignment guarantee does it give you about the pointer it returns? (0.7 points)

    All addresses returned by MALLOC are divisible by 4. That is, the hexadecimal value ends in 0, 4, 8 or C.

    Suggestion: Instead of printing out 100 addresses and then trying to find a pattern, consider oring all of the addresses into a single value, then look at how many zero bits are on the bottom of the result after 100 allocations of various sizes of blocks. In Python, Java, C and C++, the a|b operator will or two values of any integer type; on the Hawk, the OR machine instruction does this for any two 32-bit registers.

    If you use this suggestion, you can show your work by showing the value you got and your interpretation of that value.

    Here is a little C program that illustrates the relevant logic:

    #include 
    #include 
    #include 
    int main() {
        int i;
        intptr_t acc = 0;
        for (i = 0; i < 100; i++) {
            void * p = malloc(i);
            acc = acc | (intptr_t)p;
        }
        printf( "%lX\n", acc );
        return 0;
    }
    

    And here is the output of a sequence of experiments with the above program:

    [dwjones@fastx07 ~]$ a.out
    13D1FF0
    [dwjones@fastx07 ~]$ a.out
    1A73FF0
    [dwjones@fastx07 ~]$ a.out
    1B9DFF0
    [dwjones@fastx07 ~]$ a.out
    4D1FF0
    [dwjones@fastx07 ~]$ a.out
    1107FF0
    

    From the above, you can easily see that the malloc routine must have some degree of nondeterminism. Running the same program many times in a row, it always seems to get different addresses. The only pattern is that the addresses always end in 0. (Running the equivalent program on the Hawk, you'll find that MALLOC is deterministic. Every run will give the same result.)