Files
Andreas T Jonsson f6f75112d7 refactor: cleanup phase 4 -- width-generic div.c
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).
2026-07-19 09:34:48 +02:00

16 KiB
Raw Permalink Blame History

VirtualXT -- Status

Build Infrastructure

  • Top-level Makefile / build system for C11
  • Freestanding compilation support (-ffreestanding, -fPIC)
  • make testdata -- Python script downloads+decompresses 8088 MOO test files
  • make test -- runs both module and CPU test suites in sequence
  • make test-cpu -- runs MOO opcode tests (8088 + V20, 348 files total)
  • make test-modules -- runs module/peripheral tests (119 tests)
  • make distclean -- removes build artifacts and test data
  • make bios-data -- generates C headers from BIOS ROM binaries and disk image
  • make vxtbios -- assembles bios/vxtbios.bin from bios/vxtbios.asm (requires nasm)
  • make vxtx -- assembles bios/vxtx.bin from bios/vxtx.asm (requires nasm)
  • Woodpecker CI -- separate "module tests" and "cpu tests" pipeline steps
  • Replace/update legacy Odin-based libretro packaging Makefile
  • make object -- release-mode single-object libretro build for web/Emscripten
  • Bare-metal Raspberry Pi packaging script -- tools/package/rpi/package-rpi.sh builds Pi 3/4/5 boot directories from Circle + VirtualXT artifacts
  • Libretro GitLab CI matrix matches ../virtualxt_C platform coverage
  • Woodpecker libretro/itch bundle pipeline with CI-built dist/libretro artifacts staged into package/virtualxt
  • Build flags for baremetal size reduction: NO_DEFAULT_DISK=1, NO_SAMPLES=1. The make rpi target sets both.
  • Baremetal USB floppy pass-through. Circle bridges USB vfs_read/vfs_write through a Core 0 job; the VXTX BIOS polls the disk controller while pending USB I/O completes. Verified on Pi 3 with USB floppy boot.

Core Systems

  • Bus -- flat memory map (65,536 x 16-byte pages) and I/O map (65,536 ports)
  • Default peripheral (index 0: 1 MB RAM, open-bus I/O)
  • Peripheral interface -- vtable struct, lifecycle callbacks, class tags
  • Peripheral registration (up to 64 devices, index-based dispatch)
  • Peripheral arena allocator -- 8 MB bump allocator
  • Machine -- step loop, timer dispatch, reset
  • Timer system -- cycle-based accumulation, microsecond intervals

CPU (Intel 8088 / NEC V20)

  • Full 8088 instruction set -- 315/315 MOO opcode test files pass, 0 failures
  • NEC V20 instruction set -- 320/320 MOO opcode test files pass, 0 failures (PUSHA, POPA, BOUND, PUSH imm, IMUL r,ew,imm, INSB/INSW/OUTSB/OUTSW, ENTER/LEAVE, shift by immediate, REPNC/REPC, bit manipulation opcodes. Prefetch queue stays at 4 bytes.)
  • Decode-dispatch loop (prepare, prefix, decode, execute, REP, interrupt check)
  • Cycle accounting (EU/BIU split, bus cycle charging, 81.5% average accuracy)
  • Prefetch queue (4-byte circular buffer, drain/fill/flush)
  • ModR/M decoding and effective address calculation (8 addressing modes)
  • All standard opcodes: ALU, MOV, PUSH/POP, Jcc, LOOP, CALL/JMP/RET, string ops, shifts/rotates, MUL/IMUL, IN/OUT, INT/IRET, BCD adjust, XLAT, PUSHF/POPF
  • DIV/IDIV -- CORD shift-and-subtract algorithm for hardware-accurate flags
  • All undocumented opcodes: SALC, Jcc aliases (0x60-6F), RET aliases (0xC0/C1/C8/C9), ALU imm alias (0x82), SETMO, FE /2-7 aliases, LOCK alias (0xF1), POP CS (0x0F)
  • FPU ESC (0xD8-0xDF) -- consume ModR/M, NOP (no FPU installed)

CPU split: cpu.c (~2450 lines), alu.c (~390 lines), div.c (~290 lines), cpu_internal.h (~172 lines). Total ~3300 lines.

