ROMulus and Remus; a tale of two EPROMs

A project log for OMNI 4 - a Kaypro 2x Logic Analyzer

A while back I acquired a rare logic-analyzer, whose lone system-diskette needed backing-up. Now this page is all things OMNI 4

ziggurat29ziggurat29 09/15/2017 at 16:028 Comments

A couple days ago Esot.eric dumped the two EPROMS on the board, U9 and U32.  I spent a little time with those images, and Esot.eric suggested I share my findings thus far (there is much more to go, but that will take several days work).

U9 is the character generator ROM, and U34 is the 'boot' ROM.  Why the quotes?  Well, we're pretty sure it is more than just the boot ROM.  For starters, a boot ROM would not need to be 8K -- 2K would be more than sufficient to init the system and load the second stage loader off disk (and indeed earlier Kaypros did have a 2K boot ROM), so the current thinking is that it contains other subroutines that are of use at runtime.

The Kaypro is a CP/M machine, and those familiar with CP/M will know that one of it's quirks is that it requires RAM at the bottom of memory.  Yet 0000h is the reset vector for 8080 and Z80 (for which CP/M was created), so you've got to have ROM there at least during boot up, and that generally implies some sort of bank switching to get it out once you've gotten past boot.  So far, so conventional.  Also interesting is that the ROM does not reference any RAM locations below 8000h (even though this one is only 2000h long), so it seems that Kaypro had plans to possibly expand this ROM-based subroutine approach in future models, and set up their codebase accordingly.

I'll cover the disassembly findings in a subsequent post in more detail (i.e., when I have details to post!), but some interesting things have come out already with a cursory scan:

  1. there are some peculiar register addressings related to video
    The video is mostly controlled by the then-popular MC6845 (or clones).  This chip has 18 registers, and is interfaced through two physical addresses:  the first is the address register selecting which of the 18 internal registers is being operated upon, and the second is the data register that is mapped to the selected register.  So far so straightforward, but I noticed in the code many times accessing of a register 1Fh, which does not exist on the chip.  Blargh!  My mind is boggled.
  2. to add confusion to befuddlement, there is a physical port 1fh that is routinely accessed in the (ostensibly) video driver code.  But this one I think I have more of a handle on:
    The video memory on this unit is composed of two 6116 static RAMs, 2K each.  Hmm, this device has an 80x25 display, which is 2000 characters, so why do we need a total of 4K?  Hypothesis:  attributes.  8 bits for character data, and some more bits for things like high-intensity, inverse video, etc.  Some of Esot.eric's screen shots show that these capabilities are there, so we know we need the extra bits.  Also, the 6845 is essentially a fancy address generator -- you still have to implement circuitry to shift out pixels and blend in sync pulses and whatnot.  So, the video memory is typically being addressed and controlled by the 6845 on a continual basis, and not mapped into the CPU's direct address space.  But something's gotta let us write data into video memory!
    There is a gate array in this machine (actually two).  Since it is a custom chip, it is a black box from the standpoint of analysis, and could be doing anything.  So, my hypothesis in this case is that the port 1fh is decoded by the gate array, and is used to access the video memory to write characters into it.  And probably the attribute bits as well.  Much more work will be done in this area.
  3. As was common on many machine designs, the address decoding may take shortcuts by not decoding some bits and thus creating aliases in the address space.  This was typically done simply to save on parts count ($) but was Hell on 3rd party expandability.  This was done here as well, and A6 does not contribute to the decoding of port addresses, and so there are IO aliases of the first 64 ports into the 40h-7fh space.  Fortunately for the rest of the world, they did decode A7, leaving the upper 128 ports for others, which brings me to my point:  I have noticed in the ROM I/O accesses to ports 81h-87h, which are not standard Kaypro 2x ports.  Hmm.  Ain't that something?  It's so obvious now, but it wasn't until I woke up this morning that I realized that the boot ROM is also custom to the Omni4 (over the Kaypro 2x), and that these ports must be part of the logic analyzer daughterboard.  Also, because there is customization of the boot ROM, it further suggests that Omni either had a close business relationship with Kaypro to have access to the source code to modify, or they were hackers that reverse-engineered it.  My guess is the former, but the latter was not at all uncommon in those days (for fun, search "Michael Geary Adobe Type Manager" to learn about an (in)famous case, and the wrath of the software pusblisher whose code was reverse-engineered).  Anyway, my suspicion is that ports 80h-87h (at least) operate the Omni4 daughterboard, and it is very good indeed that we have the ROM dump, because disassembling the code on the floppy would be insufficient.  Thanks Esot.eric!

