Mirrors the existing x64 job, including its only: [edge, release] rule.
The libretro buildbot has no Linux aarch64 build of VirtualXT today, while
it has 160 other cores; nothing in the tree needs changing for it.
cord8/cord16 unified into one CORD_DEFINE X-macro template with two
instantiations; the rewrite also eliminates the do-nothing else
branches and redundant temporaries from the CORD loops.
negate8/negate16 unified into cord_negate() (width-mask
parameterized); POSTIDIV factored into v20_divide_error_flags() and
postidiv_fix_signs(). The four DIV/IDIV exec handlers are generated
from EXEC_DIV_DEFINE/EXEC_IDIV_DEFINE templates. Magic cycle costs
(80/101/144/165) are named constants.
div.c: 477 -> 291 lines. Verified flag- and cycle-exact against all
635 MOO test files (8088 + V20).
CPU: V20 bit manipulation consolidated from 16 templated handlers into
one parameterized do_bitop() with 16 one-line thunks and a 16-entry
dispatch table.
Video: new shared modules/video_common.h -- crtc_cursor_t embedded in
CGA/EGA/HGC, crtc_cursor_write(), crtc_blink_tick(),
video_attach_frontend(); EGA write modes 0-2 share ega_write_plane().
Tests: setup_pic_machine() fixture collapses 11 test_pic.c sites;
make_ems/make_rtc wrappers inlined.
Frontend: retro_load_game() split into setup_host_interfaces(),
register_disk_control(), and setup_content().
Debug server: table-driven command dispatch with exact cmd matching
(was a 24-branch substring if-chain); run_cycles() reuses
vxt_machine_step(); JSON list handlers use first-flags.
Consistency: unhandled I/O port reads return 0xFF (open bus) across
PIC/PPI/CGA/HGC/EGA; VXT_FLAG_8088_RESERVED renamed to
VXT_FLAG_RESERVED_HIGH; PPI keyboard queue converted to a ring buffer;
machine_step accumulation contract documented; stale comments
refreshed (Odin references, EGA DIP default, cpu_cycles semantics).
Bug fixes:
- pic: honor auto-EOI mode (ICW4 bit 1); ISR bit now only set when
auto-EOI is disabled (previously set unconditionally)
- spec: correct port 0xB0-in polarity (0x00 = hard disk, 0xFF = floppy)
to match both the Odin and C implementations
- debug: gate cga_regs/cga_vram handlers on the video adapter actually
being CGA (returned garbage when EGA/HGC was active)
Dead code removal:
- unused public APIs: vxt_cpu_flush_prefetch, vxt_bus_set_pic,
vxt_uart_registers, vxt_uart_set_error, vxt_disk_bootstrap
- write-only CPU instruction-stream fields + MAX_INSTRUCTION_SIZE
- unused macros VXT_CPU_USE_PREFETCH, MBOX_TOTAL_SIZE
- debug server: g_active_server, single_step, last_segments, SEG_TAG_*
- UART iir field, always-false ega_config stub, empty destroy stubs
(joystick, UART, mouse), exec_wait/exec_esc folded into exec_nop
- frontend: BIOS_ROM_BASE/VXTX_ROM_BASE, mixer HOSTFS_DURATION/
HOSTFS_VOLUME/seek_volume, unreachable clamps, ini_parse return value
- misc dead stores/clamps (hostfs, hgc), dangling comment (cpu.c)
Makefile: drop 12 redundant pattern rules, replace dead check-circle
test with a real compiler check, drop duplicated distclean line,
complete .PHONY, collapse flag lists, develop honors LIBRETRO_OUT.
STATUS.md: correct stale module test counts (110 -> 119).
Update display_version in virtualxt_libretro.info. The version flows
through tools/getversion/export.sh to:
- src/frontend/core.c (library_version reported to libretro frontends)
- tools/package/itch/push-{web,rpi,libretro}.sh (butler --userversion)
- the bare-metal Pi and web builds via the top-level Makefile.
Web UI: video button defaulted to EGA in HTML/JS while the runtime
defaults to CGA, leaving the label out of sync at page load (first click
was a silent no-op). Fixed by setting `videoState = 'CGA'`, the button
label to `Video: CGA`, and reordering the cycle to `['CGA', 'HGC', 'EGA']`.
Added a `Turbo` toggle (`Turbo: 4.77` default / `Turbo: 10.0`)
live-applied via new `wasm_set_turbo` and the existing `?cpu=4.77` /
`?cpu=10` URL parameter.
Libretro: default CPU speed was 10 MHz (turbo enabled). Now defaults to
4.77 MHz (turbo disabled) to match the web build. Descriptor at
`core.c:743` swapped to `disabled|enabled`; fall-through default at
`core.c:1399` set to `CPU_FREQ_477`. First-time installs see the new
default; existing users keep their saved option. The RPi bare-metal
build inherits the new default because its env callback returns false
for `GET_VARIABLE`.
docs/STATUS.md: added entries and consolidated the Boot Diagnostics /
Gotchas / EGA Notes / Known Limitations sections into a unified Notes
section.
KeyStatusHandlerRaw in the Circle baremetal kernel dropped the
ucModifiers byte and sized the keyboard state arrays to 0x100 entries,
too small for libretro modifier keycodes (RETROK_LSHIFT=304, etc.).
As a result, holding Shift (or Ctrl/Alt) produced no XT scan code in
the shared frontend, so shifted key combinations never worked.
- Translate ucModifiers bits to RETROK_LCTRL/LSHIFT/LALT/RCTRL/RSHIFT/RALT
so the existing RETROK_LSHIFT -> 0x2A / RETROK_RSHIFT -> 0x36 mapping
in src/frontend/core.c fires correctly.
- Resize key_states/key_states_current and the dispatch / input_state
loops from 0x100 to RETROK_LAST so modifier and other high keycodes
fit and are not silently truncated.
- Add bounds guard for mapped raw keycodes.
Also stages long-pending packaging fixes in tools/package/rpi/package-rpi.sh
(Pi 4/5 KERNEL_MAX_SIZE bump, Pi 4 family DTBs, disable_splash, [pi400]
and [cm4] stanzas) and updates docs/STATUS.md.
Verified with 'make rpi' for Pi 3/4/5 and the new kernel images
written to /dev/sda1.
Replaces the synchronous Core 0 bridge with the guest-visible async
polling protocol that was proven working on Pi 3 USB floppy boot.
Core changes:
- Disk controller now handles VXT_DISK_NOT_READY from frontends
- Pending operation state tracks multi-sector transfers
- VXTX BIOS polls port 0xB1 and reads AL/AH from 0xB2/0xB3
- Added floppy_timing / harddrive_timing INI toggles
Circle frontend:
- vfs_read/vfs_write return VXT_DISK_NOT_READY for USB handles
- Core 0 processes blocking USB jobs via usb_disk_process()
- Core 1 BIOS polling loop runs without stalls
Docs:
- Added async completion protocol to SPEC.md
- Updated STATUS.md for verified Pi 3 USB floppy boot
Default Pi config remains safe (no USB passthrough enabled).
Implements lazy-resolution USB block device VFS layer that allows
USB floppy drives and USB sticks to be mounted as raw disk images
via machine.ini paths like A=usbfd: or C=umsd1:.
Key design decisions:
- No startup delay: USB devices are resolved on first I/O, allowing
POST to begin immediately while USB enumeration happens in background
- Supports both UFI floppy (usbfd:, ufd1:) and bulk-only mass storage
(usbhd:, umsd1:) device types
- Thread-safe: CSpinLock protects USB handle state across emulation
and USB interrupt threads
- Zero emulator core changes: all logic in frontend VFS layer
- Handles hot-plug gracefully: device re-resolved on each I/O call
Fixes:
- Correctly uses TRUE for bBlockDevice parameter in GetDevice() calls
(was FALSE, causing device lookup to always fail)
- Removes problematic pre-boot USB polling that broke keyboard enumeration
Tested on Raspberry Pi 3 Model B+ with USB floppy drive.
On POSTIDIV divide-error, V20 microcode pushes FLAGS where PSZ are
derived from the unsigned quotient byte/word and CF/AF/OF are cleared,
distinct from 8088 which reflects the last CORD subtract. Verified
against hardware-captured data: all 20000 IDIV tests now pass.
V20 MOO suite: 318/320 -> 320/320. CI workaround removed.
Changes:
- Fix V20 AAM divide-by-zero: V20 does not trigger INT 0 for AAM with
base=0, unlike 8088. Fixes v20/D4.MOO.
- Fix fetch.py sparse-checkout re-application on existing repos.
- Update CI to run module + 8088 tests strictly, but allow V20 test
failures with a warning. This makes CI green while we fix the
remaining 2 V20 IDIV failures (F6.7, F7.7).
- Update STATUS.md to document V20 test status and known failures.
Previously the Docker image baked in .MOO test files and the CI copied
them into the workspace before running make testdata. This caused stale
test data on the CI when the test repos were updated, because fetch.py
skipped existing .MOO files without checking if the source was newer.
Changes:
- Dockerfile: replace COPY of .MOO/masks/metadata with sparse git clones
of 8088 and v20 repos into /opt/testdata-cache/
- fetch.py: accept TESTDATA_CACHE env var for git cache dir, and add
mtime check so existing .MOO files are re-extracted when .gz is newer
- .woodpecker/test.yaml: remove file-copy cache step, set TESTDATA_CACHE
env var on the testdata step instead
This ensures CI always gets fresh test data via git pull + re-extraction
while keeping the fast path of not re-downloading unchanged repos.
Pi 3 bare-metal improvements:
- Offload CRT filter to Core 2 via CRT_FILTER_EXTERNAL compile flag.
Core 1 queues frames; Core 2 worker (RenderThread) processes
asynchronously with 1-frame latency. Trampoline pattern in crt.c/h.
- Add 4:3 letterboxed scaling on Core 2 render thread.
HW framebuffer created once at physical display resolution
(CBcmFrameBuffer auto-detect); largest centered 4:3 rect computed,
black bars cleared once, nearest-neighbor scale into target rect.
- Raise KERNEL_MAX_SIZE to 0x1400000 (20 MB) across Circle Config.mk,
kernel Makefile, and package-rpi.sh to accommodate ~19 MB .bss.
- Add vxt_peripheral_print_status() in src/vxt/peripheral.c that logs
CPU type (8088/V20), frequency, installed peripherals, IO map ranges,
and memory map ranges. Called from retro_load_game() in core.c.
Refs: docs/STATUS.md
Fix four critical issues preventing VirtualXT from booting on Raspberry Pi 3B+:
1. vfs_file_load was a stub returning NULL - implement real VFS-backed
loader using peripheral arena allocator
2. setup_machine_from_ini never wired disk.file_api - refactor to two
passes (instantiate + wire APIs, then configure)
3. Missing vfs_tell in Circle kernel - null pointer crash when disk
mount tried to get file size
4. Libretro VFS seek returns position, but disk module expects POSIX
0/-1 - add adapter in disk_file_seek
Also:
- Add NO_DEFAULT_DISK and NO_SAMPLES build flags for baremetal size
- Replace GLABIOS.ROM with vxtbios.bin in raspberrypi.ini
- Add package-rpi.sh packaging script with cmdline.txt LOGFILE support
- Raise KERNEL_MAX_SIZE to 17 MB for Circle sysinit
- Clean up kernel error handling (halt() instead of error_halt)
- Update STATUS.md with known bugs and recent changes
Replace the 80186 instruction set extension with full NEC V20 support:
- Rename cpu_186.c to cpu_v20.c and add cpu_cycles.c/h for per-CPU
cycle lookup tables indexed by opcode
- Change CPU type from bitmask options to vxt_cpu_type_t enum
(VXT_CPU_8088 / VXT_CPU_V20)
- Add V20-specific instructions: TEST1/CLR1/SET1/NOT1, ROL4/ROR4,
ADD4S/SUB4S/CMP4S, INS/EXT, REPNC/REPC
- Update IDIV to ignore REP prefix quirk on V20
- Update test infrastructure and docs for V20 nomenclature
Make mul skip PSZ flags in 186 mode so the BIOS cpu_check routine
(which tests whether mul affects ZF) detects 186 mode as V20.
This is a cosmetic deviation - real 80186 mul does update PSZ,
but since VirtualXT never emulates true V20 silicon this only
affects the POST display string.
Update vxtbios string from 'V20 CPU' to '186 CPU' and add comments
explaining the V20 detection behavior used for 186 display.
Codeberg's pages-server cert renewal for app.virtualxt.org broke during
the git-pages migration (last cert 2025-09-01). With .domains in the
deployed pages branch, the pages-server 307-redirects every visitor to
the unreachable custom domain. Custom-domain access is now handled by
HTTP forwarding configured at the DNS provider (Loopia) instead.
The pages deploy script checks out the orphan 'pages' branch into the
shared workspace and never restores the original branch, removing
tools/package/itch/push-web.sh and other source files from the tree.
Running itch deploy first avoids the clobber. Also drop leftover debug
output from the itch script.
- Fix web_vfs_readdir skipping first directory entry by tracking parent node
- Add wasm_hostfs_set_launch_path to set auto-launch path without overwriting root
- Fix web_vfs_stat to return proper libretro bit flags (VFS_STAT_IS_VALID)
- Add path normalization to handle double slashes from hostfs path concatenation
- Export new wasm_hostfs_set_launch_path in Emscripten build