Peripheral Modules

  • PIC -- Intel 8259 interrupt controller (pic.h/pic.c)
  • PIT -- Intel 8253 programmable interval timer (pit.h/pit.c)
  • PPI -- Intel 8255 programmable peripheral interface (ppi.h/ppi.c)
  • DMA -- Intel 8237 DMA controller (dma.h/dma.c)
  • ROM -- read-only memory-mapped peripheral (rom.h/rom.c)
  • Disk -- BIOS-level INT 0x13/0x19 controller, I/O ports 0xB0-0xB3 (disk.h/disk.c) Blocking reads/writes with simulated seek and sector delays. Frontends may return VXT_DISK_NOT_READY to complete disk I/O through the VXTX polling protocol. [disk] floppy_timing=false / harddrive_timing=false disables emulated timing per drive type.
  • CGA -- Color Graphics Adapter, text + graphics modes (cga.h/cga.c)
  • EGA -- Enhanced Graphics Adapter, planar graphics + programmable palette (ega.h/ega.c)
  • HGC -- Hercules Graphics Card, monochrome text + graphics (hgc.h/hgc.c)
  • Mouse -- Serial Microsoft Mouse on COM1, UART consumer (mouse.h/mouse.c)
  • UART -- National Semiconductor 8250, standalone peripheral (uart.h/uart.c)
  • HostFS -- Fast host filesystem via direct I/O ports 0xB4-0xB5 (hostfs.h/hostfs.c)
  • EMS -- Lo-tech 2MB EMS Board, page-frame memory expansion (ems.h/ems.c)
  • RTC -- Motorola MC146818 real-time clock, passive time consumer driven by frontend via vxt_rtc_set_time(). NO_RTC=1 disables.
  • Joystick -- IBM Game Control Adapter (joystick.h/joystick.c), I/O port 0x201, one-shot RC timing model for 4 axes + 4 buttons. Reads host input via libretro analog/joypad callbacks at ~60 Hz. Two-joystick / two-button layout.
  • RAM module (separate from default bus peripheral)

Test Infrastructure

  • MOO binary format parser (v1) with CYCL cycle calibration
  • Test harness, CLI runner (./test-runner), data fetch script
  • Module test framework + suites: bus (13), PIC (12), PIT (14), DMA (18), ROM (7), PPI (16), EMS (9), RTC (25), debug (5); shared pic_init_xt() helper

Frontend (Libretro)

  • Libretro API integration -- all 25 required exports (~945 lines)

Frontend (Web)

  • Standalone web frontend in tools/web/ using Emscripten; compiles src/frontend/core.c unchanged with C glue acting as libretro host.
  • HTML5 Canvas 2D rendering, Web Audio (44100 Hz stereo ring buffer), DOM keyboard mapped to XT scan codes, pointer-lock mouse.
  • Memory-backed VFS; disk upload accepts .img, .zip, .exe, .com with auto-detect. Floppy hot-swap; HD load triggers full reset.
  • ZIP extraction (fflate) and folder drag-and-drop auto-mount as read-only HostFS with single-EXE/COM auto-launch and tree flattening.
  • Persistent disk storage -- sector-level localStorage keyed by CRC32. ?storage=0 disables, ?storage=clear wipes.
  • XRGB8888 framebuffer up to 640x700, 4:3 aspect, dynamic resolution.
  • CRT filter via virtualxt_crt_filter core option: 2x vertical upscale, scanlines, horizontal softening, phosphor persistence, vignette.
  • Frontend audio mixer -- artificial PC effects (seek samples, procedural fan/read/write/HostFS noise) toggled via virtualxt_pc_noise + virtualxt_noise_volume.
  • Core options: boot_priority, cpu_turbo, video, crt_filter, hostfs, ems, rtc, etc. Web defaults: CGA, 4.77 MHz (turbo off), HostFS on.
  • ROM/BIOS loading -- embedded VirtualXT BIOS, vxtx, LGPL EGA BIOS; custom BIOSes loadable via INI. Embedded 10MB FreeDOS default disk.
  • Frontend fully freestanding (no libc), arena allocation, module ctor table, setup_default_machine() / setup_machine_from_ini().
  • MCP debug server -- NDJSON TCP port 12088 (develop builds), keyboard input, PNG screen capture, OpenCode bridge.
  • make web target + Woodpecker CI deploys web package.
  • File-loader VFS pass-through (ROM loading from INI file paths)
  • Save states (retro_serialize/unserialize)

