Four roadmap items: - codegen: fix parenthesized string comparison. emit_atom didn't consume the body of a string-literal token (`"`), so for PRINT (A$+B$ < "ZZZ") it emitted a 0 placeholder, advanced one byte, and left "ZZZ" to be reparsed as a variable + extra trailing tokens -- the binary then failed to link with `var_ZZ_sng` undeclared. emit_atom now skips to the closing quote. Separately, the left_type tracking in emit_num_prec dropped VT_STR after a string + string concat (becoming VT_SNG), so the string-comparison codepath skipped when the relational operator arrived. Preserve VT_STR through TOK_PLUS when both operands are strings. Verified: paren string-cmp now compiles and produces the same -1 / 0 result as the interpreter. - compiler: --no-gc-check and --fast-math optimization flags. --no-gc-check skips the per-line gwrt_check_line() (no string-pool GC, no Ctrl+Break trap). --fast-math drops the divide-by-zero guard on `/`; the divisor still goes through (double) so 10/0 produces inf rather than SIGFPE. Both threaded through codegen_opts_t and exposed in --help. --inline-arrays from the roadmap deferred -- larger refactor. - interp: raise static caps on 32-bit / Linux builds. vars 256 -> 1024, arrays 64 -> 256, MAX_FOR_DEPTH 16 -> 64, MAX_GOSUB_DEPTH 24 -> 128, MAX_WHILE_DEPTH 16 -> 64. Codegen FOR_STACK_MAX 16 -> 64. Analysis-pass caps: MAX_LINES 4096 -> 8192, MAX_VARS 256 -> 1024, MAX_GOTOS 256 -> 1024, MAX_DATA 1024 -> 4096, MAX_GOSUB_RET 256 -> 1024. 16-bit DOS keeps the original modest caps via #ifdef _M_I86 -- the MEDIUM model has a single 64KB DGROUP for all static data and the bumped sizes broke runtime startup under DOSBox-X. 16-bit binary grew from 128KB to 132KB from the offset_secs field plus DATE$/TIME$ shift code, well within the FreeDOS budget. - interp + codegen: DATE$ / TIME$ assignment via process-local clock offset. Was a no-op accept-and-ignore. Now sets gw.time_offset_secs (long), and DATE$ / TIME$ / TIMER readers apply it to time(NULL) before formatting. The OS clock is unaffected (would need root). Compiled-binary readers also reference gw.time_offset_secs since libgwrt shares the gw struct. Verified: PRINT DATE$; DATE$="12-31-1999"; PRINT DATE$ shows the expected before/after in both interpreter and AOT paths. After these changes: 72/72 interpreter tests, 68/68 compat, 63/63 compiler tests, DOS smoke under DOSBox-X all pass. Build clean on both Linux (cmake) and 16-bit DOS (build_dos.sh 16).
132 lines
3.3 KiB
C
132 lines
3.3 KiB
C
#ifndef GW_INTERP_H
|
|
#define GW_INTERP_H
|
|
|
|
#include "types.h"
|
|
#include <stdbool.h>
|
|
|
|
/* Interpreter state - the global context */
|
|
typedef struct {
|
|
/* Text pointer (TXTPTR equivalent) */
|
|
uint8_t *text_ptr;
|
|
|
|
/* Current accumulator (FAC equivalent) */
|
|
gw_value_t fac;
|
|
|
|
/* Program state */
|
|
uint16_t cur_line_num; /* CURLIN - 65535 = direct mode */
|
|
program_line_t *prog_head; /* first line of program */
|
|
bool trace_on; /* TRON/TROFF */
|
|
|
|
/* Tokenizer buffers */
|
|
uint8_t kbuf[300]; /* crunch buffer */
|
|
char buf[256]; /* input line buffer */
|
|
|
|
/* Default variable types for A-Z (DEFTBL) */
|
|
gw_valtype_t def_type[26];
|
|
|
|
/* Variable storage. Caps stay modest on 16-bit DOS (MEDIUM model has
|
|
* a single 64KB DGROUP for all static data); 32-bit / Linux builds
|
|
* raise them substantially. */
|
|
#ifdef _M_I86
|
|
#define MAX_VAR_TABLE 256
|
|
#define MAX_ARRAY_TABLE 64
|
|
#define MAX_FOR_DEPTH 16
|
|
#define MAX_GOSUB_DEPTH 24
|
|
#define MAX_WHILE_DEPTH 16
|
|
#else
|
|
#define MAX_VAR_TABLE 1024
|
|
#define MAX_ARRAY_TABLE 256
|
|
#define MAX_FOR_DEPTH 64
|
|
#define MAX_GOSUB_DEPTH 128
|
|
#define MAX_WHILE_DEPTH 64
|
|
#endif
|
|
var_entry_t vars[MAX_VAR_TABLE];
|
|
int var_count;
|
|
array_entry_t arrays[MAX_ARRAY_TABLE];
|
|
int array_count;
|
|
int option_base;
|
|
|
|
/* Control flow stacks */
|
|
for_entry_t for_stack[MAX_FOR_DEPTH];
|
|
int for_sp;
|
|
gosub_entry_t gosub_stack[MAX_GOSUB_DEPTH];
|
|
int gosub_sp;
|
|
while_entry_t while_stack[MAX_WHILE_DEPTH];
|
|
int while_sp;
|
|
|
|
/* DEF FN */
|
|
fn_def_t fn_defs[26];
|
|
|
|
/* Run state */
|
|
program_line_t *cur_line;
|
|
bool running;
|
|
|
|
/* CONT state */
|
|
uint8_t *cont_text;
|
|
program_line_t *cont_line;
|
|
|
|
/* Process-local clock offset (seconds). DATE$ / TIME$ assignments
|
|
* shift the program's view of the clock without touching the OS
|
|
* time (which would need root). Defaults to 0. */
|
|
long time_offset_secs;
|
|
|
|
/* DATA pointer */
|
|
uint8_t *data_ptr;
|
|
program_line_t *data_line_ptr;
|
|
uint16_t data_line;
|
|
|
|
/* File I/O table (#1-#15, index 0 unused) */
|
|
file_entry_t files[16];
|
|
|
|
/* Error trapping */
|
|
uint16_t on_error_line;
|
|
bool in_error_handler;
|
|
uint8_t *err_resume_text;
|
|
program_line_t *err_resume_line;
|
|
uint16_t err_line_num;
|
|
|
|
/* AUTO mode */
|
|
bool auto_mode;
|
|
uint16_t auto_line;
|
|
uint16_t auto_inc;
|
|
|
|
/* COMMON variable list for CHAIN */
|
|
int common_count;
|
|
struct { char name[2]; gw_valtype_t type; } common_vars[64];
|
|
|
|
/* Event trapping */
|
|
timer_trap_t timer_trap;
|
|
event_trap_t key_traps[10]; /* KEY(1)-KEY(10) */
|
|
|
|
/* DEF SEG segment (for PEEK/POKE/BSAVE/BLOAD) */
|
|
uint16_t def_seg;
|
|
} interp_state_t;
|
|
|
|
extern interp_state_t gw;
|
|
|
|
/* Direct mode line number sentinel */
|
|
#define LINE_DIRECT 65535
|
|
|
|
void gw_init(void);
|
|
void gw_exec_direct(const char *line);
|
|
void gw_exec_stmt(void);
|
|
void gw_run_loop(void);
|
|
|
|
/* Program storage */
|
|
void gw_store_line(uint16_t num, uint8_t *tokens, int len);
|
|
void gw_delete_line(uint16_t num);
|
|
program_line_t *gw_find_line(uint16_t num);
|
|
void gw_free_program(void);
|
|
|
|
/* CHRGET/CHRGOT - advance/peek the text pointer */
|
|
uint8_t gw_chrget(void);
|
|
uint8_t gw_chrgot(void);
|
|
void gw_skip_spaces(void);
|
|
bool gw_is_letter(uint8_t ch);
|
|
bool gw_is_digit(uint8_t ch);
|
|
|
|
/* Expect a specific token, else syntax error */
|
|
void gw_expect(uint8_t tok);
|
|
|
|
#endif
|