From 2434e547ca19f6ee29f4ac44c7b3d704d20ada2f Mon Sep 17 00:00:00 2001 From: Dimitris Panokostas Date: Thu, 15 Jan 2026 02:21:34 +0100 Subject: [PATCH] Enhance consolehook: thread safety, CLI, config, shutdown Significantly improve consolehook (Amiga console.device emulation) for headless/CLI use: - Add thread-safe input ring buffer and input thread for non-blocking reads. - Implement graceful shutdown of input thread on reset/quit. - Allow custom root path via new `-cli[=path]` command-line option. - Update emulation prefs for better compatibility and expand ROM selection. - Normalize console output, add safety checks, and update API signatures. - Export `set_console_input_mode` for Windows. - Improve GitHub Actions workflow for Windows builds. These changes make console emulation more robust, responsive, and configurable. --- .github/workflows/build-winuae-binary.yml | 219 +++++++++++++--------- consolehook.cpp | 210 ++++++++++++++++----- include/consolehook.h | 3 +- include/sysdeps.h | 1 + main.cpp | 24 ++- od-win32/writelog.cpp | 2 +- 6 files changed, 322 insertions(+), 137 deletions(-) diff --git a/.github/workflows/build-winuae-binary.yml b/.github/workflows/build-winuae-binary.yml index 298133db..47d7fd10 100644 --- a/.github/workflows/build-winuae-binary.yml +++ b/.github/workflows/build-winuae-binary.yml @@ -1,112 +1,147 @@ +@@ -0,0 +1,146 @@ name: Build WinUAE binary on: workflow_dispatch: push: - branches: + branches: - master pull_request: branches: - master - + env: SOLUTION_FILE_PATH: od-win32\\winuae_msvc15 jobs: Build-WinUAE-32bit-binary: - runs-on: windows-latest + runs-on: windows-2026 steps: - - uses: actions/checkout@v4 - - - name: Add MSBuild to PATH - uses: microsoft/setup-msbuild@v2 - with: - vs-version: '[18.0]' - - # Running roughly step 4 of README.md - - name: Download WinUAE includes and libs - shell: powershell - run: Invoke-WebRequest -Uri "https://download.abime.net/winuae/files/b/winuaeinclibs.zip" -OutFile "winuaeinclibs.zip" - - - name: Unpack WinUAE includes and libs to C:\\dev - uses: ihiroky/extract-action@v1 - with: - file_path: winuaeinclibs.zip - extract_dir: C:\\dev - - # Running roughly step 6 of README.md - - name: Download AROS ROM cpp - shell: powershell - run: Invoke-WebRequest -Uri "https://download.abime.net/winuae/files/b/aros.rom.cpp.zip" -OutFile "aros.rom.cpp.zip" - - - name: Unpack AROS ROM cpp - uses: ihiroky/extract-action@v1 - with: - file_path: aros.rom.cpp.zip - extract_dir: . - - # Running roughly step 7 of README.md - - name: Add NASM to PATH - uses: ilammy/setup-nasm@v1.5.1 - - # Running roughly step 12 of README.md - - name: Build Win32 FullRelease - working-directory: ${{env.GITHUB_WORKSPACE}} - # Add additional options to the MSBuild command line here (like platform or verbosity level). - # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference - run: msbuild /m /p:Platform=Win32 /p:Configuration=FullRelease ${{env.SOLUTION_FILE_PATH}} - - - uses: actions/upload-artifact@v4 - with: - name: WinUAE 32-bit - path: D:\\Amiga + - uses: actions/checkout@v4 + + - name: Diagnostics (VS instances) + shell: powershell + run: | + if (Test-Path "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe") { + & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -all -prerelease -products * -format json + } elseif (Get-Command vswhere.exe -ErrorAction SilentlyContinue) { + vswhere.exe -all -prerelease -products * -format json + } else { + Write-Host "vswhere.exe not found" + } + + - name: Add MSBuild to PATH + uses: microsoft/setup-msbuild@v2 + with: + vs-version: '18.0' + + - name: Diagnostics (MSBuild) + shell: powershell + run: | + Get-Command msbuild -ErrorAction SilentlyContinue | Format-List * + msbuild -version + + # Running roughly step 4 of README.md + - name: Download WinUAE includes and libs + shell: powershell + run: Invoke-WebRequest -Uri "https://download.abime.net/winuae/files/b/winuaeinclibs.zip" -OutFile "winuaeinclibs.zip" + + - name: Unpack WinUAE includes and libs to C:\\dev + uses: ihiroky/extract-action@v1 + with: + file_path: winuaeinclibs.zip + extract_dir: C:\\dev + + # Running roughly step 6 of README.md + - name: Download AROS ROM cpp + shell: powershell + run: Invoke-WebRequest -Uri "https://download.abime.net/winuae/files/b/aros.rom.cpp.zip" -OutFile "aros.rom.cpp.zip" + + - name: Unpack AROS ROM cpp + uses: ihiroky/extract-action@v1 + with: + file_path: aros.rom.cpp.zip + extract_dir: . + + # Running roughly step 7 of README.md + - name: Add NASM to PATH + uses: ilammy/setup-nasm@v1.5.1 + + # Running roughly step 12 of README.md + - name: Build Win32 FullRelease + working-directory: ${{env.GITHUB_WORKSPACE}} + # Add additional options to the MSBuild command line here (like platform or verbosity level). + # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference + run: msbuild /m /p:Platform=Win32 /p:Configuration=FullRelease ${{env.SOLUTION_FILE_PATH}} + + - uses: actions/upload-artifact@v4 + with: + name: WinUAE 32-bit + path: D:\\Amiga Build-WinUAE-64bit-binary: - runs-on: windows-latest + runs-on: windows-2026 steps: - - uses: actions/checkout@v4 - - - name: Add MSBuild to PATH - uses: microsoft/setup-msbuild@v2 - with: - vs-version: '[18.0]' - - # Running roughly step 4 of README.md - - name: Download WinUAE includes and libs - shell: powershell - run: Invoke-WebRequest -Uri "https://download.abime.net/winuae/files/b/winuaeinclibs.zip" -OutFile "winuaeinclibs.zip" - - - name: Unpack WinUAE includes and libs to C:\\dev - uses: ihiroky/extract-action@v1 - with: - file_path: winuaeinclibs.zip - extract_dir: C:\\dev - - # Running roughly step 6 of README.md - - name: Download AROS ROM cpp - shell: powershell - run: Invoke-WebRequest -Uri "https://download.abime.net/winuae/files/b/aros.rom.cpp.zip" -OutFile "aros.rom.cpp.zip" - - - name: Unpack AROS ROM cpp - uses: ihiroky/extract-action@v1 - with: - file_path: aros.rom.cpp.zip - extract_dir: . - - # Running roughly step 7 of README.md - - name: Add NASM to PATH - uses: ilammy/setup-nasm@v1.5.1 - - # Running roughly step 12 of README.md - - name: Build x64 FullRelease - working-directory: ${{env.GITHUB_WORKSPACE}} - # Add additional options to the MSBuild command line here (like platform or verbosity level). - # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference - run: msbuild /m /p:Platform=x64 /p:Configuration=FullRelease ${{env.SOLUTION_FILE_PATH}} - - - uses: actions/upload-artifact@v4 - with: - name: WinUAE 64-bit - path: D:\\Amiga + - uses: actions/checkout@v4 + + - name: Diagnostics (VS instances) + shell: powershell + run: | + if (Test-Path "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe") { + & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -all -prerelease -products * -format json + } elseif (Get-Command vswhere.exe -ErrorAction SilentlyContinue) { + vswhere.exe -all -prerelease -products * -format json + } else { + Write-Host "vswhere.exe not found" + } + + - name: Add MSBuild to PATH + uses: microsoft/setup-msbuild@v2 + with: + vs-version: '18.0' + + - name: Diagnostics (MSBuild) + shell: powershell + run: | + Get-Command msbuild -ErrorAction SilentlyContinue | Format-List * + msbuild -version + + # Running roughly step 4 of README.md + - name: Download WinUAE includes and libs + shell: powershell + run: Invoke-WebRequest -Uri "https://download.abime.net/winuae/files/b/winuaeinclibs.zip" -OutFile "winuaeinclibs.zip" + + - name: Unpack WinUAE includes and libs to C:\\dev + uses: ihiroky/extract-action@v1 + with: + file_path: winuaeinclibs.zip + extract_dir: C:\\dev + + # Running roughly step 6 of README.md + - name: Download AROS ROM cpp + shell: powershell + run: Invoke-WebRequest -Uri "https://download.abime.net/winuae/files/b/aros.rom.cpp.zip" -OutFile "aros.rom.cpp.zip" + + - name: Unpack AROS ROM cpp + uses: ihiroky/extract-action@v1 + with: + file_path: aros.rom.cpp.zip + extract_dir: . + + # Running roughly step 7 of README.md + - name: Add NASM to PATH + uses: ilammy/setup-nasm@v1.5.1 + + # Running roughly step 12 of README.md + - name: Build x64 FullRelease + working-directory: ${{env.GITHUB_WORKSPACE}} + # Add additional options to the MSBuild command line here (like platform or verbosity level). + # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference + run: msbuild /m /p:Platform=x64 /p:Configuration=FullRelease ${{env.SOLUTION_FILE_PATH}} + + - uses: actions/upload-artifact@v4 + with: + name: WinUAE 64-bit + path: D:\\Amiga diff --git a/consolehook.cpp b/consolehook.cpp index 06659fb0..db5c0dfd 100644 --- a/consolehook.cpp +++ b/consolehook.cpp @@ -1,5 +1,3 @@ - - #include "sysconfig.h" #include "sysdeps.h" @@ -9,17 +7,94 @@ #include "rommgr.h" #include "uae.h" #include "threaddep/thread.h" -#include "keybuf.h" - +#include "fsdb.h" +#include "custom.h" #include "consolehook.h" +#ifdef _WIN32 +#include +#else +#include +#endif + +extern int consoleopen; + +#define CONSOLEHOOK_DEBUG 0 + static uaecptr beginio; -void consolehook_config (struct uae_prefs *p) +static uae_sem_t console_in_sem; +static uae_atomic console_in_init; +static uae_atomic console_thread_started; +static uae_atomic console_stop; + +static unsigned char console_in_buf[8192]; +static volatile uae_u32 console_in_rp; +static volatile uae_u32 console_in_wp; + +static void console_in_init_once(void) { - struct uaedev_config_info ci = { 0 }; - int roms[] = { 15, 31, 16, 46, -1 }; + if (console_in_init) + return; + console_in_init = 1; + uae_sem_init(&console_in_sem, 0, 1); + console_in_rp = console_in_wp = 0; +} +static uae_u32 console_in_avail_nolock(void) +{ + return (console_in_wp - console_in_rp) & (sizeof console_in_buf - 1); +} + +static void console_in_push(unsigned char b) +{ + uae_sem_wait(&console_in_sem); + uae_u32 next = (console_in_wp + 1) & (sizeof console_in_buf - 1); + if (next != console_in_rp) { + console_in_buf[console_in_wp] = b; + console_in_wp = next; + } + uae_sem_post(&console_in_sem); +#if CONSOLEHOOK_DEBUG + write_log(_T("[consolehook] push %02X avail=%u\n"), b, console_in_avail_nolock()); +#endif +} + +static uae_u32 console_in_pop(unsigned char *dst, uae_u32 maxlen) +{ + uae_u32 got = 0; + uae_sem_wait(&console_in_sem); + while (got < maxlen && console_in_rp != console_in_wp) { + dst[got++] = console_in_buf[console_in_rp]; + console_in_rp = (console_in_rp + 1) & (sizeof console_in_buf - 1); + } + uae_sem_post(&console_in_sem); + return got; +} + +/* CMD_READ must not block: it is called on the emulation thread. */ + +void consolehook_shutdown(void) +{ + console_stop = 1; +} + +void consolehook_config (struct uae_prefs *p, const TCHAR *custom_path) +{ + struct uaedev_config_info ci = { 0 }; + int roms[8]; + roms[0] = 11; + roms[1] = 15; +#ifdef AMIBERRY + roms[2] = 276; + roms[3] = 281; + roms[4] = 286; + roms[5] = 291; + roms[6] = 304; + roms[7] = -1; +#endif + + consoleopen = 0; default_prefs (p, true, 0); //p->headless = 1; p->produce_sound = 0; @@ -33,11 +108,15 @@ void consolehook_config (struct uae_prefs *p) configure_rom (p, roms, 0); p->cpu_model = 68020; p->fpu_model = 68882; + p->chipset_mask = CSMASK_AGA | CSMASK_ECS_AGNUS | CSMASK_ECS_DENISE; + p->cpu_compatible = p->address_space_24 = false; p->m68k_speed = -1; - p->cachesize = 8192; - p->cpu_compatible = 0; - p->address_space_24 = 0; - p->chipmem.size = 0x00200000; +#ifdef JIT + p->cachesize = 16384; +#else + p->cachesize = 0; +#endif + p->chipmem.size = 0x200000; p->fastmem[0].size = 0x00800000; p->bogomem.size = 0; p->nr_floppies = 1; @@ -49,8 +128,27 @@ void consolehook_config (struct uae_prefs *p) p->turbo_emulation = 0; //p->win32_automount_drives = 2; //p->win32_automount_cddrives = 2; - - _tcscpy (ci.rootdir, _T(".")); + p->cs_compatible = CP_A1200; + built_in_chipset_prefs (p); + + uci_set_defaults(&ci, false); + if (custom_path && custom_path[0]) { + // Use the custom path provided via command line + my_canonicalize_path(custom_path, ci.rootdir, MAX_DPATH); + } else { + // Use current working directory as before + TCHAR cwd[MAX_DPATH]; + cwd[0] = 0; +#ifdef _WIN32 + GetCurrentDirectory(MAX_DPATH, cwd); +#else + getcwd(cwd, MAX_DPATH); +#endif + if (cwd[0]) + my_canonicalize_path(cwd, ci.rootdir, MAX_DPATH); + else + _tcscpy(ci.rootdir, _T(".")); + } _tcscpy (ci.volname, _T("CLIBOOT")); _tcscpy (ci.devname, _T("DH0")); ci.bootpri = 15; @@ -61,16 +159,22 @@ void consolehook_config (struct uae_prefs *p) static void console_thread (void *v) { uae_set_thread_priority (NULL, 1); + console_in_init_once(); + set_console_input_mode(0); for (;;) { + if (console_stop) + break; TCHAR wc = console_getch (); - char c[2]; - - c[0] = 0; - c[1] = 0; - ua_copy (c, 1, &wc); - record_key_direct((0x10 << 1) | 0, false); - record_key_direct((0x10 << 1) | 1, false); + if (!wc) + continue; + char c = (char)wc; + /* Normalize host console keys to what Amiga console.device expects. */ + if (c == '\r') + c = '\n'; + console_in_push((unsigned char)c); } + set_console_input_mode(1); + console_thread_started = 0; } int consolehook_activate (void) @@ -82,40 +186,62 @@ void consolehook_ret(TrapContext *ctx, uaecptr condev, uaecptr oldbeginio) { beginio = oldbeginio; write_log (_T("console.device at %08X\n"), condev); - - uae_start_thread (_T("consolereader"), console_thread, NULL, NULL); + open_console(); + console_in_init_once(); + if (!console_thread_started) { + console_thread_started = 1; + console_stop = 0; + uae_start_thread (_T("consolereader"), console_thread, NULL, NULL); + } } uaecptr consolehook_beginio(TrapContext *ctx, uaecptr request) { uae_u32 io_data = trap_get_long(ctx, request + 40); // 0x28 uae_u32 io_length = trap_get_long(ctx, request + 36); // 0x24 - uae_u32 io_actual = trap_get_long(ctx, request + 32); // 0x20 - uae_u32 io_offset = trap_get_long(ctx, request + 44); // 0x2c + (void)trap_get_long(ctx, request + 32); // io_actual (unused) + (void)trap_get_long(ctx, request + 44); // io_offset (unused) uae_u16 cmd = trap_get_word(ctx, request + 28); if (cmd == CMD_WRITE) { - int len = io_length; - char *dbuf; - TCHAR *buf; - if (len == -1) { - dbuf = xmalloc(char, MAX_DPATH); - trap_get_string(ctx, dbuf, io_data, MAX_DPATH); - len = uaestrlen(dbuf); - } else { - dbuf = xmalloc(char, len); + open_console(); + int len = (int)io_length; + if (len < 0) + len = 0; + if (len > 1024 * 1024) + len = 1024 * 1024; + if (len > 0 && io_data) { + char *dbuf = xmalloc(char, len); + if (!dbuf) + return beginio; trap_get_bytes(ctx, dbuf, io_data, len); + + for (int i = 0; i < len; i++) { + TCHAR ch = (TCHAR)dbuf[i]; + if (ch == '\n') + console_out_f(_T("\r")); + console_out_f(_T("%c"), ch); + } + console_flush(); + xfree(dbuf); } - buf = xmalloc(TCHAR, len + 1); - au_copy(buf, len, dbuf); - buf[len] = 0; - f_out(stdout, _T("%s"), buf); - xfree(buf); - xfree(dbuf); } else if (cmd == CMD_READ) { - - write_log (_T("%08x: CMD=%d LEN=%d OFF=%d ACT=%d\n"), request, cmd, io_length, io_offset, io_actual); - + console_in_init_once(); + uae_u32 maxlen = io_length; + if ((uae_s32)maxlen < 0) + maxlen = 0; + if (maxlen > 4096) + maxlen = 4096; + unsigned char tmp[4096]; + uae_u32 got = 0; + if (maxlen && io_data) // Fix: check io_data (buf) is not NULL before use + got = console_in_pop(tmp, maxlen); +#if CONSOLEHOOK_DEBUG + write_log(_T("[consolehook] CMD_READ max=%u got=%u\n"), maxlen, got); +#endif + if (got && io_data) // Fix: check io_data (buf) is not NULL before use + trap_put_bytes(ctx, tmp, io_data, got); + trap_put_long(ctx, request + 32, got); // io_actual } return beginio; } diff --git a/include/consolehook.h b/include/consolehook.h index d119644c..60767f5c 100644 --- a/include/consolehook.h +++ b/include/consolehook.h @@ -6,6 +6,7 @@ int consolehook_activate(void); void consolehook_ret(TrapContext *ctx, uaecptr condev, uaecptr oldbeginio); uaecptr consolehook_beginio(TrapContext *ctx, uaecptr request); -void consolehook_config(struct uae_prefs *p); +void consolehook_config(struct uae_prefs *p, const TCHAR *custom_path); +void consolehook_shutdown(void); #endif /* UAE_CONSOLEHOOK_H */ diff --git a/include/sysdeps.h b/include/sysdeps.h index dae18aab..6f4d2a67 100644 --- a/include/sysdeps.h +++ b/include/sysdeps.h @@ -460,6 +460,7 @@ extern void open_console(void); extern void reopen_console(void); extern void activate_console(void); extern void deactivate_console(void); +extern void set_console_input_mode(int); extern void console_out (const TCHAR *); extern void console_out_f (const TCHAR *, ...); extern void console_flush (void); diff --git a/main.cpp b/main.cpp index e2f69ed6..40f7b67c 100644 --- a/main.cpp +++ b/main.cpp @@ -63,6 +63,7 @@ bool no_gui = 0, quit_to_gui = 0; bool cloanto_rom = 0; bool kickstart_rom = 1; bool console_emulation = 0; +TCHAR console_path[MAX_DPATH] = { 0 }; struct gui_info gui_data; @@ -837,6 +838,7 @@ void uae_reset (int hardreset, int keyboardreset) currprefs.quitstatefile[0] = changed_prefs.quitstatefile[0] = 0; if (quit_program == 0) { + consolehook_shutdown(); quit_program = -UAE_RESET; if (keyboardreset) quit_program = -UAE_RESET_KEYBOARD; @@ -851,6 +853,7 @@ void uae_quit (void) #ifdef DEBUGGER deactivate_debugger (); #endif + consolehook_shutdown(); if (quit_program != -UAE_QUIT) quit_program = -UAE_QUIT; target_quit (); @@ -965,6 +968,25 @@ static void parse_cmdline (int argc, TCHAR **argv) started = true; for (i = 1; i < argc; i++) { + if (_tcsncmp(argv[i], _T("-cli="), 5) == 0 || _tcsncmp(argv[i], _T("--cli="), 6) == 0) { + console_emulation = 1; + TCHAR *path = _tcschr(argv[i], _T('=')) + 1; + if (path && path[0]) { + _tcsncpy(console_path, path, MAX_DPATH - 1); + console_path[MAX_DPATH - 1] = 0; + } + continue; + } + if (_tcscmp(argv[i], _T("-cli")) == 0 || _tcscmp(argv[i], _T("--cli")) == 0) { + console_emulation = 1; + // Check if next argument is a path (not starting with '-') + if (i + 1 < argc && argv[i + 1][0] != _T('-')) { + i++; + _tcsncpy(console_path, argv[i], MAX_DPATH - 1); + console_path[MAX_DPATH - 1] = 0; + } + continue; + } if (!_tcsncmp (argv[i], _T("-diskswapper="), 13)) { TCHAR *txt = parsetextpath (argv[i] + 13); parse_diskswapper (txt); @@ -1169,7 +1191,7 @@ static int real_main2 (int argc, TCHAR **argv) } if (console_emulation) { - consolehook_config (&currprefs); + consolehook_config (&currprefs, console_path[0] ? console_path : NULL); fixup_prefs (&currprefs, true); } diff --git a/od-win32/writelog.cpp b/od-win32/writelog.cpp index 9fef5642..f009d638 100644 --- a/od-win32/writelog.cpp +++ b/od-win32/writelog.cpp @@ -88,7 +88,7 @@ static HWND myGetConsoleWindow (void) return GetConsoleWindow (); } -static void set_console_input_mode(int line) +void set_console_input_mode(int line) { if (console_input_linemode < 0) return; -- 2.47.3