Since it is going to take me several days to analyze and comment this ROM dump, for fun in the meantime I decided to code up a little program to transform the character generator dump into something meaningful to the human eye.  Attached herewith in the files section is OMNI4U9.txt containing that result.  I didn't think that much of it at first, but Esot.eric believes that some of the graphics characters are non-standard, and are used by the Omni4 software to depict timing waveforms.  So, once again, thanks for the dumps, because that will make disassembling the logic analyzer program make much more sense knowing what the special characters look like!

Next, I must weave time between real-world responsibilities and code archaeology, so it may be a week or so before I have the ROM disassembly completed.  Or sooner if my burning desires overcome my sense of responsibility -- this has happened before, alas....


Dylan Brophy wrote 09/16/2017 at 06:10 point

How fun!  It is incredible to me that the source files - or any information on the code at all - cannot be found.  I am ever intrigued by information's ability to disappear (Hence my love of Orwell's 1984)

  Are you sure? yes | no

ziggurat29 wrote 09/16/2017 at 14:41 point

CP/M source in various forms is indeed available, but the particular source for this unit is another matter.  CP/M was written in a bespoke language 'PL/M' I think for some portability thing (C was still pretty new in the 70s, though folks have since made PL/M-to-C converters), though licensees were expected to supply some adaption code to their product.  Also, we're hoping to eventually understand the Logic Analyzer part better, and that would be quite a miracle to find the source to.  And lastly (perhaps mostly?) I just like to do disassembly, haha.

In the day, we considered all this stuff to be discardable as things obsolesced very very fast, and even when we didn't, it usually existed in the form of green bar printouts in someone's garage (oftentimes the same person that shut the doors to the company for the last time) and were met with any of several fates.  And it is a shame; there's an interesting (I find) anthropological perspective on how people thought and created in different eras that is lost when they finally go.

  Are you sure? yes | no

Eric Hertz wrote 09/16/2017 at 02:14 point

Great work! Diggin' the explanations as well.
Tbh I hadn't considered that the boot rom was modified any more than to put the word omni4 where typically it'd be kaypro. Interesting to think there's rom routines for the logic analyzer!
Also, I'll have to look more closely at the motherboard, I don't recall any PALs... but if the programmable logic is simple enough, my universal programmer might be able to read them. Certainly no promises. 

  Are you sure? yes | no

ziggurat29 wrote 09/16/2017 at 04:16 point

That was my assumption, too (just branding in the ROM) but it seems a little more involved.  Still, I could wind up being totally wrong about the ports 80h-87h being for the logic analyzer, but those are not decoded on the motherboard, and they are in the 'expansion' range past 80h, so that's what led me to the hypothesis.

The gate arrays are I think factory programmed semi-custom ASICs.  The U10 does video stuff, and U29 does system stuff.  The video stuff involves (at least) pixel-shifting the character generator output, and combining some signals from the 6845 to make the video signal for the CRT.  It also does some other stuff giving access to the video/attribute RAM, since those are not in the processor's address space.  My mind is currently boggled on some of those going's-on, and it's not clear from the circuit how it's pulling it off.

Anyway, here it the Kaypro-native port blocks:
00-03 BRG A
04-07 SIO 1
08-0B BRG B
10-13 FDC
14-17 System Port -- nifty things
18-1B Parallel Printer Data
1C-1F Video stuff -- here there be dragons
20-23 PIO
24-27 RTC
28-3f (unused)

And, as I mentioned, due to there not being A6 decoded, the range 40-7F is an alias of those port addresses above.  But A7 is decoded, so anything 80-FF won't alias one of these.

