In the previous part we’ve discussed the purpose of driver/buffer chips, how chip selection is implemented through the signals on the address bus, how interrupts work and how the stack works. In this part we’ll begin by taking a closer look at the interrupt mechanism, while introducing some interesting hacks that allow system and software designers to extend the basic features of the 8080 without changing the 8080 itself.
The 8080 in its basic configuration supports up to 8 different interrupt routines. This is implemented through the INT signal and the interrupting chip sending the interrupt vector through the single byte RST instruction on the data bus. Here’s a diagram of an implementation of the 8214 for multi interrupt vector support:
The 8212 is used to buffer the 8214’s output. Notice all but 3 of the 8212’s input data pins are hardwired to Vcc, meaning they will always output 1 when ever the 8212 is selected. Speaking of selection, the 8212 will be activated when the INTA signal on the control bus goes low. The 8228 system controller will drive the control bus’ INTA signal low based on a combination of the data pins when the 8080 is in output mode (DBIN is low): D0, D3 and D5 are high while the rest are low. The different data pins are decoded using this table:
This means the INTA signal on the control bus will go be generated during a fetching cycle (the RST instruction is about to be fetched from the data bus), while an INTA is sent on the data bus (D0 is high), and that the operation of the current machine cycle (several instruction cycles combined are considered a machine cycle) is a read cycle in general (again, instruction fetch). The INTA signal will activate the 8212 just in time (T2) for it to transmit the buffered RST instruction during the T3 cycle as described in the previous post.
Also, notice the INT signal has its polarity reversed when it comes out of the 8212 so it’ll match the 8080’s specification. Another interesting thing to notice when examining the 8214 logic diagram on page 5-153 in the manual is that the INT signal, when activated, will remain active for one clock cycle only (it is connected to the “set” pin of a D-flipflop). The 8212 latches this INT signal so that it continuously transmits to the 8080.
As mentioned, the 8214 chips can be cascaded in order to support more interrupt vectors. But since there’s still a single interrupt pin on the 8080, and since the RST instruction still is a one byte instruction with 3 bits to indicate the interrupt vector, how can we tell the 8080 where the interrupt handler’s code is located? The intuitive solution would involve removing the hard wiring from the D0-D2 pins on the 8212 and connecting them to multiplexing circuitry that’ll be connected to multiple 8214s. The problem is that if D0-D2 will not be hardwired to 1, an RST instruction won’t be transmitted on the data bus, since an RST instructions opcode is constructed in the following way:
meaning that we only have 3 bits to work with and that’s it. To work this problem out, we need a little outside help that comes in the form of the RST 7 instruction feature of the 8228 system controller. Just to make things clear, and RST 7 instruction is a regular RST instruction with the reset vector of 0x38. An RST 7 opcoce is 0xFF, or an instruction byte of all binary 1’s.
To activate this feature, a system designer needs to connect the INTA output from the 8228 to a voltage source through a resistor as specified on page 5-8, and the 8228 will be ready to send an RST 7 instruction to the 8080 through the data bus as soon as the 8080 acknowledges and interrupt. Remember that the 8228 (if used in the system) sits between the 8080 and the data bus, so it can intercept signals before they go from the 8080 to the other chips through the data bus, and vise-versa.
After the RST 7 instruction is executed, the 8080 will begin executing code from address 0x38. Now it’s the programmer’s job to write the code that “talks” with the 8214 array in order to read the extended interrupt vector, and figure out the address for the real interrupt handler. But again, how can the 8214 array point to more than 8 different interrupts? Since the 8214 was relived from the responsibility of transmitting the RST instruction to the 8080 (which is now done by the 8228), outputs from multiple 8214s can now be multiplexed into the 8212 input pins. This method can support up to 40 different interrupt vectors. Here’s an example of an array that can support up to 16 interrupts:
Notice that in this implementation, input pins D0-D2 are on the 8212 are hard wired to 0, meaning that watever vector is sent down the data bus is already a multiple of 8 (if you aren’t sure why, check out how multiples of 8 look like in binary), so the programmer skips multiplying the interrupt vector by 8. In this implementation the top 8214 is the higher priority controller, because the ENLG pin output of the top 8214 goes into the bottom chip’s ETLG input. This means that the bottom chip’s interrupts will be serviced only when there are no interrupts waiting to be serviced in the top 8214.
Since this interrupt controller array isn’t autonomous anymore (it doesn’t send the interrupt vector through the RST instruction), a programmer must somehow be able to read the interrupt vector from the array. This can be done by activating the array (chip selection) and then reading the array’s output on the data bus.
In the previous post we’ve covered memory chip selection through the address bus, and general chip selection can work in a similar way. There are many ways to implement chip selection through the address and control bus. The only thing a system designer needs to be careful about is collisions. The 8080 has different memory read and write instructions, and their execution causes the 8228 to send the according MEMR or MEMW signals down the control bus. This means that if a system designer wants to implement interrupt controller array using a 8205 that is connected to the address bus, he must make sure that the address the programmer uses to read the data from the array isn’t also a functioning memory address. In case such a collision happens, both the interrupt array and the memory array will try to send their data down the data bus, and the result will be undefined.
Since the communication with the interrupt array is not memory communication, it is considered as chip I/O (Input/Output). Chip selection using the address bus is called Memory Mapped I/O (MMIO). In a system that uses MMIO, if pin A15 is used to activate the interrupt array, a programmer can read the interrupt vector by using a single MOV instruction targeting address 0x8000. While this method allows for very simple and intuitive programming, it “eats” a chunk of the address space, meaning that the amount of addressable memory is decreased.
The 8080 features an alternative to MMIO in the form of isolated I/O (or just I/O). The I/O address space is made of “ports”, and reading/writing from/to them requires special instructions. The main advantage of using isolated I/O is that it doesn’t diminish the amount of addressable memory.
Physically, isolated I/O is implemented through the IOR and IOW signal outputs on the 8228 chip:
and as far as the programmer is concerend, this is how the address spaces look like using the different methods:
In the above example A15 pin is used for general I/O chip selection, meaning that the programmer is left with only 32KiB of addressable memory (which might be fine for some systems).
To support the isolated I/O model, specific instructions were implemented: IN and OUT, which are synonymous to read and write respectively. A port number must be specified with the IN/OUT instruction and the content of the data bus is read to the accumulator or written from the accumulator to the data bus respectively. The reason only 256 ports are addressable through isolated I/O probably has something to do with instruction length (this way the instruction size can be reduced to two bytes, one for the instruction and one for the port address), and the thought that 256 different ports seem like a large amount of chips that can be selected with a single instruction.
Lets get back to the interrupt array example.
When an interrupt is accepted by the 8080, the 8228 sends and RST 7 instruction back to the 8080, which begins executing the single interrupt handler. The interrupt handler’s code will probably look like this:
First thing is to save processor status (as discussed in the previous post). Then, the interrupt vector is read from the interrupt array to the accumulator register using the IN instruction. now a-16 bit address is set using registers L and H, representing low and high address bits respectively. Notice the base address of the interrupt handling routine array loaded into register H, while the specific interrupt routine’s offset from the beginning of the array is loaded to the L register. The PCHL instruction means load L and H to PC register, which is synonymous to “jump to the address represented by a combination the values in H and L registers”.
This is how I/O works in terms of chip selection and programming. We can now continue to the next subject, peripheral I/O.
For reference, here’s a diagram of a 8080 based system:
- I/O peripheral interface – Lets consider a common input device – a fully decoded keyboard. “Fully decoded” means that the keyboard has its own processor that handles polling for key presses, and outputs bits that represent them (using an ASCII table for example) through a data bus that can be connected to our system. While it sounds surprising that keyboards might be anything but “fully decoded”, remember the Busicom calculator’s firmware from the previous post, the 4004 actually handled polling and decoding key presses!
This keyboard however can’t (more correctly – shouldn’t) just jam a byte (representing a character) down the data bus when ever a key is pressed. For synchronization and control we need a chip that can fulfill the specifications required by the 8080’s interrupt system. The 8255 is the right chip for this job. It is a programmable peripheral interface chip that comes in a 40-pin package. There reason for the large amount of pins is versatility. The 8255 can be implemented in many ways and reprogrammed on the fly by software – but in this post we’ll focus on the 8255’s mode 1 that can handle keyboard and display I/O (the chip’s functions are explained in detail starting from page 5-113 in the manual).
Lets look the the 8255’s pin-out and internals:
We can see that the chip has its own internal data bus connecting 3 different ports (A, B and C that is split to lower and upper 4 bits). The internal is connected to the host’s data bus through a data bus buffer. The chip also features control logic circuitry that connects to the host’s control, data and address bus. In theory, each port can handle I/O to a single device, and port selection is implemented by using 2 bits from the address bus (A0 and A1).
But before the 8080 can select a specific port, it needs to be able to select the 8255 for communication first. Here’s an example of how 8255 chip selection can be implemented through MMI/O without additional 8205 decoders:
Address pins A0-A1 are used for 8225 port selection, and A2-A14 are used for direct chip selection (each address pin is connected to an individual chip select pin on the 8225). This way 13 different 8225 can be selected, while the memory address space gets cut in half, but this might be a very good solution for systems with a small amount of memory while also reducing the cost of the system (because extra 8205 chips aren’t needed for chip select decoding).
An isolated I/O chip select similar to the above would limit the possible number of addressable chips to 6 (since an I/O port is 8 bits in size, 2 of them used for 8225 port selection). Again, this is with direct chip select using the address lines – adding 8205 decoders can allow much more chips to be selected using I/O ports.
Now that we have chip selection figured, lets see how we can control the chip to set it up for proper communication with our keyboard.
When the system boots, or when the 8255 is reset, it will automatically go into mode 0. For our implementation, we need to put the chip in mode 1. This is done by sending a control word to the 8255 down the data bus. Since the 8255 has 3 ports, when the A0 and A1 pins are both 1, it means the word on the data bus is addressed to the 8255 itself (and not to a peripheral connected to port A,B or C) and it is a control word. Here’s the chips full control table:
Remember that all the signals with the line above them are “active low”. The control word on the data bus programs the 8255’s mode for each group as explained by this diagram:
There are many possible combinations that can be programmed mixing up between groups and modes, and they are described starting from page 5-116. As mentioned, we’ll be focusing on mode 1, which is strobe I/O mode. With strobe I/O, the peripherals and 8080 communicate indirectly through the 8255. Lets see what happens when a key is pressed on a keyboard connected to port A which acts as input mode 1:
- The keyboard’s internal circuitry decodes the keypress and translates it to 6 bits of data.
- The keyboard uses pin PA6 (pin 6 of port A) to strobe (signal) that there’s keypress data ready on the PA0-5 pins.
- The 8255 latches the data from the keyboard to an internal buffer.
- The 8255 sends an INT signal through PC3 pin (pin 3 of port C). This port should be connected to the 8080 through a 8212 or through a combination of 8212 and a-8214 priority interrupt chip.
- The 8080 eventually executes the interrupt handling routine, which will select the interrupting 8255, and read the latched decoded keypress on the data bus, and save it memory.
- An ACK signal is sent from the 8080 to the keyboard to notify its circuitry that the decoded key was read, meaning that the keyboard is now ready do decode the next keypress.
Here’s a diagram of the connection:
You can see that we have another peripheral device connected to the 8255 – a Burroughs self-scan display which is a small system by itself. You can watch a video on how it looks and works here (I/O explanation starts at the 5:45 mark).
So lets see how a cycle of reading a character from the keyboard and printing it on the display works:
- Keyboard input as described above. In the end a byte representing a character will be saved to RAM in a known address, lets say it’s saved to a “ready to print buffer”. A variable that represents the number of characters sitting in the “ready to print” buffer is incremented by this routine.
- before the keyboard input interrupt routine finishes, it reads the ACK signal from the display. If the ACK signal is high, it means the display is ready to print a character, so a display print routine will be called before the keyboard input interrupt handler finishes. If the ACK signal from the display is low, the keyboard input interrupt handler finishes.
- The display print routine translates the bytes stored in ram to data the display “understands”, and it to the display by writing the data to the 8225 chip. The 8225 latches the data to pins PB0-8 and then sends a signal down the DATA READY pin. When the display receives the DATA READY signal, it’ll read the byte from pins PB0-8, and send it to its internal circuitry. Once the data is sent to the display, the display print routine finishes.
- When the display is done printing the character, it’ll send an ACK signal to the 8255, which will trigger another (different) interrupt.
- The interrupt handler that deals with the display’s ACK will check if there are any more charecters waiting to be printed from the “ready to print” buffer (that’s located in RAM). If there are charecters to be printed, the routine will call the display print routine. Before it finishes, the interrupt handler will decrement the number of charecters in the “ready to print” buffer (because one just got printed).
A ring buffer can be implemented in software to handle this scenario. You can see that the software programmer needs to be very accurate in order for this system work properly. The program described above is the “software driver” that drives the hardware. Without the software driver, this sophisticated system will be useless.
Now that we have a good understanding of how memory and I/O reads/writes work, we can discuss one last feature supported by the 8080 – DMA (Direct Memory Access).
With DMA, peripheral I/O chips can be designed to write directly to memory. While the 8255 doesn’t support this feature, other chips can be designed to support it. The way DMA is implemented is simple: A device with DMA capabilities will prepare the target address (and data in case of a memory write), and when it is ready, it will drive the signal on the 8080’s HOLD pin high. The 8080 will sample the HOLD pin during T2, and if it accepts the request (depends on conditions described on page 2-13), it’ll drive the HLDA pin high, signalling to the requesting device that the CPU is now suspended.
The device then executes the memory read/write, and drives the HOLD pin low once it’s done. The 8080 then resumes normal operation. Simple.
DMA saves a considerable amount of time. Instead of interrupting the CPU, causing it to execute routines to read the data from the device to internal registers, and them write the data from the registers to the memory – a device can get everything read, and execute the read/write to memory in just one cycle! Of course the device won’t just decide on where and what to write to memory, that’s where the software driver that is in charge of the DMA operation comes in.
DMA is used extensively in modern computers, and the chip arrays that handle communications with the device and DMA operations are called controllers (USB controller, SATA controller, etc..), or adapters (Display adapters, etc..).
* * * * *
This post concludes the introduction series. The purpose here was to answer the simple question “How computers work” without leaving anything in the dark or refer to something as “magic” or “it just works”. This series also set the basic knowledge-base so I won’t have to repeat things, or have to include a low-level intro in future posts.
There are many more interesting subjects to discuss, and from this point it would be impossible to set them up in a linear fashion like these series was set. That’s why from here I’ll make post on different subjects, going as low-level as need to understand exactly how things work.
I’ll also be accepting post requests on any hardware or software subjects. So if you have any, write them down in the comments, or send them to the email in the side-panel.
Hope you found this series informative and enjoyable to read. Feel free to leave comments, and ask questions.