From d1a58876e75024c1dfb5529c8d54581e3810d319 Mon Sep 17 00:00:00 2001 From: Eremey Valetov Date: Mon, 30 Mar 2026 18:12:24 -0400 Subject: [PATCH] Add DOS HAL backend and OpenWatcom build for FreeDOS platform/hal_dos.c: DOS HAL implementation using BIOS INT 10h for screen/cursor, INT 16h for keyboard, direct character output via BIOS write-char. Compile-time selection via __MSDOS__ define. Linux HAL (hal_posix.c) unchanged -- full backward compatibility. hal.h: add hal_dos_create() declaration under __MSDOS__ guard. main.c, gwrt.c: select HAL at compile time via #ifdef __MSDOS__. Makefile.dos: OpenWatcom wmake build file targeting DOS/4GW 32-bit. Builds GWBASIC.EXE (interpreter), GWBASCOM.EXE (compiler), GWRT.LIB (runtime library for compiled programs). All 72 interpreter, 14 kernel, and 69 compiler tests continue to pass on Linux (no regression). --- Makefile.dos | 59 +++++++++++++ include/hal.h | 3 + platform/hal_dos.c | 213 +++++++++++++++++++++++++++++++++++++++++++++ src/gwrt.c | 4 + src/main.c | 4 + 5 files changed, 283 insertions(+) create mode 100644 Makefile.dos create mode 100644 platform/hal_dos.c diff --git a/Makefile.dos b/Makefile.dos new file mode 100644 index 0000000..743cae8 --- /dev/null +++ b/Makefile.dos @@ -0,0 +1,59 @@ +# Makefile for building GW-BASIC 2026 with OpenWatcom for DOS +# +# Usage: +# wmake -f Makefile.dos (from OpenWatcom environment) +# wmake -f Makefile.dos clean +# +# Requires: OpenWatcom 2.0+ with DOS/4GW 32-bit target +# Produces: GWBASIC.EXE (interpreter), GWBASCOM.EXE (compiler), GWRT.LIB (runtime) + +CC = wcc386 +CFLAGS = -bt=dos -mf -ox -w4 -zq -Iinclude -D__MSDOS__ +LINKER = wlink +LIBRARIAN = wlib + +# Interpreter sources (excludes compiler_main, analysis, codegen, gwrt) +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 + +# Runtime library (for compiled programs) +GWRT_OBJS = & + 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 src/gwrt.obj platform/hal_dos.obj + +# Compiler sources +COMP_OBJS = & + src/compiler_main.obj src/analysis.obj src/codegen.obj & + src/tokens.obj src/tokenizer.obj + +.c.obj: + $(CC) $(CFLAGS) -fo=$@ $< + +all: gwbasic.exe gwbascom.exe gwrt.lib + +gwbasic.exe: $(INTERP_OBJS) + $(LINKER) system dos4g name $@ file { $< } + +gwrt.lib: $(GWRT_OBJS) + $(LIBRARIAN) -q -n $@ $(GWRT_OBJS) + +gwbascom.exe: $(COMP_OBJS) + $(LINKER) system dos4g name $@ file { $< } + +clean: .SYMBOLIC + del src\*.obj + del platform\*.obj + del gwbasic.exe + del gwbascom.exe + del gwrt.lib diff --git a/include/hal.h b/include/hal.h index a1df884..229cb01 100644 --- a/include/hal.h +++ b/include/hal.h @@ -38,5 +38,8 @@ extern hal_ops_t *gw_hal; /* Platform implementations */ hal_ops_t *hal_posix_create(void); +#ifdef __MSDOS__ +hal_ops_t *hal_dos_create(void); +#endif #endif diff --git a/platform/hal_dos.c b/platform/hal_dos.c new file mode 100644 index 0000000..55fd628 --- /dev/null +++ b/platform/hal_dos.c @@ -0,0 +1,213 @@ +/* + * DOS HAL backend for GW-BASIC 2026. + * + * Uses BIOS/DOS interrupts for terminal I/O, keyboard input, + * and screen control. Replaces the POSIX HAL (hal_posix.c) + * when targeting FreeDOS or other DOS-compatible systems. + * + * Build with OpenWatcom: wcl386 -bt=dos -l=dos4g ... + * Build with DJGPP: gcc -o gwbasic.exe ... + * + * Linux compatibility is retained -- hal_posix.c is used on + * POSIX systems, hal_dos.c on DOS. The hal_ops_t vtable + * provides the compile-time abstraction. + */ + +#ifdef __MSDOS__ + +#include "hal.h" +#include +#include +#include +#include +#include +#include /* OpenWatcom int86() */ + +static int cursor_row = 0; +static int cursor_col = 0; +static int screen_cols = 80; +static int screen_rows = 25; + +/* --- BIOS video services (INT 10h) --- */ + +static void bios_set_cursor(int row, int col) +{ + union REGS r; + r.h.ah = 0x02; /* Set cursor position */ + r.h.bh = 0; /* Page 0 */ + r.h.dh = (uint8_t)row; + r.h.dl = (uint8_t)col; + int86(0x10, &r, &r); + cursor_row = row; + cursor_col = col; +} + +static void bios_get_cursor(int *row, int *col) +{ + union REGS r; + r.h.ah = 0x03; /* Get cursor position */ + r.h.bh = 0; + int86(0x10, &r, &r); + *row = r.h.dh; + *col = r.h.dl; +} + +static void bios_scroll_up(int lines, int attr, int r1, int c1, int r2, int c2) +{ + union REGS r; + r.h.ah = 0x06; /* Scroll window up */ + r.h.al = (uint8_t)lines; + r.h.bh = (uint8_t)attr; + r.h.ch = (uint8_t)r1; + r.h.cl = (uint8_t)c1; + r.h.dh = (uint8_t)r2; + r.h.dl = (uint8_t)c2; + int86(0x10, &r, &r); +} + +static void bios_write_char(int ch, int attr) +{ + union REGS r; + r.h.ah = 0x09; /* Write char + attribute */ + r.h.al = (uint8_t)ch; + r.h.bh = 0; + r.h.bl = (uint8_t)attr; + r.x.cx = 1; + int86(0x10, &r, &r); +} + +/* --- Terminal I/O --- */ + +static void dos_putch(int ch) +{ + if (ch == '\n') { + cursor_col = 0; + cursor_row++; + if (cursor_row >= screen_rows) { + bios_scroll_up(1, 0x07, 0, 0, screen_rows - 1, screen_cols - 1); + cursor_row = screen_rows - 1; + } + bios_set_cursor(cursor_row, cursor_col); + } else if (ch == '\r') { + cursor_col = 0; + bios_set_cursor(cursor_row, cursor_col); + } else if (ch == '\b') { + if (cursor_col > 0) cursor_col--; + bios_set_cursor(cursor_row, cursor_col); + } else { + bios_write_char(ch, 0x07); + cursor_col++; + if (cursor_col >= screen_cols) { + cursor_col = 0; + cursor_row++; + if (cursor_row >= screen_rows) { + bios_scroll_up(1, 0x07, 0, 0, screen_rows - 1, screen_cols - 1); + cursor_row = screen_rows - 1; + } + } + bios_set_cursor(cursor_row, cursor_col); + } +} + +static void dos_puts(const char *s) +{ + while (*s) + dos_putch(*s++); +} + +static int dos_getch(void) +{ + /* INT 16h AH=00: wait for key, return AL=ASCII, AH=scan */ + union REGS r; + r.h.ah = 0x00; + int86(0x16, &r, &r); + return r.h.al ? r.h.al : (0x100 | r.h.ah); /* extended key */ +} + +static bool dos_kbhit(void) +{ + return kbhit() != 0; +} + +static void dos_locate(int row, int col) +{ + bios_set_cursor(row, col); +} + +static int dos_get_cursor_row(void) { return cursor_row; } +static int dos_get_cursor_col(void) { return cursor_col; } + +static void dos_cls(void) +{ + bios_scroll_up(0, 0x07, 0, 0, screen_rows - 1, screen_cols - 1); + bios_set_cursor(0, 0); +} + +static void dos_set_width(int cols) +{ + (void)cols; +} + +/* DOS doesn't have raw/cooked mode distinction for console */ +static void dos_enable_raw(void) { } +static void dos_disable_raw(void) { } + +static void dos_write_raw(const char *data, int len) +{ + /* On DOS, write directly to screen buffer for graphics. + * For text, just output character by character. */ + for (int i = 0; i < len; i++) + dos_putch(data[i]); +} + +/* --- Lifecycle --- */ + +static void dos_init(void) +{ + /* Get current video mode to determine screen size */ + union REGS r; + r.h.ah = 0x0F; /* Get video mode */ + int86(0x10, &r, &r); + screen_cols = r.h.ah; + + /* Get screen rows from BIOS data area */ + /* 0040:0084 = rows - 1 */ + screen_rows = *(uint8_t far *)MK_FP(0x0040, 0x0084) + 1; + if (screen_rows < 25) screen_rows = 25; + + bios_get_cursor(&cursor_row, &cursor_col); +} + +static void dos_shutdown(void) +{ + /* Nothing to clean up on DOS */ +} + +/* --- HAL vtable --- */ + +static hal_ops_t dos_hal = { + .putch = dos_putch, + .puts = dos_puts, + .getch = dos_getch, + .kbhit = dos_kbhit, + .locate = dos_locate, + .get_cursor_row = dos_get_cursor_row, + .get_cursor_col = dos_get_cursor_col, + .cls = dos_cls, + .set_width = dos_set_width, + .enable_raw = dos_enable_raw, + .disable_raw = dos_disable_raw, + .write_raw = dos_write_raw, + .screen_width = 80, + .screen_height = 25, + .is_tty = true, /* DOS console is always a TTY */ + .init = dos_init, + .shutdown = dos_shutdown, +}; + +hal_ops_t *hal_dos_create(void) +{ + return &dos_hal; +} + +#endif /* __MSDOS__ */ diff --git a/src/gwrt.c b/src/gwrt.c index dcd8b6c..1bacdc8 100644 --- a/src/gwrt.c +++ b/src/gwrt.c @@ -79,7 +79,11 @@ int gwrt_resume_label; void gwrt_init(void) { +#ifdef __MSDOS__ + gw_hal = hal_dos_create(); +#else gw_hal = hal_posix_create(); +#endif gw_hal->init(); memset(&gw, 0, sizeof(gw)); for (int i = 0; i < 26; i++) diff --git a/src/main.c b/src/main.c index f50979b..bc3cefe 100644 --- a/src/main.c +++ b/src/main.c @@ -167,7 +167,11 @@ static void print_banner(int interactive) int main(int argc, char **argv) { +#ifdef __MSDOS__ + gw_hal = hal_dos_create(); +#else gw_hal = hal_posix_create(); +#endif gw_hal->init(); gw_init(); strpool_init(STRPOOL_DEFAULT_SIZE);