My mind is boggled with video for two reasons:
1)  the code references a port 1Fh, but the 6845 only has two interface registers at 1Ch and 1Dh, so that's odd.  I am guessing that the video gate array is getting involved there for... something!
2)  the code also sets the 6845 address register to 1Fh (coincidence on the number), but the 6845 does not have a register numbered 1Fh; it only goes up to 11h.  I am really befuddled by that, but maybe it's a trick of some sort to set the address to reference an invalid register.

I only messed much with the video code to get a handle on what's being done, and to give names to the subroutines based on what I think they are doing.  Same with the floppy, but in a way that is more slow-going because CP/M gets more 'flavorful'.  There's a bunch of copying of DCBs and an ocean of buffer pointers, so tracking the thread of what is actually stored anywhere at any time gets a little challenging for my monkey brain.  Still, I did manage to wind my way up to the OS loader, and looked at enough of that to get back to the disk image problem I was having.

So...  The first sector contains some parameters that control the loading process.  Well, first I should mention that CP/M likes a thing called 'records', which are 128 bytes.  Even though the fundamental unit of transfer to the floppy is a 512-byte sector, the majority of the code (that I've seen) operates in terms of records, and there's a subroutine that will translate to (sector, offset) for a given record number.

The start of the first sector starts off:

C: 0, H: 0, S: 0(0)
0000:0000  18 FE 00 D8 00 EE 37 00  00 00 00 00 00 00 00 00  ......7.........
0000:0010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
0000:0020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
0000:0030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
0000:0040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
0000:0050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
0000:0060  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
0000:0070  00 00 00 00 00 00 00 00  00 00 00 00 00 00 13 03  ................
0000:0080  C3 5C DB C3 58 DB 7F 07  4D 41 53 4D 45 4E 55 00  .\..X...MASMENU.
0000:0090  20 4D 41 53 4D 45 4E 55  00 43 4F 50 59 52 49 47   MASMENU.COPYRIG
0000:00A0  48 54 20 28 43 29 20 31  39 37 39 2C 20 44 49 47  HT (C) 1979, DIG
0000:00B0  49 54 41 4C 20 52 45 53  45 41 52 43 48 20 00 00  ITAL RESEARCH ..
0000:00C0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
0000:00D0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
0000:00E0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
0000:00F0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
0000:0100  00 00 00 00 00 00 00 00  08 D8 00 00 5F 0E 02 C3  ............_...

OK, according to my disassembly, the first several words are interesting.

FE18 'jr $' (if it is even supposed to be code)
D800 OS load address
EE00 OS entry point vector
0037 record count (55)

So, as shown in the hex dump, the first record 0000-007F is what I'm calling the 'boot record' (CP/M probably has an official name for it), and we can see that it should load a contiguous strip of records into memory starting at D800.  There should be 55 records.  The start record is implicitly '1' (I showed that record in the hex dump above just for fun; it starts at 0080).  After all the records are loaded into memory, the boot ROM jumps to location EE00, which then is the rest of the OS.

OK, this is where I think that the floppy image IMD is incorrect in some way.  55 records is 13.75 sectors (55*128/512).  And considering that we are offset by one record, that makes it an even 14 *contiguous* sectors that should be read in.  But... recall that these disks are 10 sector per track, two sided.  So the following is interesting:

0000:01D0  3A 45 E3 3C CA 84 EB 21  0C 00 19 71 21 0E 00 19  :E.<...!...q!...
0000:01E0  70 CD 51 E8 3A 45 E3 3C  C2 7F EB C1 C5 2E 04 0C  p.Q.:E.<........
0000:01F0  CA 84 EB CD 24 E9 2E 05  3A 45 E3 3C CA 84 EB C1  ....$...:E.<....
C: 0, H: 1

C: 0, H: 1, S: 0(10)
0000:0000  00 4C 41 20 20 20 20 20  20 43 4F 4D 01 00 00 80  .LA      COM....
0000:0010  02 03 04 05 06 07 08 09  0A 0B 0C 0D 0E 0F 10 11  ................
0000:0020  00 4C 41 20 20 20 20 20  20 43 4F 4D 02 00 00 08  .LA      COM....

The top part is the end of track 0 side (head) 0 sector 9, and the second part is the beginning of sector 0 (logically numbered 10, you discovered).  But clearly this is directory, not more OS code.  (and very clearly if you have the rest of the hex dump, which I am not putting in this message).

So the OS looks truncated from the standpoint of the disk IMG, if you are to interpret the data encoded within (which specifies what CHS each sector is for) as per spec.  I even did extract those sectors, and do a quick disassembly and it is pretty clear that everything up to the last bit is OK, then it just abruptly ends.  Q.v.

EB61 CD 51 E8    call    sub_E851            ;plausible reference to previous code
EB64 3A 45 E3    ld      a, (word_E345)
EB67 3C          inc     a
EB68 C2 7F EB    jp      nz, loc_EB7F        ;plausible reference to following code
EB6B C1          pop     bc
EB6C C5          push    bc
EB6D 2E 04       ld      l, 4
EB6F 0C          inc     c
EB70 CA 84 EB    jp      z, 0EB84h           ;jump beyond the bounds of code
EB73 CD 24 E9    call    sub_E924
EB76 2E 05       ld      l, 5
EB78 3A 45 E3    ld      a, (word_E345)
EB7B 3C          inc     a
EB7C CA 84 EB    jp      z, 0EB84h           ;jump into the cruel icy vacuum of uninitialized
EB7F                                                          ;space, and NOP till you drop (or wrap to 0000)
EB7F          loc_EB7F:
EB7F C1          pop     bc
EB7F          ; end of 'ROM'

hmm.  But, you've managed to reconstitute the image onto a physical floppy and boot -- how bad can it be?  I don't know, but I wonder if it is possible that the process you used to create the image was technically incorrect, but the process used to reconstitute the image is also inversely incorrect.  So there was a bogon-antibogon annihilation that emitted a valid floppy.  It's just a guess, but it seems possible.  Anyway, if you successfully reconstitute that other disk I gave you, then that negates my hypothesis.  EDIT:  nope, the bogons were all in my mind.

Next, I might try to find the rest of the OS.  If I can, that might give a clue about how things are scrambled.  Which may just be my assumptions.

EDIT:  I found the rest of the OS; it was on part of the directory track.  I had later found some conditional code switching on something I don't understand, but when that condition is true, it loads the OS in two chunks, one from Track 0, as mentioned above, but the rest from Track 1 after the directory entries, for 4 sectors.  Maybe it will make sense later, but it now seems that maybe cpmtools is just not configured properly to understand these images, which is where we were before.

  Are you sure? yes | no

Eric Hertz wrote 09/17/2017 at 03:35 point

maybe the boot rom/os treats the disk as single sided... maybe a remnant of the past, for compatibility...?

There was a lot of reading-up ony part when starting this endeavor. ... came to numerous conclusions regarding e.g. copy protection, etc...

There was also a bit regarding chs mapping, which, as I recall can happen at several different levels... e.g. the rom bios might access the physical sectors, but the sector numbers are arbitrary... it's entirely possible to have a sector labelled c10h20s100 and it might not even be on c10... so then there's a bit in the os which also handles physical mapping... and... it could get quite ugly. I was lucky, really, that all tracks follow the same format and that the cyl number always matched the physical.

But my extraction process kept that possibility in mind, and I would've seen logical cylinders named differently than their physical location... in fact I saw a *lot* of that... but none of those ever had matching crcs, so attributes to read errors. Further because all 800 sectors were recovered eventually with matching crcs.

That said... it's still entirely plausible one of those levels is responsible for wierd mapping... wonder if the whole disk is like that... maybe side 0 is contiguous and then side 1...

Also plausible the os isn't responsible for the entirety of chs mapping... e.g. what's to stop an application from accessing the sectors directly rather than offsets...? But I doubt this has that level of weirdness...

  Are you sure? yes | no

ziggurat29 wrote 09/17/2017 at 04:42 point

nope; I now think that you are right, and I am wrong.  The more I look at it, I think that cpmtools is simply misconfigured for these disks (and you do have to configure it a lot), and that some of my assumptions about how the disk should look in a linear raw format were simply wrong.  I have verified:

1)  I can make an OS image that I can disassemble in a sane way
2)  maybe more interestingly, the OS image from your Omni4 disk binarily matches the OS image from a random disk archived on the internet (that I sent you a little while back).  This seems to imply that the disk-part of the OS for the KayPro2x is the same out-of-box stuff.  So what about the ROM?

