Files
sibo-playground/code/inventory/CONTINUATION.md

6.4 KiB

Finishing the Workabout MX scanner — on-device continuation guide

This document describes exactly how to continue from the current findings to a fully working scanner client, using on-device debugging. Read SCANNER-API.md first for the findings this builds on.

Where we are

Established (see SCANNER-API.md for detail):

  • The integral laser is driven as an OO library object in SCANNER.DYL (category token seen in ROM: oscanner), not by raw device I/O.
  • The app path is the standard OLIB one: p_getlibhp_newsend/f_newsend (create + init) → p_send (configure / trigger / read).
  • Internals confirmed: WL2:D is the driver's channel; control ops 6 then 7 (p_iow(chan,6), p_iow(chan,7)) fire the laser to a good decode (green LED) on the physical device.
  • The decompiled read (FUN_858d) creates the object via LIBMANAGER (NMLIBCREATE/NMLIBCREATEBYHANDLE), sets up a buffer, then reads; the decode lands CR/LF-terminated (default Symbol2 postamble).

Missing, and why on-device: the message ordinals and parameter structs for the scanner's methods. OLIB assigns ordinals dynamically from the whole class hierarchy (including base classes in olib/hwim), so they cannot be read reliably from a static dump — but they resolve at runtime, where the debugger can capture them. MAME cannot inject a barcode, so validation must be on hardware anyway.

What to capture

For a working client you need five things:

  1. The scanner category (confirm the name/id for p_getlibh — candidate oscanner).
  2. The scanner class created by the app.
  3. The message ordinals for: init, set-parameters, enable/trigger, read.
  4. The parameter/result structs each message takes (esp. the read result buffer and its length).
  5. The exact call sequence the working app uses.

Tooling: the SIBO Debugger

The SIBO Debugger (see manual 2-03/2-04, "The SIBO Debugger") supports:

  • Remote debugging: development PC connected to the Workabout by a serial cable; debug up to 8 processes.
  • Breakpoints in dynamic libraries / shared code — required to break inside SCANNER.DYL.
  • Single-step, trace, register and memory display.

Build your own code for source-level debugging with:

#pragma debug(vid=>full)     /* in the .pr / source */

and produce the .sym/.dbd symbol files with EMAKE.

Set-up

  1. Connect the PC to the Workabout with a serial cable (PC serial ↔ Workabout RS-232 port — note this is a different port from the barcode TTY:D).
  2. Start the debugger on the PC and connect to the remote (Workabout) target (Local/Remote CPU menu → Connect to Remote).
  3. Have the Workabout ready to run either the ROM DEMMAN.APP/SCANAPP (for Procedure A) or your test harness (Procedure B).

Goal: watch DEMMAN/SCANAPP drive the real scanner and record the ordinals.

  1. On the Workabout, start DEMMAN and enter its Scanner (Barcode) demo.
  2. From the debugger, attach to that process and set breakpoints on the OLIB dispatch path so you catch the object creation and messages:
    • LIBMANAGER (INT 0x84) — sub-functions NMLIBCREATE (AH=5), NMLIBCREATEBYHANDLE (AH=6): captures the category/class and the object handle.
    • MESSMANAGER (INT 0x83) — NMMESSSEND and the send/receive variants: captures each message ordinal and its argument pointer.
    • Optionally the SCANNER.DYL method handlers we identified (init dispatch, trigger) as cross-checks.
  3. For each captured call record: AH, the category/class in registers, the message ordinal, and the pointed-to argument struct (dump memory at the pointer). For the read, note where the decoded bytes land and the returned length.
  4. Trigger a real scan and record the read message and its result buffer.

Result: the exact category, class, ordinals, param structs, and sequence.

Procedure B — iterate a C test harness

Once Procedure A gives the ordinals (or to trial them), build a small OO client:

#include <plib.h>
#include <p_object.h>

/* Fill these from Procedure A (placeholders until captured): */
#define SCAN_CATEGORY   /* category id/handle for "oscanner" via p_getcat/p_getlibh */
#define C_SCANNER       /* the scanner class */
#define O_SCAN_INIT     /* init message ordinal   */
#define O_SCAN_PARAMS   /* set-parameters ordinal */
#define O_SCAN_READ     /* read-a-barcode ordinal */

GLDEF_C INT main(VOID)
{
    VOID *lib, *scanner;
    /* result/param structs per Procedure A */
    lib = p_getlibh(SCAN_CATEGORY);
    scanner = p_newsend(SCAN_CATEGORY, C_SCANNER, O_SCAN_INIT, /*&initargs*/ 0);
    /* configure Symbol2 + the default 11-byte param block:
       04 3f 01 15 06 04 1e 80 0d 0a 06  (CR/LF postamble) */
    p_send3(scanner, O_SCAN_PARAMS, /*&params*/ 0);
    for (;;) {
        /* send the read message; result is the decoded barcode, CR/LF-terminated */
        p_send3(scanner, O_SCAN_READ, /*&result*/ 0);
        /* display result ... */
    }
}

Build with #pragma debug(vid=>full), run under the debugger, single-step the sends, and inspect return values / the result buffer. Adjust ordinals/structs until a real scan returns the UPC.

Turning captures into the shipped reader

  • Replace code/inventory/bcode.c with the OO client: bcodeOpen does p_getlibh + p_newsend(init) + params; bcodeRead does p_send(read) and returns the decoded string (strip the CR/LF postamble; strip any preamble byte 0x80 if present).
  • upc.c (UPC-A check-digit validation) is unchanged and already correct.
  • Cross-check the captured ordinals against the decompiled handlers in SCANNER-API.md (FUN_7ae3 init dispatch — decoder type at [channel+10], case 4 = Symbol; FUN_7aa4 trigger = the 6/7 ops).

Validation

  • A valid UPC-A scan should return 12 digits (plus any pre/postamble), and upcIsValid should pass. The default postamble is CR LF (0x0d 0x0a).
  • Confirm repeated scans and clean shutdown (p_send a destroy/close message, then p_close any channel).

Reproduce the RE environment (for further static/dynamic work)

See ../mx-re/toolchain-and-plan.md: MAME psionwamx (live trace/dasm), radare2 (static), and Ghidra headless (decompilation of the DYLs). These remain useful for reading further SCANNER.DYL methods, but the ordinals themselves come from the on-device debugger as above.