I've done some work with the DECNA boards (I wrote a DECNA emulator for an Xhomer fork that more or less works -- it even booted disklessly). So I had to do a bunch of reverse engineering.
The diagnostic ROM runs through 10 separate tests at its start:
- Test1: tests the ID ROM (which contains the MAC address) and does some initial testing on the dual-ported RAM. When it fails for the following reasons it yields the following codes (numbers are in octal):
- Failure 001: bus error reading ID ROM
- Failure 002: inconsistency in ID ROM contents: checksum invalid, MAC address is all 0s, bit 0 in the second MAC word is 1.
- Failure 003: failure to map dual ported RAM to physical address 0o05000000
- Failure 004: readback on MEM register isn't 012, inverted (using COMB) MEM register doesn't read back properly, write of 007 to MEM register doesn't read back, inverted (using COMB) MEM register doesn't read back properly. When this part succeeds it leaves the dual-ported RAM mapped at 0o14000000
- Failure 005: attempt to write first word of the dual-ported RAM yields a bus error
- Failure 006: *failure to see a bus error* when clearing ME in CTL and repeating test from previous step. After this step the DP RAM is unmapped.
- Test2: tests the dual ported RAM in earnest. This test maps the 128 KiB DP RAM to physical address 0o14000000..0o14377777 and turns on access in the CTL register.
- It then maps 4 pages (32 KiB) at a time of the DP RAM to virtual address space 020000..117777. It repeats this, moving the 32 KiB window over the 128 KiB of DP RAM on the board. For each of these mappings it does a series of tests. If any of these tests fails it posts failure codes 010 (for DP RAM offsets 0300000..0377777), 011 (for DP RAM offsets 200000..277777), 012 (for DP RAM offsets 100000..177777), or 012 (for DP RAM offsets 000000..077777)
- It then waits for a bit and back to read the first word of shared RAM and makes sure it's the pattern it last wrote (052525). If it's not then it posts error 013. On successful completion it leaves the highest 8 KiB of DP RAM (PA 0o14360000..0o14377777, which corresponds to DP RAM offsets 0360000..0377777) mapped at virtual address 020000...037777).
I haven't really worked through the details of the remaining 8 tests.
Concerning the discussion of the CTI option ROM: the DECNA actually contains 2 completely separate position-independent code blocks: the diagnostic ROM and the network boot ROM (to boot a diskless client system).
Every CTI option ROM starts with the following header:
Offset | Size | My name | Value on DECNA | Description |
---|
000 | word | ID | 0000042 | The CTI board ID. Because the CTI option ROM is read byte at a time boards that don't implement a real ROM will simply return a single byte for the ID and everything else (e.g., 004 for the floppy controller). Because the ID is a 16-bit word, in such a case the byte is read twice (e.g., 004 * 0400 + 004 = 002004). This is why all the non-ROM CTI boards have IDs with the same high and low bytes. |
002 | word | MAGIC | 0125377 | This is a pattern that the system boot ROM will recognize as indicating that the CTI board actually has a ROM. |
004 | byte | FLAGS | 004 | I'm guessing this is flags. The DECNA board is the only one with a non-zero value in it, so I'm guessing 004 means "bootable". |
005 | byte | NSECS | 002 | The number of sections (and section headers) to follow. |
006 | word | ROMSIZ | 000040 | The overall size of the ROM, in bytes, divided by 256. |
010 | word | ? | 000000 | All zero. I'm not sure what this is. |
This is then followed by the two section headers:
ROM offset | Header offset | Size | My name | DECNA value |
Description |
---|
012 | 000 | 5 words | SECTYP | 0042046, 0000000, 0000000, 0000000, 0000000 | Identifies the section type. 0042046 just happens to be "&D", and this section is the Diagnostic section. Other possible values are 00410046 + [0]*4 ("&B") for boot sections, 00530046 ("&V") for I have no idea, and 00500046 ("&P") also for something I don't know. |
024 | 012 | 3 bytes | SECOFF | 000000052 | ROM byte-offset of the beginning of this actual section. |
027 | 015 | word | SECLEN | 0012040 | Size (in bytes) of section |
031 | 017 | byte | - | 0 | Must be 0 |
|
|
|
|
|
|
032 | 000 | 5 words | SECTYP | 0041046, 0000000, 0000000, 0000000, 0000000 | Boot ("&B") section ID |
034 | 012 | 3 bytes | SECOFF | 000012112 | ROM offset of the beginning of this actual section. |
037 | 015 | word | SECLEN | 0003742 | Size (in bytes) of section |
041 | 017 | byte | - | 0 | Must be 0 |
So the diagnostic code you want to disassemble starts at ROM byte offset 000000052. I'm attaching a very raw and barely annotated disassembly that I've worked from.
Note that the last word of each of these two sections contains a dedicated checksum word that covers its contents, and similarly the entire ROM ends with a checksum word that covers the whole thing.
The system boot ROM will load the entire contents of a CTI option ROM section (up to 0o60000 bytes worth) at RAM location 02000 and it will then jump to that first location.
Before making the jump the system boot ROM sets up R1 with the CTI slot number (0..5 for the PC325/PC350, 0...6 for the PC380) and makes the jump. The system ROM provides some support code that is accessible via EMTs:
EMT number | My name | Inputs | Outputs | Description |
---|
0 | CTRSM | None | None | Doesn't return. Resumes back in the system boot ROM. |
1 | CTFIND | R0: CTI card ID (e.g., 0000042) | R0: CTI slot index 0..6 or -1 on failure | Looks through the soft configuration data to see if it has seen the given CTI card ID. Returns the slot number if it finds such a card, -1 on failure. |
2 | ICENB | R1: (Interrupt controller # (0..2) << 6 | interrupt channel (0..7)) | None | Executes "Clear Single IRR and IMR" for the given controller and channel. This enables the given interrupt. |
3 | ICDIS | R1: (Interrupt controller # (0..2) << 6 | interrupt channel (0..7)) | None | Executes "Set Single IMR Bit" for the given controller and channel. This disables the given interrupt. |
4 | ICTST | R1: (Interrupt controller # (0..2) << 6 | interrupt channel (0..7)) | None | Executes "Set Single IRR Bit" for the given controller and channel. This creates a software-generated interrupt. |
5 | KMAP | -4(sp) number of 8 KiB pages to map (0..4)
-2(sp) High 9 bits of physical address. | None | Stores KIPAR1-4 into 01120, 01116, 01114, 01112, and then reprograms them to map the required space. Also sets bit 04000 in the UIPAR0 to indicates that KIPAR1-4 are stored in RAM. |
6 | KUMAP | None | None | Restores KIPAR1-KIPAR4 from values stored in 01120, 01116, 01114, 01112 only if bit 04000 is set in UIPAR0. Then clears bit 04000 in UIPAR0. |
Hopefully that will give you more information to help you decode what's going on in the diagnostic ROM.
If you get further into it you'll need
http://bitsavers.org/components/intel/ethernet/i82586.pdf and an app note in
http://bitsavers.org/components/intel/_dataBooks/1991_Microcommunications.pdf (pp. 1-337 to 1-417).
IIRC you should be forewarned: the datasheet is actually wrong in several important places. Off the top of my head I seem to recall it is wrong about the top-level shared RAM structure used by the i82586. The top-level "System Configuration Pointer" in the block at the last 10 bytes of memory does
not point to the system configuration block (SCB). Instead, it points to something called an "intermediate system configuration pointer" (ISCP) which then points to the SCB. This is mentioned in the app note. Ugh.
--Bjoren