Well, what indeed.  That could mean that my theory about Omni stuff in the ROM (beyond branding) is wrong, but I'm still hanging onto that one because it doesn't explain the wildly weird port addresses, and also as I disassembled more, it seems like there is some 'device redirection' that occurs.  I've seen some conditional switching between spewing bulk data to the floppy along with alternatively spewing bulk data to things in the 80h-87h range, which ain't .. on.. the mobo!

I've found some other nifty stuff.  I think I may have found some undocumented ESC code sequences (or maybe just lost-documented -- at least I couldn't find them):

    ESC,G; ESC,A; ESC,*,n,m; ESC,/x20,n,m; ESC,L,nn,mm; ESC,D,nn,mm

Who knows what these do? G & A just set a flag but I don't know what it does.  The others are more complex, and I think the *,' ' and L,D versions are principally the same, except you get to encode your number differently.

Maybe more interesting is that I think it is intended that you are meant to be able to have an 8K character generator ROM in place, and have two alternative character sets.  We don't have that on your board -- only 4K -- but I caught the code toggling a latch for A12 if you had a 2764.  I don't know if it is electrically possible on your physical board to change rom sizes, though. JEDEC only saved us so far.

I tired to make this post short -- I swear I did -- but I failed.  On the other hand, I have to do some work this week, alas, so I might be dark for half a fortnight or so.

