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).
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <conio.h>
|
||||
#include <dos.h>
|
||||
#include <i86.h> /* 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__ */
|
||||
@@ -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++)
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user