Assignment 4, due Feb. 9


Part of the homework for CS:3620, Spring 2018
by Douglas W. Jones
THE UNIVERSITY OF IOWA Department of Computer Science

On all assignments, your name must be legible as it appears on your University ID card! Assignments are due at the start of class on the day indicated (usually Friday). Exceptions will be by advance arrangement unless there is what lawyers call "an act of God" (something outside your control). Homework must be turned in on paper, either in class or in the teaching assistant's mailbox. Never push late work under someone's door!

  1. Background: Here is the makefile that was presented in class on Feb. 2, with slightly improved comments and line numbers added so you can refer to specific lines in the file:
    # Makefile                                             1
    #  make mush  -- makes the minimally usable shell      2
    #  make clean -- deletes all files created by make     3
    # primary make target                                  5
    mush:   mush.o splitargv.o                             6
            cc -o mush mush.o splitargv.o                  7
    mush.o: mush.c globals.h                               9
            cc -c mush.c                                  10
    splitargv.o:    splitargv.c globals.h                 12
            cc -c splitargv.c                             13
    # utility make targets                                15
    clean:                                                16
            rm -f *.o mush                                17

    a) If we split off separate source files for getcommand() and launch(), the other two routines that perform the majority of the work in mush, we would have to add 4 more lines to Makefile (excluding any comments or blank lines you might elect to add). Give those 4 lines, and for each line or group of lines, indicate the best place for that addition by indicating the lines it goes between (use the line numbers given above to refer to specific lines). (0.5 points)

    getcommand.o:   getcommand.c globals.h                11a
            cc -c getcommand.c                            11b
    launch.o:       launch.c globals.h                    14a
            cc -c launch.c                                14b

    The line numbers suggested above put the Makefile lines for the three components of Mush in the same order they were in the original code; there is no necessary order of the three components — make only cares that you do not break up the make dependency line (the first line of each pair) and the shell command. Nonetheless, there is something compellingly rational about putting them in the order they were in the origial source code: Get the command, then split it, then launch it.

    b) Which lines in the file given above are shell commands? (The answer should be a set of line numbers.) (0.5 points)

    Lines 7, 10, 13, 17

  2. Background: The notes for Feb. 5 describe the classic IBM PC serial port.

    From the 1930s through the 1970s, the US Navy used numerous devices that ran at 75 baud with 5-bit data, no parity and 2 stop bits after each character.

    A question: From the above brief specification, what values should be set in the device control registers to make this work. Specify the control registers both by their numeric device address and symbolic name. Specify the register contents in hexadecimal.

    If a control register must be used twice, first to do some secondary setting and then to actually run the device, give only the final value you would store in that register. You will have to infer how to set the baud rate, that is a bit tricky. (1 point)

    COM1LCR = 0x04 -- 00xx0100

    COM1BRDL = 0x00 -- 115200/75 = 1536 = 0x600
    COM1BRDH = 0x06

    Note that to set COM1BRDH/L, the high bit of COM1LCR must be set, so COM1LCR=0x80 briefly, but the question asked for the final value of COM1LCR, not some value used briefly.

  3. Background: In the notes for Feb 5, this code is given for sending one byte of output to a classic PC serial port via the putcom() function:
         while ((inp( cp->comstatus ) & TxDE) == 0) /* empty loop body */;
         outp( cp->comdata, c );

    The following alternative code would also work:

         outp( cp->comdata, c );
         while ((inp( cp->comstatus ) & TxDE) == 0) /* empty loop body */;

    a) How do these two bits of code behave differently. Hint: Consider the extent to which they allow input/output activity to overlap with user computation. (0.5 points)

    The first code allows the overlap of computation with output activity, the second does not.

    To see this, it helps to create more context. Let's assume it takes 0.01 seconds to compute the next character to output and also assume that it takes 0.01 seconds for the hardware to output a character. Here's the first bit of code:

         while ((inp( cp->comstatus ) & TxDE) == 0);
         outp( cp->comdata, c );                     /* time = 0 */
         c = compute_next_character_to_output();     /* time = 0 */
         while ((inp( cp->comstatus ) & TxDE) == 0); /* time = 0.01 */;
         outp( cp->comdata, c );                     /* time = 0.01 */

    Here's the second bit:

         outp( cp->comdata, c );                     /* time = 0 */
         while ((inp( cp->comstatus ) & TxDE) == 0); /* time = 0 */
         c = compute_next_character_to_output();     /* time = 0.01 */
         outp( cp->comdata, c );                     /* time = 0.02 */
         while ((inp( cp->comstatus ) & TxDE) == 0);

    In the first version, the computer figured out what to output next while the I/O operation was being done, so the polling loop didn't need to wait. In the second version, the computer first waited for the output to be done and then computed the next character to output.

    b) Suppose that a user simply duplicated this code in each place a character was to be output instead of calling putcom(). What would happen if the user used the original version in some contexts and the alternative version in other places. (0.5 points)

    Some characters would be lost because sequences like this would be executed:
         while ((inp( cp->comstatus ) & TxDE) == 0);
         outp( cp->comdata, c1 );
         outp( cp->comdata, c2 );
         while ((inp( cp->comstatus ) & TxDE) == 0);

    If you put a new character into COM1DATA when the device is not ready, you will overwrite what was already there.