Ciao for now!

  Are you sure? yes | no

Eric Hertz wrote 09/24/2017 at 04:31 point

ah... missed your last reply... (reply to a comment from me and I'll get a notice... I know it gets lame on deep threads)

Did some reading last night about the KayPLUS ROM and am pretty sure it saus 80-87h is used for something... was it the hard disk? (Oddly? That same rom image can work on all three '84 kaypros?! 2-84, 4-84, and 10-84)


Think I read somewhere yesterday that cpmtools expects all tracks from side zero to be sequential, followed by side one... which would get confusing with the logical sectors being 0-9 on side 0 and 10-19 on side 1... might be easier to write a script to reorganize the sectors in a raw binary than to figure out how to reconfigure cpmtools. And... might help to explain why we couldn't find definitions for the 2x... since most other tools (imaging) would do track 0 side 0, track zero side 1... based on logical sector numbering... I'd think.

  Are you sure? yes | no

ziggurat29 wrote 09/24/2017 at 14:48 point

Your research on the KayPLUS ROM is really interesting; I should check it out myself.

There is at least one thing that corroborates the hard disk hypothesis:  I did notice the port 80-87 access 'near' (codewise) access that was floppy-related.  I didn't look into it deeply, saving that analysis for later.  Perhaps I should find out what hard-disk controller was used in the units that had them, and study it's datasheet.

Still, the diff between the Omni4 ROM and the 'stock' Kaypro 2x ROM is non-trivial.  That doesn't disprove the hard disk hypothesis, but it sill seems to suggest that there is something Omni4-specific code in there.  OTOH, maybe the diffs arise from firmware versions, and the Omni4 ROM has later bugfixes (or earlier unfixed bugs) relative to the 'stock' ROM.

If it /is/ hard disk code, then that is certainly interesting, because it gives some insight into the software development practices within the Kaypro product line:  to have chunks of common code between the systems even if the specific system onto which it was installed physically does not have the particular hardware.  Actually, now that I think about it, the 2x is electrically similar to the 10, which preceded it (the 2x uses some gate arrays, while the 10 uses SSI logic).  The 10 /did/ have a hard disk option, so it very well could be that hard disk support code was inherited by the 2x firmware even if it was never used by that particular model.  Buried treasure!

Your cpmtools idea is interesting.  I should be able to reorder the sectors with relative ease since I already wrote a program to dump the IMD format to raw -- I just need to change the ordering of it's output.  And yes, if you're handy with Python/whatnot, then resequencing a raw image should be fairly straightforward, also, since it's just a random-access series of 512 byte records.

  Are you sure? yes | no