into the same FIFO--
## Why XyWrite Gets Double Keystrokes
XyWrite does not use INT 16h (BIOS keyboard) or INT 21h (DOS keyboard) in the
conventional way most DOS programs do. It reads the keyboard at a lower level,
using hardware interrupts and, critically, **INT 15h function 4Fh** — the
keyboard intercept. This is a hook point where a program can intercept raw
scan codes before they reach the BIOS keyboard buffer.
When XyWrite is running, itterrupt) and/or monitors input through INT 15h/4Fh. msdos-player
does not implement INT 15h function 4Fh at all (it is absent from the
supported calls list and from the source). This means XyWrite's hook is never
invoked correctly.
The likely result: XyWrite issues its own low-level read for the keystroke
*and* the emulator's normal BIOS path delivers the same keystroke again
througho
mechanism for XyWrite to signal "I've consumed this keystroke, discard it" —
the key is processed twice.
Additionally, the dual-path architecture (ReadConsoleInput AND _getch) means
that under certain timing conditions, even without XyWrite's low-level hooks,
a keystroke entering via one path could be visible to the other path before
being cleared from the Windows input queue, potentially causing double reads.
---
## The Missing Piece: INT 15h Function 4Fh
On real DOS hardware, when the keyboard controller fires an interrupt, the
following chain occurs:
1. INT 9h fires (hardware keyboard interrupt)
2. The INT 9h handler calls INT 15h, AH=4Fh, passing the raw scan code in AL
3. If a program has hooked INT 15h/4Fh, it can examine or modify the scan code
4. **If the hook clears the carry flag, the BIOS discards the keystroke** —
the hook has "consumed" it
5. If carry is set (or no hook is present), the BIOS adds the key to its buffer
XyWrite hooks INT 15h/4Fh to intercept keystrokes before they reach the BIOS
buffer. In msdos-player, this interrupt hook mechanism is not implemented, so:
- XyWrite installs its INT 15h/4Fh hook but the hook is never called
- The emulator delivers the key through the normal BIOS buffer anyway
- XyWrite, expecting to have intercepted the key, also readse key is processed twice
---
## What Wouldinimum viable fix
Implement INT 15h function 4Fh in the keyboard interrupt chain.
In `msdos.cpp`, within the keyboarde_input()`, where each key event is processed after the
`ReadConsoleInputA()` call), before placing the key into `key_buf_char` /
`key_buf_scan`, the code should:
1. Place the scan code in CPU register AL
2. Set the carry flag
3. Call the INT 15h vector (i.e., invoke whatever handler the DOS program has
installed at interrupt vector 15h) with AH=4Fh
4. Check the carry flag after the call returns
5. **Only if carry is still set**, proceed to add the key to the buffer
6. If carry was cleared, discard the key (the program has consumed it)
This is the standard keyboard intercept contract. XyWrite's hook would clear
carry to signal it has handled the key, and the BIOS-level buffer would then
not receive a duplicate.
### Secondary concern
The dual-path architecture (ReadConsoleInput + _getch)v>keyboard usage patterns it may activate simultaneously with the main path.
Once INT 15h/4Fh is implemented, it is possible the dual-path issue becomes
moot (since XyWrite would consume keys before they reach either buffer), but
it may need separate attention if problems persist.
---
##b>Implementing INT 15h/4Fh requires calling back into the emulated CPU to
execute the program's installed interrupt handler —ine, and resume. msdos-player already does this for other
interrupts (INT 8h timer, for example), so the infrastructure exists. The
change would follow the pattern of those existing interrupt.
This is a moderate-difficulty fix for someone familiar with the codebase —
probably a day's work for a developer who knows DOS interrupt mechanics and
can read the existing interrupt dispatch code in msdos.cpp for the pattern to
follow.
---
## Relevant Source Locations in msdos.cpp (cracyc fork, branch vt)
| Location | Description |
|---|---|
| ~line 5400–5700 | `update_console_input()` — ReadConsoleInput path, where keys enter the buffer |
| ~line 7395–7470 | `msdos_getch_ex()` — INT 21h keyboard path, containing the _getch fallback` — pulls keys from FIFO into key_code/key_recv |
| ~line 12805–12860 | `pcbios_int_16h_00h()` and its thread — INT 16h "get keystroke" handler |
| ~line 10879 | INT 15h dispatch switch — where 4Fh handling would be added |
The INT 15h supported-calls list in the readme does **not** include AH=4Fh,
confirmingb>
3420817045yqt12479" class="ydpc5ef4220yiv3420817045yqt7917550298">
ans-serif;font-size:13px;color:#26282a;">
rd Mendelson <dmarc-noreply@xxxxxxxxxxxxx> wrote:
is list who uses Claude Code.
An open-source project on GitHub called msdos-player is here:
It lets you run some 16-bit DOS programs without a separate emulator,
and lets you combine the msdos.exe program with the 16-bit app so that
you have a single program than runs in Windows 11. It doesn't need a
separate emulator like vDosPlus or DOSBox-X.
Claude Code fixed various bugs in the program for me, and nowrd converters, the WordPerfect editor, the WP
printing-definition programs, and WordPerfect itself ase are limits to what it can do; no shelling out to
DOS, no graphic mode, but basic editing works veryClaude Code can suggest ways to fix the limitations.
The source code for the Claude-Code extended version is here:
And an executable is here:
The bad news is that when I try to run Xy4 wiith msdos.exe, every
keystroke gets entered twice, so the command line reads "ccaa" instead
of "ca". My guess is that Claude Code can fix this, and if anyone on
this list is adept with Claude Code, it might be worth trying. I don't
have any use for a native version of XyWrite, so Iand money on it, but I hope this might be a very rewarding exercise
to anyone who might want to use the results.