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).
16 KiB
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 filesmake test-- runs both module and CPU test suites in sequencemake 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 datamake bios-data-- generates C headers from BIOS ROM binaries and disk imagemake 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.shbuilds Pi 3/4/5 boot directories from Circle + VirtualXT artifacts - Libretro GitLab CI matrix matches
../virtualxt_Cplatform coverage - Woodpecker libretro/itch bundle pipeline with CI-built
dist/libretroartifacts staged intopackage/virtualxt - Build flags for baremetal size reduction:
NO_DEFAULT_DISK=1,NO_SAMPLES=1. Themake rpitarget sets both. - Baremetal USB floppy pass-through. Circle bridges USB
vfs_read/vfs_writethrough 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_READYto complete disk I/O through the VXTX polling protocol.[disk] floppy_timing=false/harddrive_timing=falsedisables 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=1disables. - 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; compilessrc/frontend/core.cunchanged 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,.comwith 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=0disables,?storage=clearwipes. - XRGB8888 framebuffer up to 640x700, 4:3 aspect, dynamic resolution.
- CRT filter via
virtualxt_crt_filtercore 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 webtarget + 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 withUSE_SDHOST, raisingKERNEL_MAX_SIZEto 20 MB, stagingvxtbios.bin/vxtx.bin/FreeDOSC.img, implementingvfs_file_load, and wiring the disk file API before configure. -
Raspberry Pi bare-metal: Shift/Ctrl/Alt combinations did not work.
KeyStatusHandlerRawintools/circle/kernel/kernel.cppignored the USB HIDucModifiersbyte, and the Pi keyboard state arrays (key_states,key_states_current) and the dispatch loop were sized to0x100, too small for libretro modifier keycodes (RETROK_LSHIFT= 304, etc.). Fix: translateucModifiersbits toRETROK_LCTRL/LSHIFT/LALT/RCTRL/RSHIFT/RALTand resize the arrays and loops toRETROK_LAST. The shared frontend already mapsRETROK_LSHIFT/RETROK_RSHIFTto XT scan codes0x2A/0x36.
Notes
- Boot diagnostics: kernel opens
LOGFILE(fromcmdline.txt, e.g.LOGFILE=virtualxt.log) on FAT beforeretro_init(), capturing all setup-timeLOG_*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_outputbit 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_DEFINEX-macro template with two instantiations, eliminating the misleading do-nothing else-branches and redundant temporaries in the process. negate8/ negate16 unified intocord_negate()(width-mask parameterized); POSTIDIV logic factored intov20_divide_error_flags()andpostidiv_fix_signs(). The four DIV/IDIV exec handlers are now generated fromEXEC_DIV_DEFINE/EXEC_IDIV_DEFINEtemplates. 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 headermodules/video_common.h:crtc_cursor_tstate embedded in all three adapters,crtc_cursor_write(),crtc_blink_tick(), andvideo_attach_frontend()replace per-adapter copies; EGA write modes 0-2 shareega_write_plane(). Test framework gainedsetup_pic_machine()fixture (11 test_pic.c sites collapsed); make_ems/make_rtc wrappers inlined.retro_load_game()split intosetup_host_interfaces(),register_disk_control(), andsetup_content(). Debug server command dispatch is now table-driven with exact cmd matching (was a 24-branch substring if-chain);run_cycles()reusesvxt_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_RESERVEDrenamed toVXT_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_distring-op helpers (10 sites); V20 shift-by-immediate reuses the shareddo_shift_*helpers; NEG handlers reduced to singlealu_subcalls. Addedvxt_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 asvxt_disk_size_is_floppy()),int13_ok/err/busyresult helpers, sector copies via the new sharedmodules/guest_mem.h(also used by HostFS). HostFS: merged READFILE/WRITEFILE intohostfs_xfer(), RMDIR+DELETEFILE cases merged,hostfs_mailbox_path()/launch_substituted()/build_83_pattern()helpers,check_patternrenamed topattern_mismatch. Video: dropped HGC font copy (usesmda_fontdirectly), removed double VRAM masking, consolidated EGA I/O range registration, addedvxt_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: addedutils_strstr,utils_parse_bool,utils_fill_random(replaces 4 LCG fill copies). UART create reusesuart_reset; ROM vtable init helper; PIT tick interval is a constant; test suites usepic_init_xt(true)andVXT_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_vramnow 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), UARTiirfield, always-falseega_configstub, emptydestroystubs (joystick, UART, mouse),exec_wait/exec_esc(folded intoexec_nop), dead frontend/ mixer constants (BIOS_ROM_BASE,VXTX_ROM_BASE,HOSTFS_DURATION,HOSTFS_VOLUME,seek_volume),ini_parsereturn value (now void). Makefile: removed 12 redundant pattern rules, deadcheck-circletest (now checks for the cross-compiler), duplicateddistcleanline; added missing.PHONYentries;developnow honorsLIBRETRO_OUT/LDFLAGS. -
Version bump: 3.0.0 -> 3.1.0 (
display_versioninvirtualxt_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) insrc/vxt/peripheral.c, called fromretro_load_game(). Prints CPU type/frequency, installed peripherals, IO and memory map ranges. -
Pi bare-metal: raised
KERNEL_MAX_SIZEto 20 MB across all models (was 17 MB on Pi 4/5, triggering a silent_end > MEM_KERNEL_ENDhalt beforeCKernel::Initialize). Rebuilt Circle SDCard/fatfs addons, staged Pi 4 family DTBs, added[pi400]/[cm4]stanzas, prependeddisable_splash=1. -
Pi 3 bare-metal: real
vfs_file_load(was NULL stub),setup_machine_from_inirefactored to two passes sodisk.file_apiis wired beforeconfigure(),cmdline.txtwithLOGFILE=virtualxt.log, andmachine.inipointed atvxtbios.bin(wasGLABIOS.ROM). CRT post-processing offloaded from Core 1 to Core 2 viaCRT_FILTER_EXTERNALplus 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
KeyStatusHandlerRawignored USB HIDucModifiersand arrays/loops were sized for 256 entries. TranslateducModifiersto libretro modifier keycodes and resized arrays toRETROK_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 isCGA(tools/web/main.c:517); fixedvideoState = 'CGA', labelVideo: CGA, cycle['CGA', 'HGC', 'EGA']. AddedTurbotoggle (Turbo: 4.77/Turbo: 10.0) live-applied viawasm_set_turboand?cpu=4.77/?cpu=10. Libretro/RPi now also default to 4.77 MHz.