Assignment 5, due Sept. 30

Solutions

Part of the homework for 22C:112, Fall 2013
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: Consider a C program containing the statement: fputc( 'x', stdout ); to send output to an archaic character sequential printer attached to a Linux machine and configured as standard output of the running program. Eventually, the execution of this statement causes the 'x' character to be printed on the page, advancing the print head one step, but there are several steps involved in this:
    1. fputc( 'x', stdout ); is called.
    2. write() is called.
    3. 'x' is put in the output data register of the printer port.
    4. 'x' is copied from the user's buffer to a system buffer.
    5. 'x' is added to the user's output buffer.
    6. 'x' is printed on the output page.

    A problem In what order do the above steps happen? (The first and final steps above are given in the correct order.) (0.5 points)

    1. fputc( 'x', stdout ); is called.
    2. 'x' is added to the user's output buffer.
    3. write() is called.
    4. 'x' is copied from the user's buffer to a system buffer.
    5. 'x' is put in the output data register of the printer port.
    6. 'x' is printed on the output page.

  2. Background: Consider the old IBM PC serial port defined in the notes for Sept. 20. Assuming that the baud rate has already been set, the only registers that matter are COM1DATA and COM1LSR. Assume the following definitions:
    #define COM1DATA 0x3F8
    #define COM1LSR  0x3FD
    #define RxRD     (1 << 0)
    #define RxOE     (1 << 1) /* overrun error */
    #define RxPE     (1 << 2) /* parity error */
    #define RxFE     (1 << 3) /* framing error */
    #define RxBR     (1 << 4) /* break */
    #define TxDE     (1 << 5)
    #define TxID     (1 << 6)
    

    Overrun error indicates that new data arrived before the previous data was read. The old data is lost, but the received data in COM1DATA is valid. In the case of parity and framing errors, the data in COM1DATA is not valid. parity and framing errors, the data in COM1DATA is not valid. The break condition does not itself represent an error, but is an auxilary indicator indicating that an overrun error was very long.

    As in the notes, assume that you can use inp(r) to read data from device register r and outp(r,d) to output the data d to device registerr.

    a) Write getcom1(), a routine to directly implement a read from com1, ignoring the possibility of error. Keep it simple! Minimize your code. Few or no comments are needed. (0.5 points)

    char getcom1() {
            while ((in(COM1LSR) & RxRD) != 1) /* poll */;
            return in(COM1DATA);
    }
    

    b) Revise your solution to part a) so that, in the event that the COM1DATA is invalid because of an error, the invalid value is ignored. That is, in the event of such an error, getcom1() should continue to wait for valid input. (0.5 points)

    char getcom1() {
            int status;
            int data;
    
            do { /* keep trying to read a character until error-free receipt */
    
                    do { /* polling loop */
                            status = in( COM1LSR );
                    } while ((status & RxRD) != 0);
                    data = in( COM1DATA );
    
            } while ((status & (RxPE | RxFE)) == 0);
            /* note!  RxBR seems to require that RxFE be set, so no need to test */
            /* note!  RxOE does not mean the data is invalid, so should not test */
    
            return data;
    }
    

  3. Background: Consider this code based on the classic UNIX implementation of putc():
    typedef struct file {
            int fd;
            int count;
            int size;
            char * buf;
    } FILE;
    
    #define putc(ch,f) (                                    \
            buf[ f->count++ ] = ch,                         \
            (f->count >= f->size)?(                         \
                    write( f->fd, f->buf, f->size ),        \
                    f->count = 0                            \
            ):(                                             \
                    0                                       \
            )                                               \
    )
    
    #define putchar(ch) (putc( ch, stdout ))
    
    void fputc( char ch, FILE * f ) {
            putc( ch, f );
    }
    

    In C, source lines ending with backslash (\) are concatenated to the following line. This allows a #define to span multiple lines. In C, expressons of the form (a?b:c) are conditional; if In C, expressons of the form a is true, the value is b, otherwise, the value is c. In C, x->y refers to the field named y of the structure pointed to by the pointer x.

    Note that there were several small typos in the C define above; these would have interfered with compiling this code, but should not have interfered with solving this homework problem. They have been corrected here.

    a) When a file f is opened for output, what is the correct initial value for f->count? (0.5 points).

    zero, so that the buffer is filled fro the beginning.

    b) Why not use a constant size for the buffer? That is, why would it be useful to wait until the file is opened to decide what size buffer to allocate? (0.5 points).

    The optimum buffer size might depend on the device you are using.

    Examples were not required, but it might be appropriate to use a really small buffer for a really slow device, while to get the most out of a very fast device, it might be best to use a large buffer.

    c) Give an appropriate implementation of fflush() for this implementation of files. (Ignore the special behavior defined for a null parameter.)

    void fflush( FILE * f ) {
            if (f->count != 0) {
                    write( f->fd, f->buf, f->count );
                    f->count = 0;
            }
    }