Not Yet Implemented

  • AdLib / OPL2 sound
  • VGA video
  • RIFS removal -- RIFS module (rifs.h/rifs.c) fully removed. HostFS is now the sole host filesystem driver. Core option renamed to virtualxt_hostfs. All RIFS driver binaries removed from disk image.
  • GDB stub
  • COM port (host serial port pass-through)

Known Bugs

  • Raspberry Pi 3B+ bare-metal: four boot blockers identified and fixed; package at package/rpi/pi3-full/ ready for hardware testing. Fixes included rebuilding Circle SDCard/fatfs with USE_SDHOST, raising KERNEL_MAX_SIZE to 20 MB, staging vxtbios.bin/vxtx.bin/FreeDOS C.img, implementing vfs_file_load, and wiring the disk file API before configure.

  • Raspberry Pi bare-metal: Shift/Ctrl/Alt combinations did not work. KeyStatusHandlerRaw in tools/circle/kernel/kernel.cpp ignored the USB HID ucModifiers byte, and the Pi keyboard state arrays (key_states, key_states_current) and the dispatch loop were sized to 0x100, too small for libretro modifier keycodes (RETROK_LSHIFT = 304, etc.). Fix: translate ucModifiers bits to RETROK_LCTRL/LSHIFT/LALT/RCTRL/RSHIFT/RALT and resize the arrays and loops to RETROK_LAST. The shared frontend already maps RETROK_LSHIFT/RETROK_RSHIFT to XT scan codes 0x2A/0x36.

Notes

  • Boot diagnostics: kernel opens LOGFILE (from cmdline.txt, e.g. LOGFILE=virtualxt.log) on FAT before retro_init(), capturing all setup-time LOG_* output.
  • MCP debug server times out: RetroArch pauses the core when unfocused; TCP 12088 still accepts but commands don't run. Disable Pause Content When Not Active or keep the window focused.
  • EGA timing: master clock from misc_output bit 2, dot-clock divider from sequencer clocking mode bit 3.
  • HostFS file dates show as 00/00/1980 (VFS stat() returns size only).

