It’s been just less than two weeks since my last Retrochallenge update. I started off strong with posting, but I fizzled fast once I hit the real work. So have I dropped out? Not exactly…
After perusing the piles of Rainbow documentation I have available in a desperate search for how to interact with the Z80 CPU through hardware, I started writing some test code. At first it was simply a naive attempt to trigger the Z80 to execute anything at all. Without any context, I was signaling the Z80 to start running by pushing code 0×21 out through I/O port 0 on the 8088 CPU. This exercise was entirely uninteresting because, after doing so, the 8088 continues on about its own business. It doesn’t automatically stop and wait for its sister CPU to finish up.
What should happen under these circumstances is that the Z80 should trigger an interrupt (0×47 to be exact) on the 8088 to notify its friend that it has completed the requested work. In order to perform this work, I wrote a slightly more elaborate program. Now it would load some Z80 machine code into the lower 64KB of Rainbow RAM (but above the 2KB mark), attempt to configure the Z80′s start address, and request that the Z80 execute the code. The unclear part of the above is how exactly to specify the start address to the Z80. To move things along, I decided to hand-type the MS-DOS BIOS’s execz80 routine.
The above, once again, really didn’t do much at all since the code didn’t handle any Z80 interrupts yet. I did send the code off to the Z80, request some work, and halt the 8088, which did work. What this program showed was that some miscellaneous interrupt was triggering the 8088 to come back to life. I hoped it would be interrupt 0×47 from the Z80, but I wasn’t sure. It was time to start writing an appropriate interrupt handler.
The problem with all these attempts, of course, is that I really don’t know how to work with interrupts. Sure, in the past, I’ve triggered interrupts to access screen or disk I/O, but I’ve never responded to an interrupt. The other problem with writing something “new” of this vintage is the documentation. I have a complete Microsoft C and Macro Assembler documentation set, but nowhere in these binders do they explain exactly how one might author an interrupt handler. Sure, they provide functions to hook your handlers into the OS, but there’s no implementation examples. Online searches are generally useless as well because they either focus on modern 32-bit (or higher) machines or they don’t turn up in results.
I did find a promising introductory paper that gave an example of an interrupt handler responding to system clock ticks. I decided to work with that code, and I wrote a simple handler that would allow my main program to proceed if it were ever triggered. I attached the handler to the Z80 interrupt, and, lo and behold, I received the dreaded Message 16 – Interrupts Off message on the Rainbow, returning me to the boot menu. Anyone familiar with the Rainbow know that this message generally means, “something went wrong, so here’s an error that doesn’t actually help you figure out where the problem is.”
While it was an error, it was something, at least. For a few more days, I toyed with this same program, trying to get it to do something. After continual failure, I decided to see if I could write a simple program to trigger and catch a generic, unused interrupt. This program was met with varying success. It didn’t always trigger Message 16, though, but it didn’t proceed either. Basically, the program would trigger an interrupt, then continually check a variable to see if it changed. The interrupt handler would change that variable when the interrupt was caught, allowing the program to exit. Almost always, it simply sat there locked up.
Yesterday it occurred to me that perhaps the handler wasn’t changing the variable properly. Looking at the code, I made one simple change. I had declared my status variable that allowed my program to exit as a global:
static int returned;
I had been compiling my program as a “small” memory model (don’t you all love x86 chips!). In this circumstance, though, the variable “returned” existed only in the data segment in which my program ran. The interrupt handler, on the other hand, does not run in this same segment. I made one simple change:
static int far returned;
Surprise! My program suddenly worked! x86 segmented memory strikes again!
I stopped yesterday after that small victory. I’ll try again today (hopefully) to catch Z80 interrupts.