Files
Nonemm/docs/digital-bridge.md
ericek111 54b9181c06 Let MMTTY hold the text waiting to go out, behind a setting
MMTTY has a type-ahead buffer of its own: characters go into it, a backspace
takes back one it has not transmitted, and TxBufLen says how many are left. Used
that way it paces itself, so there is no gap between characters to tune and no
baud rate to keep in step with the engine.

EngineTypeAhead does that, and Config > Digital picks between it and the pump
that is there now. Off is still the default: three things it rests on have never
been seen with a real engine.

tools/Nonemm.EngineProbe asks the engine those three questions and writes the
answers to a file. It starts MMTTY through the bridge, pushes a message, polls
TxBufLen while it goes out, backspaces over text that has and has not been
transmitted, and logs what came back on the receive side and when.

The bridge learns one verb for it: `buffer` reads TxBufLen and answers with the
count, or -1 when the control will not say. A property the control does not know
is a log line rather than an error, since it stops nothing.

TypeAhead and EngineTypeAhead share the TransmitBuffer interface, which is what
the digital window now works through, so the window does not know which one it
has.

docs/unfinished.md states what each buffer assumes and how to run the probe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RtspmWmS7f8kUvcyaHpRWZ
2026-09-01 22:41:18 +00:00

151 lines
6.8 KiB
Markdown

