This page focuses primarily character IO. Once you understand it, you'll should have no problem understanding disk IO from the description of the block controller component in the SMOK documentation.
There are two parts to I/O controllers, the I and the O. Both sides are a little different than what we're accustomed to. On the input side, there is no instruction that can cause a character to be typed - the user types one whenever s/he feels like. So, input is asynchronous with instruction execution; it happens when it happens. On the output side, output is slow, requiring many cycles to complete. Because of this, output as viewed by the software is demarcated by two events. The first is the start of an output operation. That is caused by the software telling the controller to perform an output, using a controller-specific command. The second event is output completion. This is signalled by the controller raising one of the bits of its output to indicate that it is done. While an output operation is going on, it is a software error to tell the controller to start another one (meaning the result is undefined).
To accomplish these tasks, the controller has a command set (more or less a set of instructions that it knows how to execute) that it accepts as its input. When the software wants the controller to start an output, it sends it a command to the character controller (by writing some data to it) indicating what it wants (e.g., "write a character" and the character).
The controller also has an output. The output indicates the current state of the controller, e.g., "ready to accept a new output command" and/or "a keyboard input character is available" (or neither).
Given this, it is the software's responsibility to use the controller correctly. (Note that the software using it correctly is the responsibility of the software implementer, not the ISA implementer. I'm explaining it here just to give you some context about how what you're implementing fits into the larger picture.) For output, the software must do this:
When the software wants to read a character, it must do this:
The details of how this work depend on the controller. We'll be using SMOK's Character Controller Device. Its documentation tell you what the controller's command set is, and what the controllers output bits mean.
We could create new opcodes for this: RFCC
(Read From Character
Controller) and WTCC
(Write To Character Controller). That
would work, but the downside is that we'd need a different pair of opcodes
for each controller attached to our system. Besides being cumbersome, that
would make it impossible to attach controllers that were designed after the
processor was built (since the processor wouldn't have the needed opcodes).
Instead of using new opcodes, we note that sending commands to the
controller is basically a store
operation: "Take the
contents of this register and send it to..." Similarly, reading
the controller's output is basically a load
: "Get ... and
put it in this register."
So, instead of special opcodes, we'll just use sw
and
lw
.
How can that work? How does the machine know we want to talk with
the character controller, rather than memory, on a sw
or
lw
? The answer is that we use a special address to indicate
that we really mean the controller, not memory. In particular, Cebollita uses
the address 0x40000000. A sw
to that address should take the
register contents and store it to the character controller (and not anywhere
in memory), and a lw
from that address puts the character
controller's output into a register.
To get this to work is relatively simple: you just need to add enough control
to notice that the instruction is a lw
or sw
and
that the address is 0x40000000. Under those conditions, you route the data
to/from the character controller, and (even if it's a sw
)
you do NOT cause the memory interface to do a write to ram.
(Note: The Cebollita ISA defines the meaning of loading or storing to just some of the addresses in the range 0x400000000-0x40000003F. Loads or stores to other addresses have undefined result.)
To test the character device, you can use the program
...\cebollita\apps\tests\chartest.c
.
It calls printString()
and readString()
to
read/write from/to the terminal.
Compile and assemble it as always. After that, you'll need to link it with
...\cebollita\apps\iolibs\iolib.c (well, with the .o
for that file). Make sure to put the prologue first in the command
that links, otherwise the initial value of the PC in your SMOK model
(which is almost certainly 0) will not be pointing at the first instruction
that should be executed.
To test the disk controller, you can use
...\cebollita\apps\tests\blocktest.c
. It needs to
be linked with iolib.c (I mean, .o) as well.
Additionally, to run it you need to connect a "virtual disk"
to the block controller in the memory map container
(right-click / Properties).
That's simply a file
that the block controller will read/write. Note that the file will be
altered by writes, so don't use anything valuable. (I copy the file
...\cebollita\apps\blocktest_diskfile_master to blocktest_diskfile, and
connect the latter to the block controller, but you can use any file
you want (that is at least something like 3KB in size).)
If you want to compare what your SMOK machine is doing by running the same application in the Cebollita simulator, a few complications arise. They come up because, in the SMOK world, you're talking to raw hardware. When you press a key while speaking to raw hardware, all that happens is that the key's value is delivered to the hardware - no character appears on the screen, or anything else, until some software has read the character and done something with it. The iolib.c file assumes that this is what is going on -- raw hardware -- as that's pretty much what happens in SMOK.
Unfortunately, when running the Cebollita simulator, Java and/or Cygwin and/or Windows are between your keyboard and the simulator, and they one of them does "helpful" things like put the character on the screen, etc. That gets in the way of what iolib.c is trying to do, so it has to be turned off.
To turn it off, you must have things set up correctly:
stty -echo -icanon min 1
stty echo icanon
It's easiest to automate this last step.
The shell script cebrun.sh
does that for you.
Save it to the directory
containing your test application, and make sure it is executable by you
(chmod u+x cebrun.sh
).
To invoke it, type ./cebrun.sh
app-name
in the cygwin shell.
The script will try to run Cebollita on app-name.
If the simulator terminates abnormally, you might find that your Cygwin shell seems to have died - what you type doesn't show up. Everything's fine, you just need to type the second command from step 3 above.