Octobus Test Protocol - TPE OCTOBUS B00 reverse engineering¶
Status: REQUEST + REPLY FORMATS BYTE-VERIFIED (2026-07-16, second pass). All command codes, the reply header (magic / cmd+1 / station / status), every per-command reply payload layout, and the exact fields TPE validates are now proven from disassembly (sections 1, 3.3, 3.4); the emulator reply recipe is in section 3.6. Goal: recover the OMD-0 "Octobus Test Protocol" request/response formats so the RetroCore emulated stations can answer TPE tests 4-6 (Check Octobus configuration / Echo single word / Echo multi word) and the CONFIG/LIST-HARDWARE-CONFIGURATION probes.
Source of truth: octobus-b00.test loaded in Ghidra (from the test floppy
D:\ND\S\Nd-210523I01-XX-01D.img, path /D:/ND/S/testprog/x/octobus-b00.test),
plus live traces from the RetroCore run (%LOCALAPPDATA%\trace\file-trace.txt).
The program loads at base VA 0x6B88; TPE-MON library vector slots at 0x69xx.
1. Request wire format [V - live trace + disassembly agree]¶
A protocol exchange is a multibyte octobus message to the target station, OMD 0. On the wire (16-bit software frames written to IOX 100405):
SOMB frame: C | M | S | OMD (low byte 0x30 for OMD 0)
data frame: source OMD byte (0x00 = reply to OMD 0)
data frame: byte count N
data frame x N: payload bytes
EOMB frame: C | M | OMD (low byte 0x20 for OMD 0)
Payload always starts with the magic word 0x71C7 (two bytes 71 C7), followed by a 16-bit command word:
ALL command codes byte-verified 2026-07-16 (SAA immediates in the builders; menu-handler mapping via xrefs). Request byte counts are the builder's 7,X arg.
| Command word | Meaning | Extra payload words (after magic+cmd) | Req bytes | Builder fn (renamed in Ghidra) | Menu handler caller |
|---|---|---|---|---|---|
| 0x0000 | Identify yourself | - | 4 | otp_build_identify_yourself_cmd00 @ ram:cea2 | 87bd (otp_cmd_identify_yourself) |
| 0x000A | Get present stations | - | 4 | otp_build_get_present_stations_cmd0A @ ram:cf35 | 8901; also c6e8 (test-4 scan) |
| 0x000C | Echo single word | [2]=pattern NUMBER (incrementing counter, 0x0001 = first), [3]=pattern word | 8 | otp_build_echo_single_word_cmd0C @ ram:cf5a | 8988; also c85b (test-5 helper) |
| 0x000E | Echo multi word | [2]=string number, [3]=word count N, [4..3+N]=string words | 8+2N (max 250 @ [cfcc]) | otp_build_echo_multi_word_cmd0E @ ram:cf83 | 8a68; also c936 (test-6 helper) |
| 0x0010 | Read Octobus register | [2]=register function (prompt: "Register function (0,2,6)."), [3]=UNINITIALIZED stack junk (byte count is still 8!) | 8 | otp_build_read_octo_register_cmd10 @ ram:cfd0 | 8ae4 (otp_cmd_read_octo_register) |
| 0x0012 | Write Octobus register | [2]=register function (prompt: "(3,5,7)."), [3]=register content | 8 | otp_build_write_octo_register_cmd12 @ ram:cff7 | 8b7c; also cb8a (scan_emergency) |
| 0x0016 | Get Domino Information | - | 4 | otp_build_get_domino_info_cmd16 @ ram:cec6 | 880e (otp_cmd_get_domino_information) |
| 0x0018 | Get test version | - | 4 | otp_build_get_test_version_cmd18 @ ram:ceeb | 885f (otp_cmd_get_test_version) |
| 0x001A | Get module type | - | 4 | otp_build_get_module_type_cmd1A @ ram:cf10 | 88b0 (otp_cmd_get_module_type) |
(ram:d020 is NOT a builder - it is the reply parser, see 3.3. The earlier names
otp_build_cmd16_word_unk / otp_build_cmd18_twoword_unk were WRONG: those
builders carry cmd 0x10 and 0x12, byte-verified at ram:cfd8 SAA 0x10 and
ram:cfff SAA 0x12.)
Live-captured requests (trace, sent to station 10):
- Identify yourself: body 00 04 71 C7 00 00
- Get present stations: body 00 04 71 C7 00 0A
- Echo single word: body 00 08 71 C7 00 0C 00 01 FF FF
(= pattern number 1, pattern 0xFFFF. Byte-verified 2026-07-16 in the caller at
ram:c84c-c85a: first extra word is a per-iteration counter LDA -0x77,B ; AAA 0x1,
second is the pattern from -0x73,B; validators compare "pattern number" and "pattern".)
2. Send path [V]¶
octobus_send_multibyte_message@ ram:d16a: clears 6 result counters, sends SOMB / srcOMD / count / payload bytes (LBYT, byte-packed buffer) / EOMB via the frame sender at ram:d13e; counts per-frame ack results.- IOX primitives:
iox_write@ ram:d726 (T=base+off, A=value, IOXT),iox_read@ ram:d733, IDENT level 13 @ ram:d740 (also clears global 0x71de). - Transmission statistics decoded by
print_transmission_statistics@ ram:863d with labels (ram:8734+): "Transmission OK/NOT OK", "No. of PA (PArity error)", "No. of BU (BUsy)", "No. of NP (Not Present)", "No. of RE (REtry counter 0)", "No. of not RT (Ready for Transfer)", "No. of RQ (ReQuest on)". This confirms the output-status bits TPE reads after transmit: RQ=bit2, RT=bit3, RE=bit5, NP=bit6, BU=bit7, PA=bit8 (matches RetroCore TransmitStatusBits).
3. Reply wire format [recovered - V/[I] as marked]¶
3.1 Reply envelope [V - symmetric to the request]¶
A station's reply is an ordinary Octobus multibyte message, sent back to the asker's OMD (0). It uses the SAME envelope as the request (section 1):
SOMB frame: (0x30 | OMD) low byte 0x30 for OMD 0
data frame: source OMD byte
data frame: byte count N
data frame x N: payload bytes
EOMB frame: (0x20 | OMD) low byte 0x20 for OMD 0
Send side proven in octobus_send_multibyte_message @ ram:d16a: SOMB built
at ram:d1ae (SAA 30B ; ORA reg[8]=OMD), EOMB at ram:d1f1
(SAA 20B ; ORA reg[8]=OMD). The reply direction reuses this exact framing.
3.2 Frame-word bit layout as RECEIVED [V - disassembly of ram:d3ae]¶
octobus_decode_frame_word @ ram:d3ae unpacks each 16-bit word read from the
Octobus INPUT-DATA register (offset +0) into a 4-word struct:
| Bits | Field | Meaning |
|---|---|---|
| 15 | C | control / marker flag |
| 14 | B | Broadcast/TYPE flag (manual App. 2; M is bit 5 in the LOW byte, not here) |
| 13:8 | srcStation | 6-bit SOURCE STATION number (0..62), (raw AND 37400B) SHR 8 |
| 7:0 | dataByte | payload byte, raw AND 377B |
Mask byte-verified 2026-07-16: the AND at ram:d3c3 references the data word at ram:d3cb = 0x3F00 = 37400B (full 6-bit station field). An earlier draft cited 3400B - a dropped octal digit, same failure mode as the 1404B->140400B trace bug.
So the responding station number is carried in bits 13:8 of EVERY reply frame
word (hardware inserts it). The emulated station must present reply frames as
(flags<<14) | (srcStation<<8) | dataByte. [V]
3.3 Reply receive/parse path [V - fully byte-verified 2026-07-16]¶
Corrections to the earlier draft: d020 parses a WHOLE message (not single frames); 0x7198 is NOT a reply table; the "offsets 0,2,4,6" copy is the SEND result (transmission counters), not the reply.
octobus_receive_multibyte_message@ ram:d2be (was octobus_receive_frame_wait) receives one complete multibyte message into a 6-word struct R: R[0]=status flags (bits 15/14 initialized to 1 = OK, CLEARED by the error continuation at ram:d2c4 which also records error code n as bit n via a computed BSTA/EXR); R[1]=source station (bits 13:8 of the SOMB frame word, ram:d343-d345); R[2]=source OMD byte AND 0x000F ([d38d], ram:d34b-d34e); R[3..5]={payload buffer word address 0xd082 ([d38e]), 0, byteCount-1} (ram:d36b-d372). It waits for a frame with low-byte flags AND 0x30 == 0x30 (SOMB, [d342] @ ram:d329-d32c), reads the srcOMD frame, the count frame, then count payload bytes (SBYT into the static buffer at word address ram:d082, byte-indexed, HIGH byte first = big-endian byte order, ram:d35d-d36a), then requires an EOMB frame: flags AND 0x20 == 0x20 ([d38f] @ ram:d37b-d37e, else error bit 9 @ d380-d389). [V]octobus_parse_received_message@ ram:d020 (was octobus_parse_received_frame) calls d2be once ([d07d]) and produces a 3-word outrec for the caller: outrec[0] = response TYPE: 0 = Test Protocol response received OK, 1 = no response message received, 2 = received but not a Test Protocol message, 3 = part of the response message is lost (string mapping proven in otp_read_response_and_display, see 3.4a); outrec[1] = source station (R[1]); outrec[2] = payload WORD pointer = R[3] + R[4]>>1 = ram:d082. Type derivation: R[0] bit15|bit14 set -> message present (candidate type 0); both clear -> bit11|bit10 set = type 3 else type 1 (ram:d02e-d048). Type 0 requires srcOMD R[2]==0 (ram:d04e-d052) AND first payload word == 0x71C7 (magic constant AT ram:d080, compare @ ram:d05c-d062), else type 2. [V]otp_response_classify@ ram:c58a seeds timeout ctr 0x71e3 with 100. (ram:c58f-c590), loops d020 until type 0 or timeout, then validates the reply HEADER (see 3.4). On type 2 it restarts the whole listen loop (JMP [c641] -> ram:c58d). Returns 1 = reply valid. [V]- The 14-word-per-entry table at ram:0x7198 (hex; earlier "7198B" was a bogus suffix) is the per-CONTROLLER context table, indexed by [0x71e6] = selected Octobus CONTROLLER number (written by cmd_select_device @ ram:7bac, prompt "Octobus controller number"): ctx[5] = receive parameter, ctx[6] = OWN station number (ram:c77d), ctx[10..13] = 62-bit hardware presence bitmap read via computed-shift EXR (ram:c756-c760). Replies are parsed IN PLACE in the byte buffer at ram:d082 - nothing is unpacked into 0x7198. [V]
3.4 Reply payload layout - BYTE-VERIFIED (2026-07-16)¶
Every reply payload starts with a fixed 4-word header, then command-specific data. All offsets below are payload WORD indices (payload bytes are stored big-endian, high byte first, into the buffer at ram:d082; payload word j = bytes 2j..2j+1).
3.4a Common reply header [V - otp_response_classify @ c58a]¶
| Word | Content | Verification |
|---|---|---|
| 0 | 0x71C7 magic | d020 @ ram:d05c-d062, constant at ram:d080 |
| 1 | response code = REQUEST COMMAND + 1 (0x0000->0x0001, 0x000A->0x000B, ... 0x001A->0x001B) | compared vs expectedCmd at ram:c61b-c61d; expectedCmd literals: 1 @ c7ec (identify), 0xB @ c710, 0xD @ c882, 0xF @ c95e; display switch @ 8bd2/8be0/8c14/8c5f/8cc5/8cf7/8d05/8d51/8d79 |
| 2 | responder's STATION number | must equal BOTH the frame source station outrec[1] (ram:c5ca-c5cd, reporter [c60a]=b526) AND the expected station param (ram:c5e1-c5e3, reporter [c60b]=b633) |
| 3 | STATUS word | ram:c5f3-c5f7 + display @ 8f50-8f5c/8f77-8f99: 0="Ok" -> classify success; 1="Illegal Octobus register function"; 2="Illegal Test Protocol command code"; other="Status code not defined" |
A reply is accepted by the automated tests only if: type 0 (message received, srcOMD==0, magic OK), word1 == cmd+1, word2 == expected station == frame source station, word3 == 0. Then classify returns 1 (ram:c62b-c62c).
3.4b Per-command reply data [V - otp_decode_response_fields @ 8bc8 + test validators]¶
| Response code | Command | Reply payload words after the header | Evidence |
|---|---|---|---|
| 0x0001 | Identify yourself | NONE - header only (8 bytes total). Neither the display (prints title only, ram:8bd2-8bde) nor test 4 (otp_scan_identify_yourself @ c7a1, classify-only) reads anything past word 3. Test 4 additionally checks that EXACTLY ONE identify reply arrives (duplicate counter loop ram:c7f2-c80d, reporter [c82c]=bf8a). | 8bd2, c7a1 |
| 0x000B | Get present stations | word[3+j] for j=1..62: ONE WORD PER STATION, value EXACTLY 1 = station j present, anything else = absent. Full reply = 66 words = 132 bytes. Test 4 (otp_scan_get_present_stations @ c6c2) compares (word[3+j]==1) XOR ownHwPresenceBit(j) for ALL j=1..62 (loop ram:c716-c78f, XOR via REXO @ ram:c763), skipping only j == ctx[6] (own station, ram:c77b-c77e). Display loop ram:8bee-8c12 prints "Present station j" for each ==1. | c6c2, 8bee |
| 0x000D | Echo single word | [4] = pattern number (must equal the sent pattern number, cmp ram:c88a-c88c, reporter [c8b7]=b8c5), [5] = pattern word (must equal sent pattern, cmp ram:c89d-c89f, reporter [c8b8]=b971). Reply = 12 bytes. | c886-c8ad, 8c17-8c5c |
| 0x000F | Echo multi word | [4] = string number (cmp ram:c966-c968, reporter [c9b5]=ba19), [5] = string length in WORDS (must equal sent count, cmp ram:c979-c97d, reporter [c9b6]=bac4), [6..5+N] = the N string words (per-word cmp loop ram:c99d-c9a4, reporter [c9e8]=bb74). Reply = (N+6)*2 bytes - the no-response path prints exactly this expected size (ram:c9d2-c9e3). | c92b/c962-c9d1, 8c62-8cb1 |
| 0x0011 | Read Octobus register | [4] = Register content (printed octal, ram:8ce5-8cf3). Reply = 10 bytes. | 8cc8-8cf5 |
| 0x0013 | Write Octobus register | NONE - header only (status ack). Display prints title only (ram:8cfa-8d03). | 8cf6-8d03 |
| 0x0017 | Get Domino Information | [4..5] = "Type of processor" as a 32-bit number (LDD payload[4] @ ram:8d21-8d23, printed via 32-bit formatter, format word 0x35f - e.g. 68000. needs 32 bits), [6..7] = "OPCOM version" as a 4-char ASCII string (print {payload+6,0,3} @ ram:8d30-8d38), [8..17] = "Compile time" as a 19-char ASCII string (print {payload+8,0,0x12} @ ram:8d45-8d4d). Reply = 36 bytes. | 8d08-8d4f |
| 0x0019 | Get test version | [4] = "Octobus test version" (16-bit number, printed decimal, ram:8d5d-8d76). Reply = 10 bytes. | 8d54-8d77 |
| 0x001B | Get module type | [4] = module type code: 1 = "Domino controller" (str @ ram:8ebe), 2 = "MFBus controller" (@ ram:8ec7), 3 = "ACCP" (@ ram:8ecf); other values print the title only. Reply = 10 bytes. | 8d85-8dae |
Unknown response code -> "Illegal Test Protocol response code." (@ ram:8ed1).
NOTE the asymmetry: request parameters sit at payload words 2..3 (no station/status), replies insert station+status at words 2..3 and echo the request parameters starting at word 4.
Response-level reader states printed by READ-RESPONSE (type -> string): type 1 -> "No response message received" (@ 8fc6), type 2 -> "Message received, but not a Test Protocol message" (@ 8fd6), type 3 -> "Part of the response message is lost" (@ 9001), type 0 -> "Test Protocol response message received" (@ 9015). [V - ram:8ef8-8f34]
3.5 Validator field comparisons [V - error strings + printers]¶
The pass/fail validators compare reply fields against transmitted values and print (each printer sits just before its data string, data-after-code):
| Printer fn | Error string | Field compared | Call site (byte-verified) |
|---|---|---|---|
| report_pattern_number_error @ b8c5 | "Not expected pattern number received" (@ b93c) | echo-single reply word 4 vs sent pattern number | ram:c88a-c899, [c8b7] |
| report_pattern_value_error @ b971 (renamed this pass) | "Received pattern is not equal transmitted pattern" (@ b9e4) | echo-single reply word 5 vs sent pattern | ram:c89d-c8ac, [c8b8] |
| report_string_number_error @ ba19 | "Not expected string number received" (@ ba90) | echo-multi reply word 4 vs sent string number | ram:c966-c975, [c9b5] |
| report_string_length_error @ bac4 | "Length of received string is not equal..." (@ bb2e) + "String length - transmitted/received" (@ bb55) | echo-multi reply word 5 vs sent word count | ram:c979-c98b, [c9b6] |
| report_word_pattern_error @ bb74 | "Received pattern is not equal..." (@ bbff) + "Word number in string / Pattern transmitted/received" (@ bc1b) | echo-multi reply word 6+k vs sent word k | ram:c99d-c9bf, [c9e8] |
| b526 / b633 (header reporters) | "Received message from not expected station" family | reply word 2 vs frame srcStation / vs expected station | ram:c5cf-c5de / c5e5-c5f0 |
| b6e8 / b76e (status reporters) | "Illegal Octobus register function" / "Illegal Test Protocol command code" | reply word 3 (status) | ram:c5f3-c60e |
| b7db (cmd-echo reporter) | wrong response code | reply word 1 vs expectedCmd | ram:c61b-c628 |
3.6 Emulator reply recipe [V] + remaining open items¶
To satisfy TPE tests 4-6, an emulated station at number S must, on receiving an OMD-0 multibyte message whose payload starts 71 C7, send back one multibyte message (envelope of section 3.1, srcOMD byte = 0, station S in bits 13:8 of every frame word, payload big-endian per word):
word 0: 0x71C7
word 1: request command + 1
word 2: S (own station number)
word 3: 0 (Ok) [1 = illegal register function, 2 = illegal TP command code]
then per command:
cmd 0x0000 identify: nothing (count = 8 bytes)
cmd 0x000A present: 62 words, word[3+j] = 1 if station j present else 0,
j = 1..62 (count = 132 bytes)
cmd 0x000C echo single: echo request words 2,3 (count = 12 bytes)
cmd 0x000E echo multi: echo request words 2,3 and the N string words
(count = 12 + 2N bytes)
cmd 0x0010 read reg: 1 word register content (count = 10 bytes)
cmd 0x0012 write reg: nothing (count = 8 bytes)
cmd 0x0016 domino info: 2 words processor type (32-bit, e.g. 68000.),
2 words OPCOM version (4 ASCII chars),
10 words compile time (19 ASCII chars + pad)
(count = 36 bytes)
cmd 0x0018 test version: 1 word version number (count = 10 bytes)
cmd 0x001A module type: 1 word: 1=Domino ctrl, 2=MFBus ctrl, 3=ACCP
(count = 10 bytes)
For test 4 the present-station words MUST agree with which stations actually answer ident on the emulated bus (TPE XORs each word against its own hardware presence bitmap, own station excepted). Timing: the reply is polled by classify with a 100-iteration countdown ([0x71e3] seed @ ram:c58f); the reply just needs to be queued in the controller input FIFO when READ-RESPONSE / the test's receive loop runs.
Remaining open items:
- [UNCERTAIN] Whether a REAL ACCP pads the identify / write-reg replies beyond the 4 header words. TPE reads nothing past word 3, so 8-byte replies are sufficient for the tests, but a live capture from real hardware would settle the actual byte counts.
- [UNCERTAIN] Sub-function semantics of "Register function" 0,2,6 (read) and 3,5,7 (write) - which physical Octobus/station registers they map to on a Domino/ACCP module. The values come from the menu prompts (ram:8b05+/8bc0+); the station-side meaning is not derivable from this binary. Status 1 = "Illegal Octobus register function" is the reject code.
- [UNCERTAIN] Exact "Type of processor" value a real ACCP returns in the domino-info reply (displayed as a 32-bit number; 68000. = 0x109A0 is the obvious candidate for the MC68000 baby card, but not byte-provable here).
- Station scan
otp_station_scan_emergency@ ram:cb4f iterates stations 0..62 reading the per-station Octobus status viaEXR STand, for present stations, sends WRITE-OCTO-REGISTER (cmd 0x12) probes via cff7 @ cb8a. The presence test is the hardware status read, not a get-present-stations reply. [I] - Note:
BSET 0x3@ ram:c850 on the echo-single pattern-number counter - sub-function (set/clear bit 3) not decoded from the mnemonic; the live trace (pattern number 0x0001 first) implies it does not set bit 3. [UNCERTAIN - cosmetic only, TPE side]
4. Function map so far (renamed in Ghidra)¶
| Address | Name | Evidence |
|---|---|---|
| ram:cea2 | otp_build_identify_yourself_cmd00 | 0x71C7 + STZ cmd, len 4; matches trace |
| ram:cec6 | otp_build_get_domino_info_cmd16 (renamed) | 0x71C7 + SAA 0x16; caller 880e = GET-DOMINO-INFORMATION [V] |
| ram:ceeb | otp_build_get_test_version_cmd18 (renamed) | 0x71C7 + SAA 0x18; caller 885f = GET-TEST-VERSION [V] |
| ram:cf10 | otp_build_get_module_type_cmd1A (renamed) | 0x71C7 + SAA 0x1A; caller 88b0 = GET-MODULE-TYPE [V] |
| ram:cf35 | otp_build_get_present_stations_cmd0A | 0x71C7 + SAA 0x0A; matches trace |
| ram:cf5a | otp_build_echo_single_word_cmd0C | 0x71C7 + SAA 0x0C + 2 param words; matches trace |
| ram:cf83 | otp_build_echo_multi_word_cmd0E (renamed) | SAA 0x0E @ cf8b; stringNum + count + words; cap 250 bytes [V] |
| ram:cfd0 | otp_build_read_octo_register_cmd10 (renamed; WAS mislabeled cmd16) | SAA 0x10 @ cfd8; 1 param (register function 0/2/6); word3 uninitialized, len still 8 [V] |
| ram:cff7 | otp_build_write_octo_register_cmd12 (renamed; WAS mislabeled cmd18) | SAA 0x12 @ cfff; 2 params (register function 3/5/7, content) [V] |
| ram:ce6b | otp_send_test_protocol_request (renamed) | SEND ONLY - does NOT await the reply (reply is read separately by c58a/8ee4); calls d16a, returns 4-word transmission result; slot [0x6912] resolves to 0x55d7 in TPE-MON (byte-move) [V] |
| ram:d16a | octobus_send_multibyte_message | SOMB(0x30|OMD)/srcOMD/count/bytes/EOMB(0x20|OMD) + counters [V] |
| ram:d13e | (frame sender, garbled body) | sends one framed word |
| ram:863d | print_transmission_statistics | strings at 8734+ |
Reply-path functions [renamed this pass]¶
| Address | Name | Evidence |
|---|---|---|
| ram:d020 | octobus_parse_received_message (renamed) | receives one whole message via d2be, outputs {type, srcStation, payloadWordPtr}; magic 0x71C7 @ d080 [V - see 3.3] |
| ram:d2be | octobus_receive_multibyte_message (renamed) | full SOMB/srcOMD/count/bytes/EOMB receive into 6-word struct; payload bytes to buffer @ d082 [V - see 3.3] |
| ram:d082 | otp_reply_payload_buffer (data, renamed) | static reply payload byte buffer, word-addressed base [V] |
| ram:d3ae | octobus_decode_frame_word | bit15=C,14=B,13:8=srcStation (mask 0x3F00 @ d3cb),7:0=byte [V] |
| ram:d3a0 | octobus_read_input_data | reg offset +0 [V] |
| ram:d473 | octobus_read_input_status | reg offset +2 [I] |
| ram:d3f8 | octobus_write_input_control | reg offset +3 [I] |
| ram:d3ce | octobus_write_output_command | reg offset +5 [V] |
| ram:d4ce | octobus_read_output_status | reg offset +6 [I] |
| ram:d434 | octobus_write_output_control | reg offset +7 [I] |
| ram:d726/d733/d740 | octobus_iox_write / octobus_iox_read / octobus_ident_level13 | IOXT / IDENT 0x23 [V] |
| ram:d482 | octobus_decode_output_status_bits | unpacks 3 flag bits [I] |
| ram:d4dd | octobus_decode_input_status_bits | unpacks 16 bits into array [I] |
| ram:ce0e/ce28/ce35/ce54 | otp_buf_dequeue / _avail / _enqueue / _init | linked-buffer helpers on DAT_ram_ce0d [I] |
Interactive TP subcommands [V - from command table @ 6cef, menu names @ 929f]¶
| Handler | Menu name |
|---|---|
| ram:8796 | otp_cmd_identify_yourself |
| ram:88da | otp_cmd_get_present_stations |
| ram:892b | otp_cmd_echo_single_word |
| ram:89d4 | otp_cmd_echo_multi_word |
| ram:8aa2 | otp_cmd_read_octo_register |
| ram:8b1f | otp_cmd_write_octo_register |
| ram:87e7 | otp_cmd_get_domino_information |
| ram:8838 | otp_cmd_get_test_version |
| ram:8889 | otp_cmd_get_module_type |
| ram:8ee4 | otp_read_response_and_display (READ-RESPONSE) |
| ram:8bc8 | otp_decode_response_fields |
Scan / validator / report functions [renamed this pass]¶
| Address | Name | Evidence |
|---|---|---|
| ram:c58a | otp_response_classify | validates reply header (magic/cmd+1/station/status), args (ctx, reportFlag, &outrec, expStation, expCmd) -> 1 = OK [V - see 3.4a] |
| ram:c643 | otp_scan_present_bits | scans the 6-word transmission result counters (NP bits etc.) [V] |
| ram:c6ac | otp_station_num_in_range_8_55 (renamed; WAS otp_scan_stations_test4) | returns 1 if 7 < station < 0x38; small helper, NOT a scan [V] |
| ram:c6c2 | otp_scan_get_present_stations (new fn this pass) | test-4 half: get-present-stations to each in-range station, XOR-checks 62 reply words vs hw bitmap [V] |
| ram:c7a1 | otp_scan_identify_yourself (renamed; WAS otp_scan_stations_test5) | test-4 half: identify to each present station, expectedCmd=1, counts duplicate replies [V] |
| ram:c830 | otp_test_check_octobus_config (renamed; WAS otp_check_rft_and_range) | TEST 4 body = calls c7a1 [c846] then c6c2 [c848] [V] |
| ram:c84a | otp_echo_single_send_validate (new fn this pass) | sends cmd 0x0C, classify expCmd=0x0D, cmp payload[4]/[5] vs sent [V] |
| ram:c8b9 | otp_scan_echo_single_word (renamed; WAS otp_probe_all_stations) | TEST 5 body: per station sends FFFF/0000/FFFF/0000 + 16 walking-one + 16 walking-zero patterns via c84a [V] |
| ram:c92b | otp_echo_multi_send_validate (new fn this pass) | sends cmd 0x0E, classify expCmd=0x0F, cmp payload[4]/[5]/[6+k] vs sent [V] |
| ram:c9ea | otp_scan_echo_multi_word (renamed; WAS otp_fill_pattern_buffer) | TEST 6 body: fills 121-word up/down pattern buffer, sends lengths 1..([71ed]/2-6) via c92b [V] |
| ram:b971 | report_pattern_value_error (renamed) | owns str b9e4 "Received pattern is not equal transmitted pattern"; called from [c8b8] [V] |
| ram:cb4f | otp_station_scan_emergency | iterate 0..0x3e presence via EXR + probe [V] |
| ram:ae5b | report_parity_error | owns str aea0 "Parity error when transmitting" [V] |
| ram:aebc | report_dest_fifo_full | owns str af01 "Receive fifo...full" [V] |
| ram:af1d | report_no_answer_from_station | owns str af5d "No answer from station" [V] |
| ram:af73 | report_retry_counter_zero | owns str afb8 "Retry counter equal to zero" [V] |
| ram:ab36 | report_unexpected_source_station_unk | str aba5 "Received message from not expected station" [I] |
| ram:b8c5 | report_pattern_number_error | owns str b93c/b9e4 [V] |
| ram:ba19 | report_string_number_error | owns str ba90 [V] |
| ram:bac4 | report_string_length_error | owns str bb2e/bb55 [V] |
| ram:bb74 | report_word_pattern_error | owns str bbff/bc1b [V] |
| ram:7d9d | test1_transmit_receive_loop | first dispatched, station-table iterate [I] |
| ram:7eda/7f41/7fa8 | test_body_echo_single/multi/config_scan_unk | call c830/c8b9/cb4f [I] |
Loader-named commands: cmd_run @ 7d90, cmd_octobus_test_protocol @ 8617, cmd_list_octobus_devices @ 7cf6, cmd_list_hardware_configuration @ 7b24, cmd_select_octobus_station @ 7be2, cmd_decode_status_register @ 7d03, cmd_force_errors @ 8073, cmd_octobus_facilities @ 80a6, cmd_select_device @ 7b7b.
Key globals (all addresses HEX; earlier draft wrongly suffixed them "B"): 0x71e6 = selected Octobus CONTROLLER number (renamed selected_octobus_controller_num; written by cmd_select_device @ 7bac, prompt "Octobus controller number" - NOT the destination station, which is prompted per command); 0x7198 = per-CONTROLLER context table, 14 words (0xE) per entry (renamed octobus_controller_ctx_table_14w; ctx[5]=receive param, ctx[6]=own station number, ctx[10..13]=62-bit hw presence bitmap) - NOT a reply table; 0x71de = ident/interrupt flag cleared by d740; 0x71e3 (otp_recv_timeout_ctr) = receive countdown, seeded 100. by classify @ c58f; 0x71e7 (otp_all_devices_flag); 0x71e8/0x71e9 (otp_single_station_filter / otp_filter_station_num) = scan single-station filter; 0x71ed (otp_max_message_bytes_param, renamed) = max message size in bytes, test 6 uses string lengths up to [71ed]/2 - 6 words.
4b. Alignment vs ND-05.020.01 manual + ND-5800 microcode (added 2026-07-16)¶
Cross-checked against E:\Dev\Ronny\ND5000UC\manual\ND-05.020.01 EN ND-5000 Hardware
Description.md (Appendix 2, Octobus Protocol v5, frame table ~line 10898-10959 + ACCP
chapter ~line 3928) and the B30 microcode disassembly.
CONFIRMED by the manual [V]:
- Frame layout: bit 15 = C, bit 14 = B, bits 13:8 = SOURCE (on receive) / DEST (on send),
low byte = code with flag bits E=7, K=6, M=5, S=4. SOU/DEST is a 6-bit field.
- SOMB = flags M+S in the low byte = 0x30 | OMD (S=1 start); EOMB = M only = 0x20 | OMD
(S=0 end) - exactly the envelope in section 1/3.1. (The manual's row for EOMB appears to
mark the K column - table-conversion artifact; the Appendix-2 bit-value table shows
E,K,M,S = 0010 for EOMB and 0011 for SOMB, and the definitions text says S=1 start /
S=0 end of message.)
- Who answers OMD 0: the ACCP (baby card MC68000 firmware), NOT the microprogram.
Manual ~3930: "The ACCP itself responds only to multibyte messages to OMD numbers 0 and 3
... OMD 0 is reserved for octobus test programs, OMD 3 for the ACCP library commands.
Kicks, idents and multibyte messages to OMD other than 0 and 3 are written directly to
the microprogram via AOB." And ~4071: "there is no octobus driver for handling multibyte
messages in the microprogram" - corroborated independently by the B30 microcode
disassembly: OCB_DECODE handles only kick codes (1/2=activate, 3, 4/5, 6), no 0x71C7 /
multibyte parsing exists anywhere in the control store.
=> Emulator placement in section 5 (station/ACCP class = OctobusND5000Station) is correct;
the mailbox/microcode engine must never see OMD 0 traffic. The "ACCP" module-type string
in 3.4 fits: on a 5000 station the test-protocol responder IS the ACCP.
- OMD 3 replies use the Messack/Messnak convention (single byte ack, or nak + error code +
2 status bytes; returned data follows Messack in the same multibyte message) - relevant
when the ACCP library commands get implemented.
DISCREPANCIES - ALL THREE RESOLVED by Ghidra byte-verification 2026-07-16
(sections 1 and 3.2 above are corrected; kept here for the audit trail):
1. Section 3.2 claims srcStation = bits 13:8 (6-bit, 0..62) but cites the extraction as
(raw AND 3400B) SHR 8. 3400B masks only bits 10:8 (3 bits, max station 7) - it
cannot represent the live-tested station 10 (decimal). Either the mask in the binary is
really 37400B (= 0x3F00, bits 13:8) and the doc dropped a digit (same failure mode as
the earlier 1404B->140400B trailing-zeros bug in the +5 write format), or the decode fn
is genuinely 3-bit and something else supplies the station. Byte-verify at ram:d3ae.
RESOLVED [V]: data word at ram:d3cb is 0x3F00 = 37400B; full 6-bit field. Doc fixed.
2. Section 3.2 names bit 14 "M - second marker". Per the manual, bit 14 is B
(broadcast/TYPE flag); M is bit 5 in the low byte. Rename to avoid future confusion -
the type classification "bits 15/14/5/4" in 3.3 already uses the right positions.
RESOLVED [V]: section 3.2 renamed to B; consistent with the proven frame format
(C=15, B=14, station=13:8, E=7, K=6, M=5, S=4).
3. Echo-single payload ... 00 0C 00 01 FF FF reads as cmd 0x000C + pattern NUMBER 0x0001
+ pattern 0xFFFF (reply fields in 3.4 are "Pattern number","Pattern"), not
"word count + data word" as section 1's table says. Align the two tables after
byte-checking the builder at ram:cf5a.
RESOLVED [V]: caller at ram:c84c-c85a passes an incrementing counter (pattern
number) + pattern word into otp_build_echo_single_word_cmd0C. Section 1 table fixed.
4c. Station-number / LIST-HARDWARE-CONFIGURATION carve (added 2026-07-18)¶
Full answer in CARVE-ANSWER-OCTOBUS-STATION-NUMBER-2026-07-18.md (fresh whole-binary disassembly; file word offset = VA - 0x6B00, validated against the d080/d3cb constants above). Headlines [all V]:
- Own station number = INPUT STATUS (+2) bits 13:8, a STATIC thumbwheel readback (ram:d704: read +2, AND 0x3F00, SHR 8), read right after input master-clear and BEFORE any transmit; stored to ctx[6] (the 0x7198 table). +2 bits 5:4 = Speed -> ctx[7] (ram:d715, mask 0x0030).
- cmd_list_hardware_configuration @ 7b24 runs ram:7242 with phase args 0/1/2 (jump table ram:731e): phase 0 = controller presence (ctx[1]), phase 1 = ram:c1f2 station read + dest-0 self-send cross-check (frame word 0x0000; compares pre-transmit ctx[6] vs received +0 bits 13:8; reporter ram:b0a3), phase 2 = ram:c4f3/c479 presence-bitmap scan into ctx[10..13] (probes every station EXCEPT its own with C-frame 0x8000|st<<8|0xA2; present = +6 bit6 NP clear, ram:d56a).
- "Clear Device" verify (ram:c180): output +7:=4 then 0x50, poll +6 until (s AND 0x01FD)==0x08; input +3:=020B, poll +2 until (s AND 0x3F3D) == ctx[6]<<8 | ctx[7]<<4 | 0x04 - i.e. the +2 station field MUST be valid on an EMPTY FIFO.
- Reply/transfer detection everywhere = POLL of status bit 3 (<=100 iterations, ram:d591); interrupts (idents 40B/41B -> flags [0x71df]/[0x71e0], code [0x71e2]) are enabled and cross-checked as DIAGNOSTICS only (ram:d5b8/c0a9).
- Full +2 and +6 bit maps from TPE's own DECODE-STATUS-REGISTER strings, including +6 bit 15 = Master (section 3.3 of the answer doc).
- Refinement to section 3.5's table: reporter b526's own string is "The Octobus station number in the Test Protocol response message is not equal the station number in the Octobus receive data register" (~ram:b5c7) - it is the reply-word-2 vs frame-source check, more specific than the generic "not expected station family" label above.
- SINTRAN side: OCSTART never reads a station number (presence = IOX-error trap on HDEV+2; 020B to +3 and +7); ND-5000 stations are CONSTANTS (CH5CPUPRESENT: 5STATION := ASTAT 070B + cpu index; kicks CMMACLE/CMACONT to that station via IOX 100405).
5. Emulator context (why this matters)¶
IMPLEMENTED 2026-07-16 in RetroCore (E:\Dev\Repos\Ronny\RetroCore\Emulated.HW\ND\CPU\NDBUS\, uncommitted):
OctobusStationBase.SendMultibyteMessage(NDBusOctobus.cs) - the verified envelope of section 3.1, one fabric frame per byte, source station in bits 13:8 of every delivered frame.OctobusND5000Station.AnswerTestProtocolMessage+BuildTestProtocolReply- a GENERIC responder per the section 3.6 recipe: parses any OMD-0 message, checks the 71C7 magic, and COMPUTES the reply (present-station list from the live fabric registry, echoes from the actual request words, module type 3 = ACCP; unknown commands get status 2, unknown register functions status 1). Placeholder identity values (processor type 68000, OPCOM version "EMU0", test version 1) are settable properties, each marked [UNCERTAIN] pending a real-ACCP capture.NDBusOctobusbusy-retry queue - the 136-frame present-stations reply overflows the 16-word receive FIFO; frames now park receiver-side and land as the FIFO drains, modelling the sender's hardware retry after Ack=10 (destination busy).- REMOVED the CONFIG-era hack that forced input ReadyForTransfer after every +5 command write: RFT is FIFO status only (the verified interrupt model); the stale RFT made a receiver read garbage when a request got NO reply.
Unit tests: 8 new OMD-0 responder tests (identify single-reply rule, present-stations through the busy-retry path, echo single/multi, module type, stateful register write + status-1 reject, status-2 unknown command, silent non-magic) in Emulated.Tests.ND100\ControllerOctobus\OctobusND5000Tests.cs. Octobus suite 60/60 green, full ND100 suite 289/289 green. NEXT: rerun TPE OCTOBUS B00 tests 4-6 and CONFIGURATION D05 live as the oracle (watch specifically for any CONFIG regression from the RFT-hack removal).
Tests 1-3 pass live; CONFIGURATION D05 reports NO ERRORS (idents 40B/41B level 13, event-latched one-shot interrupts) - see OCTOBUS-ND100-ND5000-REFERENCE.md.