From 54e5ecd6ecc5d5ef43ab5dcf39c42b9f6e2ad11e Mon Sep 17 00:00:00 2001 From: Eremey Valetov Date: Fri, 10 Apr 2026 14:37:10 -0400 Subject: [PATCH] Use far heap for TUI screen buffer on 16-bit DOS The 4KB screen buffer (80x25x2 bytes) was allocated from near heap via calloc(), which exhausted the 64KB data segment on 16-bit DOS. Now uses _fcalloc()/_ffree() from OpenWatcom's far heap on 16-bit, keeping the buffer outside DGROUP. The TUI now works fully on 16-bit FreeDOS: full-screen editor, F-key bar, cursor positioning, and scroll -- all via BIOS INT 10h through the DOS HAL, with the screen buffer in far memory. Changes: - tui.h: GW_FAR macro (expands to __far on 16-bit, nothing elsewhere), tui.screen declared as tui_cell_t GW_FAR * - tui.c: _fcalloc/_ffree for 16-bit, _fmemmove for scroll_up() - TUI_CELL() macro works unchanged (far pointer dereference is transparent) --- include/tui.h | 10 +++++++++- src/tui.c | 19 +++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/include/tui.h b/include/tui.h index d239d46..5ee1618 100644 --- a/include/tui.h +++ b/include/tui.h @@ -4,6 +4,14 @@ #include #include +/* On 16-bit DOS, the screen buffer lives in far heap to save near heap. + * GW_FAR expands to __far on 16-bit OpenWatcom, nothing elsewhere. */ +#if defined(_M_I86) +#define GW_FAR __far +#else +#define GW_FAR +#endif + #define TUI_DEFAULT_ROWS 25 #define TUI_DEFAULT_COLS 80 #define TUI_MAX_ROWS 200 @@ -50,7 +58,7 @@ typedef struct { /* TUI state */ typedef struct { - tui_cell_t *screen; /* dynamically allocated [rows * cols] */ + tui_cell_t GW_FAR *screen; /* dynamically allocated [rows * cols] */ int rows; /* screen height (default 25) */ int cols; /* screen width (default 80) */ int cursor_row; diff --git a/src/tui.c b/src/tui.c index 04b78e5..6145683 100644 --- a/src/tui.c +++ b/src/tui.c @@ -9,6 +9,9 @@ #include #include #endif +#ifdef _M_I86 +#include /* _fcalloc, _ffree */ +#endif tui_state_t tui; @@ -37,8 +40,13 @@ static const char *default_fkeys[10] = { static void scroll_up(void) { int bottom = tui.view_bottom; +#ifdef _M_I86 + _fmemmove(&TUI_CELL(0, 0), &TUI_CELL(1, 0), + bottom * tui.cols * sizeof(tui_cell_t)); +#else memmove(&TUI_CELL(0, 0), &TUI_CELL(1, 0), bottom * tui.cols * sizeof(tui_cell_t)); +#endif for (int c = 0; c < tui.cols; c++) { TUI_CELL(bottom, c).ch = ' '; TUI_CELL(bottom, c).attr = tui.current_attr; @@ -666,10 +674,13 @@ void tui_init(bool fullscreen) tui.view_bottom = tui.rows - 1; /* Allocate screen buffer */ + /* On 16-bit DOS, use far heap to avoid exhausting the 64KB data segment */ +#ifdef _M_I86 + tui.screen = _fcalloc(tui.rows * tui.cols, sizeof(tui_cell_t)); +#else tui.screen = calloc(tui.rows * tui.cols, sizeof(tui_cell_t)); +#endif if (!tui.screen) { - /* Not enough near heap (common on 16-bit DOS). - * Disable TUI — batch mode still works via HAL. */ tui.active = false; return; } @@ -732,7 +743,11 @@ void tui_shutdown(void) printf("\033[0 q"); fflush(stdout); +#ifdef _M_I86 + _ffree(tui.screen); +#else free(tui.screen); +#endif tui.screen = NULL; signal(SIGINT, SIG_DFL);