diff --git a/code/inventory/CONTINUATION.md b/code/inventory/CONTINUATION.md new file mode 100644 index 0000000..22d8d64 --- /dev/null +++ b/code/inventory/CONTINUATION.md @@ -0,0 +1,144 @@ +# 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_getlibh` → `p_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). + +## Procedure A — trace the working ROM app (recommended first) + +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: + +```c +#include +#include + +/* 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, /*¶ms*/ 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.