Here are the instructions we will implement:
ADD | |
ADDI | |
SUB | |
MULT | |
DIV | |
SLT | |
OR | |
ORI | |
AND | |
XOR | |
SRAV | |
SLLV | |
LW | |
SW | |
LB | |
SB | |
J | |
JAL | |
JR | |
BEQ | |
BNE |
IO is performed a character at a time. A hardware controller buffers a single character to be output and a single character that has been input (typed at the keyboard). (The particular device we use is the SMOK Character Controller. The basic ideas here apply more generally, though.)
Structurally, the controller looks like a register - it has a data input, a write enable input, and an output. However, its input (when write enable is asserted) is a "command," not data to be remembered. The commands have a particular encoding, in which individual bits have special meanings. If bit 15 is 1, it means "start a character write". In that case, bits 0-7 of the input are taken to be an ASCII character to be output. It takes some number of cycles for the device to be ready to accept another character to output. That number is unpredictable.
How do you tell when the device is ready again? It turns on bit 15 of its output. So, the basic protocol to write a sequence of characters is (a) write an input value with bit 15 on and containing an ASCII character, then (b)loop reading the device's output until bit 15 comes on, then (c) repeat. This is called "programmed IO", because the processor is involved in the transfer of each individual character.
Input occurs in a similar way. Bit 14 of the controller's output is set to one when an input character has become available. In that case, the character is given in bits 0-7 of the output. Once the character has been consumed, a one bit should be written to bit 14 of the input, telling the controller that the system is ready for another input character. (If the user manages to type faster than the characters are consumed by the system, the result is unpredictable -- the controller can buffer only a single character.)
Okay, that's how IO happens in the hardware, but how do you control the controller from software? The typical approach is to use "memory mapping". Reads and writes from/to specific memory addresses don't actually operate on memory, they operate on the character controller. In Cebollita, writes (SW's) to address 0x4000000C actually write to the character controller's input. Reads (LB's) from 0x40000000 read the "input character is ready" bit of the controller output, from 0x40000004 the last keyboard character captured by the controller, and from 0x40000008 the "device ready" (to output another character) bit.
How does memory mapping happen? You build it, as part of the datapath/control of the machine. There's no real magic here.