Memory-Mapped Input/Output
MARS Has a "Keyboard and Display MMIO Simulator" Tool that We Can Use as an Example
We Will Concentrate on the Display Part. The Keyboard Part is Similar
Think of the Display as an Old-Fashioned Printer That Prints One Character at a Time
- Run MARS and pick "Keyboard and Display MMIO Simulator" on the Tools menu.
- This gives you a window that is divided into several panes;
- The display area. Think of it as a piece of paper that the printer prints on.
- Some controls for the simulation of the display
- The keyboard area. Think of it as another piece of paper on which typed characters are echoed.
- The display controls are mostly to do with the simulation of the time it takes to print a character.
- There are two "registers" that are used to operate the printer:
a control register and a data register.
- I put "registers" in quotes because I don't want you to confuse them with the CPU registers.
Here is a diagram of these registers that shows the (comparatively few) bits that get used.
- These registers have memory addresses; they are shown in hex in the diagram.
- (This is the origin of the term "memory-mapped").
- So the program will use loads and stores to read and write these registers.
- That is how the program can control the printer.
- Let's start with the data register.
- Only the low-order byte (green) is used.
- When a character code is written there, the printer will print that character.
- The problem is that the computer can write characters in the data register faster than the
printer can print them. When this happens, the characters are just ignored by the printer.
- The control register is used to solve this problem.
- Bit 0 (purple) is called the ready bit.
- The ready bit tells the program when it's OK to send a
character to be printed and when it's not OK.
- Changes in the ready bit's value are controlled by the printer, not by the computer.
- It is 0 when the printer is busy, i.e. when you can't print another character.
- It is 1 when the printer is ready, i.e. when you can print another character.
- The program looks at the ready bit and acts accordingly.
- Here is the sequence of operations for correct use of the printer:
- The program waits for the printer to set the ready bit.
- The program writes a character code in the data register using sb (store byte).
- The printer clears the ready bit and begins printing the character.
- The ready bit remains clear while the printer prints the character.
- Once the character is printed, the printer sets the read bit.
- This process repeats until all the characters have been printed.
- The simplest way to do this is with a wait loop.
- Wait loops are wasteful of CPU cycles, but they are the easiest method to talk about.
- It's a three-step process:
# The character we want to print is in $s0.
wl: lw $t0 DISPCTRL # Copy the control register into a CPU register.
andi $t0 $t0 1 # Mask off (turn to zero) all of the bits in $t0 except the ready bit.
# At this point the value in $t0 is zero if the ready bit was clear or nonzero if it was set.
beq $t0 $zero wl # Test $t0 against $zero and branch back to step 1 if they are equal.
# Once you get to the instruction after the branch, you are sure that the ready bit is set.
sb $s0 DISPDATA # Now it is safe to copy the character code into the data register.
Real operating systems don't use wait loops. They use interrupts;
this allows them to get useful work done while they are waiting for the ready bit.
- This is like setting the end-of-cycle alarm on a washing machine.
- Instead of standing by the washer waiting for it to finish (not getting useful work done), you
set the alarm and start vacuuming the floors (useful work getting done).
- When the alarm goes off, you deal with the wet clothes
(handle the interrupt) and go back to vacuuming.
- Bit 1 (blue) in the control register is the interrupt-enable bit.
- Whenever the ready bit turns on (i.e. at the time of the transition from off to on),
the CPU looks at the interrupt-enable bit.
- If it is on, the CPU will spring an interrupt.
- This will cause an interrupt-service routine
(part of the OS) to be executed to handle the interrupt.
- When the interrupt-service routine is finished,
the CPU will go back to what it was doing before the interrupt.
The CPU needs to wait on the keyboard, too.
- The control and data registers for the keyboard look just like the ones for the display,
but they work a little differently.
- The ready bit normally stays off.
- When the user types a key, the keyboard places the code of
the character that was typed into the data register
and then turns on the ready bit.
- When the program sees that the ready bit is on it can do a load byte to fetch the character code.
- When the data register is read by the load byte,
the ready bit goes back off until another character is typed.
- If the interrupt-enable bit is on when the ready bit goes on,
the CPU will spring an interrupt to a different
handler, one that was written to handle keyboard input.