Recent Changes

  • Cleanup phase 4 (div.c refactor, verified against all 635 MOO files): cord8/cord16 (~75 lines each) unified into one CORD_DEFINE X-macro template with two instantiations, eliminating the misleading do-nothing else-branches and redundant temporaries in the process. negate8/ negate16 unified into cord_negate() (width-mask parameterized); POSTIDIV logic factored into v20_divide_error_flags() and postidiv_fix_signs(). The four DIV/IDIV exec handlers are now generated from EXEC_DIV_DEFINE/EXEC_IDIV_DEFINE templates. Magic cycle costs (80/101/144/165) are named constants. div.c: 477 -> 291 lines.

  • Cleanup phase 3 (structural refactors, no behavior change): V20 bit manipulation (TEST1/CLR1/SET1/NOT1 × CL/imm × byte/word) consolidated from 16 templated handlers into one parameterized do_bitop() with 16 one-line thunks and a 16-entry dispatch table. New shared video header modules/video_common.h: crtc_cursor_t state embedded in all three adapters, crtc_cursor_write(), crtc_blink_tick(), and video_attach_frontend() replace per-adapter copies; EGA write modes 0-2 share ega_write_plane(). Test framework gained setup_pic_machine() fixture (11 test_pic.c sites collapsed); make_ems/make_rtc wrappers inlined. retro_load_game() split into setup_host_interfaces(), register_disk_control(), and setup_content(). Debug server command dispatch is now table-driven with exact cmd matching (was a 24-branch substring if-chain); run_cycles() reuses vxt_machine_step(); JSON list handlers use first-flags instead of magic-length comma checks. Unhandled I/O port reads now consistently 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. Stale comments refreshed (Odin references, EGA DIP default, HostFS VFS, fan "spinup ramp", cpu_cycles override semantics, machine_step accumulation contract).

  • Cleanup phase 2 (deduplication, no behavior change): unified 8088/V20 EA decode into one switch + cycle table; group-1 ALU dispatch now uses fn tables; shared update_si/update_di string-op helpers (10 sites); V20 shift-by-immediate reuses the shared do_shift_* helpers; NEG handlers reduced to single alu_sub calls. Added vxt_peripheral_zero_state() (replaces the save/zero/restore reset idiom in 10 modules), VXT_MAX_TIMERS, bus-map clear helper, merged status-print map loops. Disk: named port constants, shared floppy geometry table (also exposed as vxt_disk_size_is_floppy()), int13_ok/err/busy result helpers, sector copies via the new shared modules/guest_mem.h (also used by HostFS). HostFS: merged READFILE/WRITEFILE into hostfs_xfer(), RMDIR+DELETEFILE cases merged, hostfs_mailbox_path()/launch_substituted()/build_83_pattern() helpers, check_pattern renamed to pattern_mismatch. Video: dropped HGC font copy (uses mda_font directly), removed double VRAM masking, consolidated EGA I/O range registration, added vxt_ppi_set_video_switches(). Frontend: wire_disk_file_api()/ wire_hostfs_vfs() helpers (were pasted twice each), boot-drive detection now queries the disk module instead of re-opening the image, options refresh uses a bool-option table + get_option(), option defaults live only in static initializers, keymap unified into one table shared by scancode lookup and modifier tracking, named audio constants. Utils: added utils_strstr, utils_parse_bool, utils_fill_random (replaces 4 LCG fill copies). UART create reuses uart_reset; ROM vtable init helper; PIT tick interval is a constant; test suites use pic_init_xt(true) and VXT_CPU_8088.

  • Cleanup phase 1 (bug fixes + dead code): fixed PIC auto-EOI (ICW4 bit 1 was ignored; ISR bit now only set in non-auto-EOI mode). Corrected SPEC.md port 0xB0-in polarity to match both implementations (0x00 = hard disk, 0xFF = floppy). Debug server cga_regs/cga_vram now verify the video adapter is actually CGA. Removed 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, unused macros, dead debug-server state (g_active_server, single_step, last_segments), UART iir field, always-false ega_config stub, empty destroy stubs (joystick, UART, mouse), exec_wait/exec_esc (folded into exec_nop), dead frontend/ mixer constants (BIOS_ROM_BASE, VXTX_ROM_BASE, HOSTFS_DURATION, HOSTFS_VOLUME, seek_volume), ini_parse return value (now void). Makefile: removed 12 redundant pattern rules, dead check-circle test (now checks for the cross-compiler), duplicated distclean line; added missing .PHONY entries; develop now honors LIBRETRO_OUT/LDFLAGS.

  • Version bump: 3.0.0 -> 3.1.0 (display_version in virtualxt_libretro.info).

  • V20 IDIV (F6.7/F7.7): on POSTIDIV divide-error (INT 0), V20 pushes FLAGS with PSZ from the unsigned quotient, CF/AF/OF cleared. All 20000 hardware-captured IDIV tests pass; CI no longer needs || echo WARNING.

  • Startup peripheral status logging (vxt_peripheral_print_status) in src/vxt/peripheral.c, called from retro_load_game(). Prints CPU type/frequency, installed peripherals, IO and memory map ranges.

  • Pi bare-metal: raised KERNEL_MAX_SIZE to 20 MB across all models (was 17 MB on Pi 4/5, triggering a silent _end > MEM_KERNEL_END halt before CKernel::Initialize). Rebuilt Circle SDCard/fatfs addons, staged Pi 4 family DTBs, added [pi400]/[cm4] stanzas, prepended disable_splash=1.

  • Pi 3 bare-metal: real vfs_file_load (was NULL stub), setup_machine_from_ini refactored to two passes so disk.file_api is wired before configure(), cmdline.txt with LOGFILE=virtualxt.log, and machine.ini pointed at vxtbios.bin (was GLABIOS.ROM). CRT post-processing offloaded from Core 1 to Core 2 via CRT_FILTER_EXTERNAL plus 4:3 letterboxed scaling on the Core 2 render thread (tools/circle/kernel/emuloop.cpp).

  • Pi bare-metal keyboard: Shift/Ctrl/Alt were dropped because KeyStatusHandlerRaw ignored USB HID ucModifiers and arrays/loops were sized for 256 entries. Translated ucModifiers to libretro modifier keycodes and resized arrays to RETROK_LAST; shared frontend already maps shift/ctrl/alt to the correct XT scan codes.

  • Web UI: video button defaulted to EGA (HTML/JS) while the runtime default is CGA (tools/web/main.c:517); fixed videoState = 'CGA', label Video: CGA, cycle ['CGA', 'HGC', 'EGA']. Added Turbo toggle (Turbo: 4.77 / Turbo: 10.0) live-applied via wasm_set_turbo and ?cpu=4.77 / ?cpu=10. Libretro/RPi now also default to 4.77 MHz.