Intro
to hw1—
Problem
0—warmup for 2.
Just duplicate
a given remgdb session
–due Friday, Sept. 15
Rest
due following Friday, Sept. 22--
Problem
1—UNIX gdb
for hello-world problem
Problem
2--finishing an i/o
library for the SAPC.
Intro
to hw1—
Problem 2--finishing an i/o library for the SAPC.
As a library, it
implements certain useful functions for apps.
Its API is in io_public.h:
void ioinit(void);
int read(int
dev, char *buf, int nchar);
int write(int
dev, char *buf, int nchar);
also
“control”
Read
and write look very much like the same-named UNIX system calls, except
for
“dev” instead of “fd”. The dev parameter is the
device number, for
the io
library. Here dev can
be TTY0 (value 0, standing for COM1) or TTY1 (value 1, standing for
COM2). See tty_public.h
for the following:
#define
TTY0 0
#define
TTY1 1
In class, we looked briefly at the provided test program testio.c, to get the idea that it calls into the library using the API quoted above.
Recall from CS341 that the SAPC device numbers COM1 (value 1) and COM2 (value 2) are in a system of numbers set up in $pcinc/sysapi.h, which is included from $pcinc/stdio.h, and thus is in effect in any program with “#include <stdio.h>” or equivalent.
Note: you should have the environment pcinc defined for you by the ulab module, so if you can run mtip, you can also use $pcinc, for example by doing "cd $pcinc" or "ls $pcinc" or "more $pcinc/stdio.h". To see what it stands for, do "echo $pcinc". It's just a directory in the UNIX filesystem. Similarly $pcex stands for the file path to the SAPC examples directory.
The "system defined" (via stdio.h for SAPC) devices:
#define
CONSOLE 100
#define
KBMON 0
<---keyboard + monitor (we can’t use this
via mtip)
#define
COM1 1
#define
COM2 2
CONSOLE
is the current console, where the Tutor prompt shows up, and where the
user is
typing. It is
possible to run Tutor with
console KBMON, COM1, or COM2, depending on what device the user types
the first
carriage return on at bootup
time. So by using
CONSOLE, for example by fprintf(CONSOLE,
“hi”); or equivalently printf(“hi”),
we can talk to our user regardless of which way they are using our
code.
CONSOLE is the logical console, like stdout
in
UNIX. All output
via CONSOLE is sent to
remote gdb as well as
to the Tutor console.
When we use mtip, the Tutor console line is COM2. So fprintf(CONSOLE, “hi”) and fprintf(COM2, “hi”) both output directly to the user. You can put fprintf(COM1, “hi”) in your program, and it will work, but you won’t see the resulting output on COM1 (unless you remember a trick we used in CS241 mp5.) (If you’re using remote gdb, you’ll be messing around with its protocol connection, to its great detriment.)
Even simpler, we can just put "printf("hi")" to write to the console.
SAPC
Support Library
Uses device numbers: COM1, COM2, CONSOLE,
etc., #define’d
via stdio.h
for SAPC
#include
<stdio.h>
includes special stdio.h
for SAPC, in $pcinc. How does this
work? via
compiler flag “–I
<include-path>” in makefile
In
the SAPC library, also arranged for access in the makefile:
When
we use mtip, the Tutor
console line is COM2. So
fprintf(CONSOLE,
“hi”) and fprintf(COM2,
“hi”) both output directly to the user.
You can put fprintf(COM1, “hi”)
in your program, and it will work, but you
won’t see the resulting output on COM1 (unless you remember a
trick we used in
CS241 mp5.) (If
you’re using remote gdb,
you’ll be messing around with its protocol connection,
to its great detriment.)
In
some cases we want to make sure we’re talking to the user
over a serial
line. We can ask
Tutor what the current
console device is by using the call “sys_get_console_dev()”.
You’ll see this called in echo.c
and hw1’s testio.c.
hw1:
In problem 2, you write code suitable for an i/o
library layered on top of SAPC library.
As a library, it
implements certain useful functions for apps.
Its API is in io_public.h:
void ioinit(void)
int read(int
dev, char *buf, int nchar);
int write(int
dev, char *buf, int nchar);
also
“control”
Read
and write look very much like the same-named UNIX system calls, except
for
“dev” instead of “fd”. The dev parameter is the
device number, for
the io
library. Here dev can
be TTY0 (value 0, standing for COM1) or TTY1 (value 1, standing for COM2
Look
at the API again. This
can be called a
“device-independent” i/o
library because the interface
is generic, not specific to any particular device.
The actual device in use can be determined at
runtime, for example, by asking the user for a device number.
testio.c is the test program calling
through the API. After
making sure it’s talking to the user
over a serial line, device ldev,
it does a write(ldev,
“hi!\n”, 4) as a first
test. As delivered,
output works but
without using interrupts. Your
job is to
reimplement it with
interrupts.
Then
testio.c does a read(ldev,
buf, 10) requesting 10
bytes from the user. It
is implemented
with interrupts already, but you need to switch it from direct use of rbuf and tbuf
to use of Queues
from queue.c.
Also it doesn’t wait properly for all the chars
requested. Note the
busy loop before the read—this
wastes time so you have a chance to input a few characters before read
executes.
The
edits you need to make are all in tty.c,
in ttyread and ttywrite and the
interrupt handler. A
call to read ends
up at ttyread and a
call to write at ttywrite. I’ll let
you trace out how this happens. Look
at io.c and ioconf.c.
This shows how device independent i/o
systems work—the device number specifies which
device-specific function to actually execute.
Here the device-specific functions are ttyread
and ttywrite, and the
bundle of tty-functions
in tty.c is called the tty
driver, or serial port driver.
Dataflow
to be handled by Queues
Input:
chars arrive in the interrupt handler, where
they are each enqueued
into the input queue.
ttyread dequeues
each char, and
puts it in the user buffer.
What’s
the user buffer? That’s
described by the
parameters of ttyread,
or read itself: “char
*buf, int nchars”. The app code (here testio.c)
is requesting a bufferful
of chars by calling read. The
code as provided already delivers some
chars to the user buffer.
Output:
chars arrive from the user, in the user
buffer, i.e. buf and nchars
as arguments.
The
code in ttywrite enqueues them
in the output queue.
Eventually,
your new interrupt code will dequeue
them in the interrupt handler. As
provided, the code just outputs them from ttywrite.
C
Objects, a quick intro/review.
Here
the Queue ADT in C is provided in the queue subdirectory, along with a
unit
test testqueue.c.
queue.h
defines the
Queue API and the Queue type.
You
know how to do objects in Java, what about in C?
Recall the Chip object in CS241.
Also the Cmd
object
for tutor.
Example:
Rectangle objects
typedef struct
rect {
int
x1, y1, x2, y2;
}
Rectangle;
Rectangle
*recp;
recp->x1
= 10;
Watch
out!, there’s
no (validly-allocated) memory pointed to
by recp! This
is a garbage pointer, a pointer
that
points nowhere good. Using
it usually causes
segmentation violations on UNIX and similar exceptions on Windows. But on SAPC, all user
memory is writable, so
it may quietly work to damage some memory.
Normally
under UNIX or Windows, we could malloc
space for the
Rectangle, but we have no malloc
in the SAPC library. Anyway,
malloc is
quite expensive in CPU, and we can easily avoid using it. Just set up whole objects
like this:
Rectangle
rect;
Now
we can put
rect.x1
= 10;
Similarly,
we can set up larger objects that contain Rectangles:
typedef struct
view {
Rectangle
first, last;
} View;
View v;
v.first.x1
= 10;
/* set coord
of v’s first
Rectangle */
v.first = rect;
/* set whole Rectangle by struct
copy in C */
Setting
up whole objects like this may seem strange after using Java a long
time, but
it’s good to really know C too!
There
is more we should cover on this topic.
We have no encapsulation here, so calling this an
“object” is hard to
defend to object-oriented people.
It’s
an object in a practical sense that it brings together related data,
and has an
API that describes what can be done with the data—see queue.h.
Ref:
Notes
on using
C to access hardware registers, Tan, 5.1 to pg. 274.
The
PC uses I/O ports for most I/O devices, not memory-mapped I/O. It is capable of
memory-mapped I/O, however.
First
look at $pcex/echo.c.
You can build
it the same basic way as test.c. Just have $pcex/makefile
as well as echo.c in a
directory and “make
C=echo”. You’ll
get echo.lnx
to download.