# MMTTY and 2Tone through Wine
MMTTY is a Windows program with no socket, pipe or file interface. N1MM does not
talk to it directly either: it hosts `XMMT.ocx`, a 32-bit ActiveX control that
starts `MMTTY.EXE` and exchanges window messages with it. A Linux process cannot
load that control, so Nonemm runs a small Windows program under Wine —
`bridge/nonemm-mmtty-bridge.exe` — which hosts the control and passes lines over
its standard input and output.
```
Nonemm ──stdin/stdout──▶ bridge.exe (Wine) ──XMMT.ocx──▶ MMTTY.EXE / 2Tone
```
2Tone uses the same control, so it runs the same way. The only differences are
the command line and the window title, both in `MmttyOptions`.
## The line protocol
One line per message: a verb, then fields, separated by tabs. Backslash, tab,
carriage return and newline inside a field are backslash-escaped, so a field
never splits a line. `BridgeLine.cs` and `bridge/Protocol.cpp` are the two ends
of it.
Wine writes its own diagnostics to standard error, which leaves standard output
to the protocol.
To the bridge:
| Line | What the bridge does |
|---|---|
| `open <title> <port> <command line>` | sets `Title`, `ComName` and `InvokeCommand`, sets `bActive`, then posts the host window handle |
| `send <text>` | `SendString` |
| `ptt <0\|1>` | `SetMmttyPTT` |
| `post <message> <parameter>` | `PostMmttyMessage`, for everything in `MmttyMessage` |
| `buffer [property]` | reads `TxBufLen`, or the property named instead, and answers `buffer` |
| `close` | shuts the engine down and leaves the bridge running |
| `quit` | shuts the engine down and exits |
From the bridge:
| Line | Where it comes from |
|---|---|
| `ready` | the control is created and listening |
| `connected <version>` | `OnConnected`, with `verMMTTY` |
| `disconnected <status>` | `OnDisconnected`: 0 failed to close, 1 closed, 2 failed to start |
| `rx <code>` | one decoded character |
| `tx <0\|1>` | the engine started or stopped transmitting |
| `mark <hz>`, `space <hz>` | the tone pair moved |
| `switch <bits>`, `view <bits>` | AFC (4), net (8) and reverse (256), and the engine's view state |
| `buffer <n>` | how many characters the engine still has to transmit, or -1 when the control would not say |
| `log <text>`, `error <text>` | anything the bridge has to say |
`buffer` is the one thing that is asked for rather than reported: the control
has no event for the length of the transmit buffer, so it is polled. A property
name in the request is only for finding out what the control answers to; a name
it does not know fails with `DISP_E_UNKNOWNNAME`, which comes back as a `log`
line and `buffer -1` rather than an `error`, because a property the control will
not answer stops nothing.
Each of the others comes from an event of the control's own: `OnCharRcvd`,
`OnPttEvent`, `OnFreqChanged` (mark and space in one event), `OnSwitchChanged`
and `OnViewChanged`. The control raises `OnTranslateMessage` only for the
MMTTY messages it has no event for — width, resolution, thread and the like —
so the bridge does not listen to it. N1MM reads the characters the same way, in
`DigitalMultiRXWindow.XMMR_OnCharRcvd`; the `OnTranslateMessage` handler in
`DigitalInterface.cs` that maps codes 32771 to 32778 never runs with this
version of the control.
## Starting the engine
The order is N1MM's, from `DigitalInterface.cs`:
1. Read `Mmtty.INI` beside the program. `[Define] PTT` is the serial port, and
`AFC`, `TxNet` and `Rev` are the switch word the engine starts with. Without
them, the first toggle moves the wrong way.
2. Build the command line: the quoted program, the window size (`-r` normal,
`-t` small, `-s` medium 1, `-u` medium 2), `-a` when the window is not on
top, and `-Z` for MMTTY but not for 2Tone.
3. `open`. The control appends `-h<handle>` of its own, which is how the engine
finds its way back.
4. An engine that answers `disconnected 2` is started again, up to ten times.
That is N1MM's retry, and it is needed: MMTTY does not always come up first
time.
## What the control needs that is not obvious
Three things cost a day between them, so they are written down:
- **`IPersistStreamInit::InitNew` before anything else.** The control is MFC
based, and until it is initialised every dispatch call returns `E_UNEXPECTED`
— property gets included. Creating and activating it is not enough.
- **A real window.** In-place activation needs a parent window, so the bridge
needs an X display even though it shows nothing.
- **The control's own shutdown blocks.** `IOleObject::Close` and setting
`bActive` to false wait for the engine while holding the thread that would
have to pump the messages, so neither returns. The bridge posts the shutdown
message, closes the engine's windows, and kills the process if it is still
there five seconds later.
## Setting it up
Config > Digital has a Register button. It does what the next paragraph
describes, in the prefix named in the same dialog, and writes the result under
the button. Press it once, before the first start.
The steps are in `WinePrefixSetup`. A prefix that is not there yet is made with
`wineboot -u` and `WINEARCH=win32`, because the control is 32 bit. The control
is then copied into the Windows folder and registered:
```sh
export WINEPREFIX=~/.wine-nonemm WINEARCH=win32
wineboot -u
cp bridge/XMMT.ocx "$WINEPREFIX/drive_c/windows/system32/"
wine regsvr32 'C:\windows\system32\XMMT.ocx'
```
A 64-bit prefix keeps 32-bit code in `syswow64` and has a second `regsvr32.exe`
there. That is the one to register the control with: the 64-bit one cannot load
it.
`XMMT.ocx` ships with N1MM Logger+ and is distributed with Nonemm in the
`bridge` folder. MMTTY itself is downloaded separately and can live anywhere in
the prefix.
Build the bridge with the mingw-w64 cross compiler:
```sh
make -C bridge
```
## Where the program looks
MMTTY, the bridge and `XMMT.ocx` are distributed with Nonemm, in `mmtty` and
`bridge` folders beside the program, and the build copies them there from the
working copy. Config > Digital starts at those three paths, so an operator who
runs the distributed copy has nothing to fill in. An engine kept somewhere else, or
2Tone in place of MMTTY, is browsed for in the same dialog. Neither program is
in the repository.
The bridge keeps its own window hidden. Started with `--show` it shows it, which
is the way to see what the control is doing.
## What has not been tested
The bridge has been run against MMTTY 1.70 under Wine 10: it starts the engine,
connects, reports the version, takes text, opens MMTTY's setup window, moves
the tone pair and reports `mark`, `space`, `switch`, `view` and `tx` back. It
has never been run with a sound card, so nothing has been decoded and no `rx`
line has ever come from a real signal. FSK keying through EXTFSK and PTT on a
serial port have not been tried either, and neither has 2Tone.