From 71ff44828d2e6ec05917c9629b9ce23904c19b08 Mon Sep 17 00:00:00 2001 From: Eremey Valetov Date: Fri, 10 Apr 2026 06:32:47 -0400 Subject: [PATCH] Add 16-bit real-mode DOS target -- 127KB standalone, no extender New Makefile.dos16 builds with OpenWatcom wcc (16-bit, MEDIUM model) producing a standard MZ executable that runs on any DOS without DOS/4GW. All 24 source files compile clean; tested on FreeDOS 1.4 via QEMU. Changes for 16-bit compatibility: - hal_dos.c: INTX macro selects int86() vs int386() based on _M_I86 - sound.c: reduce stack buffer from 8192 to 512 samples on 16-bit - tui.c: gracefully disable TUI if screen buffer allocation fails (near heap exhaustion common on 16-bit), batch mode still works - .gitignore: add .obj/.exe/.err/.lib for OpenWatcom build artifacts Size comparison: - 32-bit DOS/4GW: 175KB + 265KB extender = 440KB total - 16-bit real-mode: 127KB standalone The 32-bit build (Makefile.dos) and Linux build are unaffected. 72/72 interpreter tests pass. --- .gitignore | 4 ++++ Makefile.dos16 | 44 ++++++++++++++++++++++++++++++++++++++++++++ platform/hal_dos.c | 22 +++++++++++++++------- src/sound.c | 5 +++++ src/tui.c | 6 ++++-- 5 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 Makefile.dos16 diff --git a/.gitignore b/.gitignore index 828e319..e59fd5f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,10 @@ build/ docs/_build/ *.o *.d +*.obj +*.exe +*.err +*.lib gwbasic_*.txt gwbasic_*.dat gwbasic_*.bas diff --git a/Makefile.dos16 b/Makefile.dos16 new file mode 100644 index 0000000..305a00d --- /dev/null +++ b/Makefile.dos16 @@ -0,0 +1,44 @@ +# Makefile for building GW-BASIC 2026 with OpenWatcom for 16-bit DOS +# +# Usage: +# wmake -f Makefile.dos16 (from OpenWatcom environment) +# wmake -f Makefile.dos16 clean +# +# Requires: OpenWatcom 2.0+ targeting 16-bit real-mode DOS +# Produces: GWBASIC.EXE (interpreter, standalone MZ executable — no DOS extender) +# +# The 16-bit build uses the MEDIUM memory model (-mm): +# - Code: multiple segments (far calls), supports > 64KB total code +# - Data: single segment (near pointers), must fit in 64KB +# +# The compiler (GWBASCOM.EXE) is not built in 16-bit mode because it uses +# open_memstream() which is not available in 16-bit DOS. Use Makefile.dos +# (32-bit) for the compiler. + +CC = wcc +CFLAGS = -bt=dos -mm -ox -w4 -zq -za99 -Iinclude -D__MSDOS__ +LINKER = wlink +LIBRARIAN = wlib + +# Interpreter sources (same as 32-bit minus compiler files) +INTERP_OBJS = & + src/main.obj src/tokens.obj src/tokenizer.obj src/error.obj & + src/eval.obj src/interp.obj src/vars.obj src/arrays.obj & + src/input.obj src/math_int.obj src/math_float.obj & + src/math_transcend.obj src/strings.obj src/print.obj & + src/fileio.obj src/program_io.obj src/print_using.obj & + src/graphics.obj src/virmem.obj src/portio.obj src/strpool.obj & + src/sound.obj src/tui.obj platform/hal_dos.obj + +.c.obj: + $(CC) $(CFLAGS) -fo=$@ $< + +all: gwbasic.exe + +gwbasic.exe: $(INTERP_OBJS) + $(LINKER) system dos option stack=8192 name $@ file { $< } + +clean: .SYMBOLIC + del src\*.obj + del platform\*.obj + del gwbasic.exe diff --git a/platform/hal_dos.c b/platform/hal_dos.c index 889af5b..5b8dab7 100644 --- a/platform/hal_dos.c +++ b/platform/hal_dos.c @@ -5,7 +5,8 @@ * and screen control. Selected at compile time via __MSDOS__. * Linux HAL (hal_posix.c) is unchanged -- full backward compatibility. * - * Build: wcc386 -bt=dos -mf -ox -za99 -D__MSDOS__ -Iinclude + * Build: wcc386 -bt=dos -mf -ox -za99 -D__MSDOS__ -Iinclude (32-bit) + * wcc -bt=dos -mm -ox -za99 -D__MSDOS__ -Iinclude (16-bit) */ #ifdef __MSDOS__ @@ -18,6 +19,13 @@ #include #include +/* int86 (16-bit real mode) vs int386 (32-bit protected mode) */ +#ifdef _M_I86 +#define INTX(n, r_in, r_out) int86(n, r_in, r_out) +#else +#define INTX(n, r_in, r_out) int386(n, r_in, r_out) +#endif + static int cursor_row = 0; static int cursor_col = 0; static int screen_cols = 80; @@ -33,7 +41,7 @@ static void bios_set_cursor(int row, int col) r.h.bh = 0; r.h.dh = (unsigned char)row; r.h.dl = (unsigned char)col; - int386(0x10, &r, &r); + INTX(0x10, &r, &r); cursor_row = row; cursor_col = col; } @@ -44,7 +52,7 @@ static void bios_get_cursor(int *row, int *col) memset(&r, 0, sizeof(r)); r.h.ah = 0x03; r.h.bh = 0; - int386(0x10, &r, &r); + INTX(0x10, &r, &r); *row = r.h.dh; *col = r.h.dl; } @@ -60,7 +68,7 @@ static void bios_scroll_up(int lines, int attr, int r1, int c1, int r2, int c2) r.h.cl = (unsigned char)c1; r.h.dh = (unsigned char)r2; r.h.dl = (unsigned char)c2; - int386(0x10, &r, &r); + INTX(0x10, &r, &r); } static void bios_write_char(int ch, int attr) @@ -72,7 +80,7 @@ static void bios_write_char(int ch, int attr) r.h.bh = 0; r.h.bl = (unsigned char)attr; r.w.cx = 1; - int386(0x10, &r, &r); + INTX(0x10, &r, &r); } /* --- Terminal I/O --- */ @@ -118,7 +126,7 @@ static int dos_getch(void) union REGS r; memset(&r, 0, sizeof(r)); r.h.ah = 0x00; - int386(0x16, &r, &r); + INTX(0x16, &r, &r); return r.h.al ? r.h.al : (0x100 | r.h.ah); } @@ -156,7 +164,7 @@ static void dos_init(void) union REGS r; memset(&r, 0, sizeof(r)); r.h.ah = 0x0F; - int386(0x10, &r, &r); + INTX(0x10, &r, &r); screen_cols = r.h.ah; screen_rows = 25; /* safe default; BIOS data area read needs far ptr */ bios_get_cursor(&cursor_row, &cursor_col); diff --git a/src/sound.c b/src/sound.c index c46f336..4c37cf7 100644 --- a/src/sound.c +++ b/src/sound.c @@ -91,8 +91,13 @@ void snd_tone(int freq_hz, int duration_ticks) return; /* Allocate buffer (or use stack for short tones) */ +#ifdef _M_I86 + int16_t stack_buf[512]; /* 1KB — 16-bit DOS has ~4-8KB stack */ + int16_t *buf = (total <= 512) ? stack_buf : malloc(total * sizeof(int16_t)); +#else int16_t stack_buf[8192]; int16_t *buf = (total <= 8192) ? stack_buf : malloc(total * sizeof(int16_t)); +#endif if (!buf) return; diff --git a/src/tui.c b/src/tui.c index 3d1327c..4d8f934 100644 --- a/src/tui.c +++ b/src/tui.c @@ -665,8 +665,10 @@ void tui_init(bool fullscreen) /* Allocate screen buffer */ tui.screen = calloc(tui.rows * tui.cols, sizeof(tui_cell_t)); if (!tui.screen) { - fprintf(stderr, "Out of memory for screen buffer\n"); - exit(1); + /* Not enough near heap (common on 16-bit DOS). + * Disable TUI — batch mode still works via HAL. */ + tui.active = false; + return; } /* Set default F-key definitions */