BigDumbDinosaur wrote:
I've completed a preliminary design for a unit I've dubbed POC V1.2...
Upon sleeping on this a bit, I re-evaluated my goals and decided to simplify the design. I removed the extended addressing function, as it isn't germane to BIOS development.
The main purpose of building V1.2 is to create a machine on which I can concoct a virtual QUART (vQUART, vQUART meaning "virtual quad UART") driver, as well as a BIOS API that is called with software interrupts instead of subroutine calls. As long as I have a reasonable amount of ROM and bank zero RAM available it will all work without the complication of extended addressing. Eliminating extended addressing means less glue logic will be required, which means fewer gates and hence more timing headroom. It also means I can use a smaller SRAM. Fewer gates means fewer chips on the board, which, as you will see, will allow me to use "vision-friendly" DIP packages for all logic devices (alas, the SRAM is only available in SMT, and space constraints require that I use the SOIC version of the MAX238 transceiver). Below is the schematic and PCB for this unit:
Attachment:
File comment: POC V1.2.1 Schematic
poc12.pdf [306.33 KiB]
Downloaded 102 times
Attachment:
File comment: POC V1.2.1 PCB Layout
poc_v1.2_pcb.gif [ 97.58 KiB | Viewed 9725 times ]
The vQUART consists of two NXP 28L92 DUARTs whose driver will make them appear to be a single QUART to applications that access serial I/O (SIO) through the BIOS. Of the four SIO channels, channel A will be the console channel, channel B will be a hardwired link to my Linux software development machine, and channels C and D will be uncommitted. A BIOS call for SIO services will use the
.X register as the zero-based channel index, which is convenient for higher-level applications. As is the case for my DUART drivers, everything will be interrupt-driven and will take full advantage of the 28L92's 16-deep FIFOs.
Speaking of interrupts, the 28L92 can be configured so general purpose output pins 4, 5, 6 and 7 (OP4-7) act as open-drain IRQ signals. OP4 is wired to the RxD channel 1 IRQ, OP5 is wired to the RxD channel 2 IRQ, and OPs 6 and 7 are wired to the channel 1 and channel 2 transmitters, respectively. When the 28L92 interrupts with its main IRQ output, it will also pull one or more of these pins low if reception and/or transmission activity was the cause of the interrupt.
In order to take advantage of this feature, I have the OPs wired to the inputs of a 74ACT540 bus driver, with bits 0-3 wired to the receiver IRQs and bits 4-7 wired to transmitter IRQs—bits 0 and 4 are channel A, bits 1 and 5 are channel B, and so forth. The bus driver is assigned a location in the I/O map (designated SIOQ) which when read, will return a real-time IRQ status. If %00000000 is read, then no receive or transmit IRQs were generated. Otherwise, bits will be set according to what interrupted. These bits are real-time, meaning they stay set until the cause of the IRQ has been cleared.
This arrangement doesn't report the cause of other vQUART IRQs, such as timer underflow, which must be detected by reading the individual DUART interrupt status registers. However, the interrupt service handler's work will greatly reduced, since no polling of each receiver and transmitter will be required. This reduction becomes important when one considers that if all four channels are simultaneously receiving and transmitting at 115.2Kbps, as many as 46,080 IRQs due to SIO activity could occur in one second, assuming an IRQ occurs with each datum received or sent. In practice, the rate won't be quite as severe, due to the buffering provided by the DUARTs' FIFOs. In the best case, 2880 IRQs would occur each second of continuous bi-directional communication, as an interrupt will only come when a receiver FIFO is full or a transmitter FIFO is empty.
Having two 28L92s in the circuit means I will have two precision timers (timers A and B) available, with timer A generating a 100 Hz jiffy IRQ for timekeeping purposes. I've yet to come up with a use for the other timer, but I'm sure I'll think of something. A useful feature of the 28L92 is the timer's output can be directed to the OP3 pin to drive something external—a pullup resistor is required to use this feature. In timer mode, a continuous square wave equal to the timer's underflow rate is produced, where as in counter mode, OP3 is high until the timer underflows, at which time it goes low.
As well as developing the vQUART driver, I will be implementing an interrupt-driven BIOS API. Instead of calling a BIOS API function with JSR, the COP instruction, along with an API function value (API index), will be used to cause a software interrupt, which will be intercepted by the BIOS. There a couple of ways this can be implemented:
- Use COP's signature byte as an index. This method is analogous to the method used in PC hardware to call BIOS services. It has the advantage of not using any of the registers, leaving them free for passing parameters. It has the disadvantage of requiring convoluted bank-twiddling operations in the BIOS' API front-end code to fetch the index, producing relatively slow performance.
- Push the index to the stack. This method also has the advantage of leaving the registers available for parameter passing. Compared to method 1 above, this method has the disadvantage of requiring that the caller push the index, which is an extra step in calling the API, as well as API front end activity to fetch the index and rebalance the stack.
- Pass the index in a register. This method has the advantage of having the least effect on performance, as no stackrobatics or bank twiddling is required to pass and fetch the API index. It has the disadvantage of using a register, which means parameter passing is made more complicated.
In POC V1.1's BIOS, only the registers are used for parameter passing, as no function requires more than three parameters. Obviously, if more than three parameters must be passed there is going to be a problem. Which of the above methods to use is something I will be kicking around as things progress.
In any case, the value of using a software interrupt to call API services is it is bank-agnostic. In a system with more than 64KB, a program can call the BIOS API—which must be in bank zero—without having to do anything special to compensate for not being in bank zero. This will be a very useful feature when I finally get a machine with more than 64KB running.