H8 Processing
Allow me, please, to appologize for the very poor name of this module.
The purpose of this module is to glue the 8080A emulator into the H8 hardware architecture, providing interrupt vectors when necessary and updating the front panel lamps when RUN and Interrupt Enabled status changes. It's called repeatedly
by the scheduling loop in the event handler to execute one 8080A instruction, and therefore is on the critical execution path from a performance perspective.
Processor Initialization
At system startup and when the H8 is reset, H8 Processing will reset the 8080A and the I/O package, then will rebuild low memory.
Low memory is laid out as follows:
Address Range
|
Length
|
Description
|
000.00A - 003.377A
|
1K bytes
|
PAM/8 ROM
|
004.000A - 023.377A
|
3K bytes
|
unused; init to zero
|
024.000A - 027.377A
|
2K bytes
|
H-17 work space (write protected)
|
030.000A - 037.377A
|
2K bytes
|
HDOS Driver ROM
|
Initialization loads the ROMs from 'CODE' resources, and zeros the unused memory space. The 2K of write-protected H-17 work space is left untouched.
go to top
Interrupt Dispatch
All Interrupts
H8 Processing must check for interrupts before dispatching each instruction, so this mechanism is in the critical execution path and must be fast.
The problem is that there are a lot of interrupt sources: the system clock, single-step hardware and the four 8250 UARTs. That's a lot of polling for interrupts, polling that takes too much time.
Starting with the 4.1 release, polling for interrupts is replaced with an interrupt mask byte. Each bit in the mask byte corresponds with its interrupt level — bit 1 is associated with vector 1, etc. Any object wanting to schedule
an interrupt calls h8SetIntMask with its interrupt mask in order to set the mask bit. (When the condition clears, it can call h8ClrIntMask to clear the bit.)
The mask is checked on each pass. If it is zero, we handle setting the single-step interrupt (see below) then the instruction is dispatched. If the mask is non-zero, we isolate the low-order bit (the highest-priority interrupt) and
then use that value to look up the associated restart instruction to pass to 8080A emulation:
if ( intMask )
{
thisMask = intMask & (-intMask); // isolate low bit
vector = vectors[thisMask];
pState.processorRunning = 1; // if halted, running now
}
The "vectors" array is preloaded with restart instructions during initialization:
vectors[0] = 0 * 8;
vectors[1] = 1 * 8;
vectors[2] = 2 * 8;
vectors[4] = 3 * 8;
vectors[8] = 4 * 8;
vectors[16] = 5 * 8;
vectors[32] = 6 * 8;
vectors[64] = 7 * 8;
intMask = 0;
Clock and single-step interrupts are handled by H8 Processing so those mask flags are cleared here. The I/O Package will clear its own interrupt flags when the interrupt is serviced.
This change from polling for interrupts to setting mask bits resulted in a processor speed increase of about 25% on the author's system.
Single-Step Interrupt
The H8 supports single-step hardware that allows PAM/8 and the dbug package to execute a single user instruction at a time. This hardware is enabled by setting bit 4 (CB.SSI) on the front panel control port (360q). This must be done
with interrupts disabled.
Enable Interrupt (EI) is delayed one instruction. Ordinarily, if you:
EI Enable interrupts
RET Return to user code
...with another interrupt pending, the RET will execute then you will immediately reenter interrupt processing. The single-step hardware is designed to hold back the single-step interrupt for one instruction time following the actual
enabling of interrupts, so that you can run one single instruction following that RET before you reenter interrupt handling.
We accomplish that here by checking the status of the CB.SSI bit after we check the interrupt mask. If CB.SSI has been set and interrupts are enabled, we'll set the interrupt mask bit for vector 2 for the next pass. This ensures
that we'll run precisely one user instruction before interrupting again.
go to top