672 lines
31 KiB
C
672 lines
31 KiB
C
// The MIT License (MIT)
|
|
//
|
|
// Copyright (C) 2026 Scott Duensing
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to
|
|
// deal in the Software without restriction, including without limitation the
|
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
// sell copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
// IN THE SOFTWARE.
|
|
|
|
// opcodes.h -- DVX BASIC bytecode instruction definitions
|
|
//
|
|
// Stack-based p-code for the DVX BASIC virtual machine.
|
|
// Embeddable: no DVX dependencies, pure C.
|
|
|
|
#ifndef DVXBASIC_OPCODES_H
|
|
#define DVXBASIC_OPCODES_H
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
|
|
// ============================================================
|
|
// Variable scope tags
|
|
// Emitted in bytecode (e.g. OP_FOR scopeTag byte) and consumed
|
|
// by the VM to choose between globals, call-frame locals, and
|
|
// form-scoped variables. Numeric values are part of the bytecode
|
|
// ABI -- do not reorder.
|
|
// ============================================================
|
|
|
|
typedef enum {
|
|
SCOPE_GLOBAL = 0,
|
|
SCOPE_LOCAL = 1,
|
|
SCOPE_FORM = 2 // per-form variable (persists while form is loaded)
|
|
} BasScopeE;
|
|
|
|
// ============================================================
|
|
// File channel modes (BasFileChannelT.mode and OP_FILE_OPEN arg).
|
|
// Numeric values are part of the bytecode ABI -- do not reorder.
|
|
// ============================================================
|
|
|
|
typedef enum {
|
|
BAS_FILE_MODE_CLOSED = 0,
|
|
BAS_FILE_MODE_INPUT = 1,
|
|
BAS_FILE_MODE_OUTPUT = 2,
|
|
BAS_FILE_MODE_APPEND = 3,
|
|
BAS_FILE_MODE_RANDOM = 4,
|
|
BAS_FILE_MODE_BINARY = 5
|
|
} BasFileModeE;
|
|
|
|
// ============================================================
|
|
// Data type tags (used in Value representation)
|
|
// ============================================================
|
|
|
|
#define BAS_TYPE_INTEGER 0 // 16-bit signed
|
|
#define BAS_TYPE_LONG 1 // 32-bit signed
|
|
#define BAS_TYPE_SINGLE 2 // 32-bit float
|
|
#define BAS_TYPE_DOUBLE 3 // 64-bit float
|
|
#define BAS_TYPE_STRING 4 // ref-counted dynamic string
|
|
#define BAS_TYPE_BOOLEAN 5 // True (-1) or False (0)
|
|
#define BAS_TYPE_ARRAY 6 // ref-counted array
|
|
#define BAS_TYPE_UDT 7 // ref-counted user-defined type
|
|
#define BAS_TYPE_OBJECT 8 // opaque host object (form, control, etc.)
|
|
#define BAS_TYPE_REF 9 // ByRef pointer to a BasValueT slot
|
|
#define BAS_TYPE_ELEM_REF 10 // ByRef array element: counted BasArrayT* + flat index
|
|
|
|
// ============================================================
|
|
// Stack operations
|
|
// ============================================================
|
|
|
|
#define OP_NOP 0x00
|
|
#define OP_PUSH_INT16 0x01 // [int16] push 16-bit integer
|
|
#define OP_PUSH_INT32 0x02 // [int32] push 32-bit integer
|
|
#define OP_PUSH_FLT64 0x04 // [float64] push 64-bit float
|
|
#define OP_PUSH_STR 0x05 // [uint16 idx] push string from constant pool
|
|
#define OP_PUSH_TRUE 0x06 // push boolean True (-1)
|
|
#define OP_PUSH_FALSE 0x07 // push boolean False (0)
|
|
#define OP_POP 0x08 // discard top of stack
|
|
#define OP_DUP 0x09 // duplicate top of stack
|
|
#define OP_STMT 0x0A // statement boundary without a line number (release builds that keep ON ERROR/RESUME semantics)
|
|
|
|
// ============================================================
|
|
// Variable access
|
|
// ============================================================
|
|
|
|
#define OP_LOAD_LOCAL 0x10 // [uint16 idx] push local variable
|
|
#define OP_STORE_LOCAL 0x11 // [uint16 idx] pop to local variable
|
|
#define OP_LOAD_GLOBAL 0x12 // [uint16 idx] push global variable
|
|
#define OP_STORE_GLOBAL 0x13 // [uint16 idx] pop to global variable
|
|
#define OP_LOAD_ARRAY 0x16 // [uint8 dims] indices on stack, array ref below
|
|
#define OP_STORE_ARRAY 0x17 // [uint8 dims] value, indices, array ref on stack
|
|
#define OP_LOAD_FIELD 0x18 // [uint16 fieldIdx] load UDT field
|
|
#define OP_STORE_FIELD 0x19 // [uint16 fieldIdx] store UDT field
|
|
#define OP_PUSH_LOCAL_ADDR 0x1A // [uint16 idx] push address of local (for ByRef)
|
|
#define OP_PUSH_GLOBAL_ADDR 0x1B // [uint16 idx] push address of global (for ByRef)
|
|
#define OP_STORE_ARRAY_FIELD 0x1C // [uint8 dims, uint16 fieldIdx] value, indices, array on stack
|
|
|
|
// ============================================================
|
|
// Arithmetic (integer)
|
|
// ============================================================
|
|
|
|
#define OP_ADD_INT 0x20
|
|
#define OP_SUB_INT 0x21
|
|
#define OP_MUL_INT 0x22
|
|
#define OP_IDIV_INT 0x23 // integer divide (\)
|
|
#define OP_MOD_INT 0x24
|
|
#define OP_NEG_INT 0x25
|
|
|
|
// ============================================================
|
|
// Arithmetic (float)
|
|
// ============================================================
|
|
|
|
#define OP_DIV_FLT 0x29 // float divide (/)
|
|
#define OP_POW 0x2B // exponentiation (^)
|
|
|
|
// ============================================================
|
|
// String operations
|
|
// ============================================================
|
|
|
|
#define OP_STR_CONCAT 0x30
|
|
#define OP_STR_LEFT 0x31
|
|
#define OP_STR_RIGHT 0x32
|
|
#define OP_STR_MID 0x33 // 3 args: str, start, len
|
|
#define OP_STR_MID2 0x34 // 2 args: str, start (to end)
|
|
#define OP_STR_LEN 0x35
|
|
#define OP_STR_INSTR 0x36 // 2 args: str, find
|
|
#define OP_STR_INSTR3 0x37 // 3 args: start, str, find
|
|
#define OP_STR_UCASE 0x38
|
|
#define OP_STR_LCASE 0x39
|
|
#define OP_STR_TRIM 0x3A
|
|
#define OP_STR_LTRIM 0x3B
|
|
#define OP_STR_RTRIM 0x3C
|
|
#define OP_STR_CHR 0x3D
|
|
#define OP_STR_ASC 0x3E
|
|
#define OP_STR_SPACE 0x3F
|
|
|
|
// ============================================================
|
|
// Comparison (push boolean result)
|
|
// ============================================================
|
|
|
|
#define OP_CMP_EQ 0x40
|
|
#define OP_CMP_NE 0x41
|
|
#define OP_CMP_LT 0x42
|
|
#define OP_CMP_GT 0x43
|
|
#define OP_CMP_LE 0x44
|
|
#define OP_CMP_GE 0x45
|
|
|
|
// ============================================================
|
|
// Logical / bitwise
|
|
// ============================================================
|
|
|
|
#define OP_AND 0x48
|
|
#define OP_OR 0x49
|
|
#define OP_NOT 0x4A
|
|
#define OP_XOR 0x4B
|
|
#define OP_EQV 0x4C
|
|
#define OP_IMP 0x4D
|
|
|
|
// ============================================================
|
|
// Control flow
|
|
// ============================================================
|
|
|
|
#define OP_JMP 0x50 // [int16 offset] unconditional jump
|
|
#define OP_JMP_TRUE 0x51 // [int16 offset] jump if TOS is true
|
|
#define OP_JMP_FALSE 0x52 // [int16 offset] jump if TOS is false
|
|
#define OP_CALL 0x53 // [uint16 addr] [uint8 argc] [uint8 baseSlot]
|
|
#define OP_GOSUB_RET 0x54 // pop the current frame's latest GOSUB return address and jump there (error 3 if none)
|
|
#define OP_RET 0x55 // return from subroutine
|
|
#define OP_RET_VAL 0x56 // return from function (value on stack)
|
|
#define OP_FOR_INIT 0x57 // [uint16 varIdx] [uint8 scope] [int16 skipOffset] init FOR, skip body if range empty
|
|
#define OP_FOR_NEXT 0x58 // [uint16 varIdx] [uint8 scope] [int16 loopTop]
|
|
#define OP_FOR_POP 0x59 // pop top FOR stack entry (for EXIT FOR)
|
|
#define OP_GOSUB 0x5A // [int16 offset] push the next instruction's address on the GOSUB stack, jump
|
|
|
|
// ============================================================
|
|
// Type conversion
|
|
// ============================================================
|
|
|
|
#define OP_CONV_INT_FLT 0x60 // int -> float
|
|
#define OP_CONV_FLT_INT 0x61 // float -> int (banker's rounding)
|
|
#define OP_CONV_INT_STR 0x62 // int -> string
|
|
#define OP_CONV_STR_INT 0x63 // string -> int (VAL)
|
|
#define OP_CONV_STR_FLT 0x65 // any -> double (VAL, CDBL)
|
|
#define OP_CONV_INT_LONG 0x66 // int16 -> int32
|
|
|
|
// ============================================================
|
|
// I/O
|
|
// ============================================================
|
|
|
|
#define OP_PRINT 0x70 // print TOS to current output
|
|
#define OP_PRINT_NL 0x71 // print newline
|
|
#define OP_PRINT_TAB 0x72 // print tab (14-column zones)
|
|
#define OP_LPRINT 0x73 // [uint8 BAS_LPRINT_BEGIN / BAS_LPRINT_END] route PRINT output to the printer for one LPRINT statement
|
|
#define OP_INPUT 0x74 // read line into string on stack
|
|
#define OP_FILE_OPEN 0x75 // [uint8 mode] filename, channel#, record length on stack
|
|
#define OP_FILE_CLOSE 0x76 // channel# on stack
|
|
#define OP_FILE_PRINT 0x77 // channel#, value on stack
|
|
#define OP_FILE_INPUT 0x78 // channel# on stack, push string
|
|
#define OP_FILE_EOF 0x79 // channel# on stack, push boolean
|
|
#define OP_FILE_LINE_INPUT 0x7A // channel# on stack, push string
|
|
|
|
// ============================================================
|
|
// UI / Event (used when form system is active)
|
|
// ============================================================
|
|
//
|
|
// All UI opcodes are name-based: control references, property names,
|
|
// method names, and form names are strings resolved at runtime.
|
|
// This allows third-party widget DXEs and new properties to work
|
|
// without recompiling the BASIC runtime.
|
|
//
|
|
// Stack convention:
|
|
// LOAD_PROP: ... controlRef propNameStr -> ... value
|
|
// STORE_PROP: ... controlRef propNameStr value -> ...
|
|
// CALL_METHOD: ... controlRef methodNameStr [args] -> ... [result]
|
|
// LOAD_FORM: ... formNameStr -> ... formRef
|
|
// CREATE_CTRL: ... formRef typeNameStr nameStr -> ... controlRef
|
|
|
|
#define OP_LOAD_PROP 0x80 // pop propName, pop ctrlRef, push property value
|
|
#define OP_STORE_PROP 0x81 // pop value, pop propName, pop ctrlRef, set property
|
|
#define OP_CALL_METHOD 0x82 // [uint8 argc] pop methodName, pop ctrlRef, pop args, push result
|
|
#define OP_LOAD_FORM 0x83 // pop formName string, push form reference
|
|
#define OP_UNLOAD_FORM 0x84 // pop formRef, unload it
|
|
#define OP_SHOW_FORM 0x85 // [uint8 modal] pop formRef, show it
|
|
#define OP_HIDE_FORM 0x86 // pop formRef, hide it
|
|
#define OP_DO_EVENTS 0x87
|
|
#define OP_MSGBOX 0x88 // pop flags, pop message string, push result
|
|
#define OP_INPUTBOX 0x89 // pop default, pop title, pop prompt, push result string
|
|
#define OP_ME_REF 0x8A // push current form reference
|
|
#define OP_CREATE_CTRL 0x8B // pop name, pop typeName, pop formRef, push controlRef
|
|
#define OP_FIND_CTRL 0x8C // pop ctrlName, pop formRef, push controlRef
|
|
#define OP_FIND_CTRL_IDX 0x8E // pop index, pop ctrlName, pop formRef, push ctrlRef
|
|
#define OP_LOAD_FORM_VAR 0x8F // [uint16 idx] push currentFormVars[idx]
|
|
|
|
// ============================================================
|
|
// Array / misc
|
|
// ============================================================
|
|
|
|
#define OP_DIM_ARRAY 0x90 // [uint8 dims] [uint8 type] bounds on stack
|
|
#define OP_REDIM 0x91 // [uint8 dims] [uint8 preserve] [uint8 type] bounds on stack (UDT: typeId, fieldCount after bounds)
|
|
#define OP_ERASE 0x92 // [uint8 mode] pop array ref, push replacement (BAS_ERASE_ZERO keeps the shape, BAS_ERASE_FREE undimensions)
|
|
#define OP_LBOUND 0x93 // [uint8 dim] array ref on stack
|
|
#define OP_UBOUND 0x94 // [uint8 dim] array ref on stack
|
|
#define OP_ON_ERROR 0x95 // [int16 handler] set error handler (0 = disable)
|
|
#define OP_RESUME 0x96 // resume after error
|
|
#define OP_RESUME_NEXT 0x97 // resume at next statement
|
|
#define OP_RAISE_ERR 0x98 // error number on stack
|
|
#define OP_ERR_NUM 0x99 // push current error number
|
|
#define OP_ERL 0x9A // push the source line of the current error (0 when none)
|
|
#define OP_STORE_FORM_VAR 0x9B // [uint16 idx] pop, store to currentFormVars[idx]
|
|
#define OP_PUSH_FORM_ADDR 0x9C // [uint16 idx] push ¤tFormVars[idx] (ByRef)
|
|
#define OP_CREATE_CTRL_EX 0x9D // pop parentRef, pop name, pop type, pop formRef, push ctrlRef
|
|
|
|
// ============================================================
|
|
// Math built-ins (single opcode each for common functions)
|
|
// ============================================================
|
|
|
|
#define OP_MATH_ABS 0xA0
|
|
#define OP_MATH_INT 0xA1 // floor
|
|
#define OP_MATH_FIX 0xA2 // truncate toward zero
|
|
#define OP_MATH_SGN 0xA3
|
|
#define OP_MATH_SQR 0xA4
|
|
#define OP_MATH_SIN 0xA5
|
|
#define OP_MATH_COS 0xA6
|
|
#define OP_MATH_TAN 0xA7
|
|
#define OP_MATH_ATN 0xA8
|
|
#define OP_MATH_LOG 0xA9
|
|
#define OP_MATH_EXP 0xAA
|
|
#define OP_MATH_RND 0xAB
|
|
#define OP_MATH_RANDOMIZE 0xAC // seed on stack (BAS_RANDOMIZE_TIMER_SEED = seed from the clock)
|
|
#define OP_RGB 0xAD // pop b, g, r; push LONG = (r<<16)|(g<<8)|b
|
|
#define OP_GET_RED 0xAE // pop LONG color; push (color>>16) & 0xFF
|
|
#define OP_GET_GREEN 0xAF // pop LONG color; push (color>>8) & 0xFF
|
|
|
|
// ============================================================
|
|
// Conversion built-ins
|
|
// ============================================================
|
|
|
|
#define OP_STR_VAL 0xB0 // VAL(s$) -> number
|
|
#define OP_STR_STRF 0xB1 // STR$(n) -> string
|
|
#define OP_STR_HEX 0xB2 // HEX$(n) -> string
|
|
#define OP_STR_STRING 0xB3 // STRING$(n, char) -> string
|
|
#define OP_STR_OCT 0xF3 // OCT$(n) -> string
|
|
#define OP_CONV_BOOL 0xF4 // CBOOL(n) -> -1 (true) or 0 (false)
|
|
#define OP_PUSH_ARR_ADDR 0xF5 // [uint8 dims] pop dims indices, pop array ref, push REF to element
|
|
|
|
// ============================================================
|
|
// Extended built-ins
|
|
// ============================================================
|
|
|
|
#define OP_MATH_TIMER 0xB4 // push seconds since midnight as DOUBLE
|
|
#define OP_DATE_STR 0xB5 // push DATE$ string "MM-DD-YYYY"
|
|
#define OP_TIME_STR 0xB6 // push TIME$ string "HH:MM:SS"
|
|
#define OP_SLEEP 0xB7 // pop seconds, sleep
|
|
#define OP_ENVIRON 0xB8 // pop env var name, push value string
|
|
#define OP_COMMAND 0xDC // pop argument index, push COMMAND$ (0 = every argument)
|
|
|
|
// ============================================================
|
|
// DATA/READ/RESTORE
|
|
// ============================================================
|
|
|
|
#define OP_READ_DATA 0xB9 // push next value from data pool
|
|
#define OP_RESTORE 0xBA // reset data pointer to 0
|
|
|
|
// ============================================================
|
|
// WRITE # (comma-delimited with quoted strings)
|
|
// ============================================================
|
|
|
|
#define OP_FILE_WRITE 0xBB // pop channel + value, write in WRITE format
|
|
#define OP_FILE_WRITE_SEP 0xBC // pop channel, write comma separator
|
|
#define OP_FILE_WRITE_NL 0xBD // pop channel, write newline
|
|
|
|
// ============================================================
|
|
// Random/Binary file I/O
|
|
// ============================================================
|
|
|
|
#define OP_FILE_GET 0xBE // pop type (+ current string value for STRING) + recno + channel, read record, push value
|
|
#define OP_FILE_PUT 0xBF // pop channel + recno + value, write record
|
|
#define OP_FILE_SEEK 0xC0 // pop channel + position, seek
|
|
#define OP_FILE_LOF 0xC1 // pop channel, push file length
|
|
#define OP_FILE_LOC 0xC2 // pop channel, push LOC (last byte / record / block, by mode)
|
|
#define OP_FILE_FREEFILE 0xC3 // push next free channel number
|
|
#define OP_FILE_INPUT_N 0xC4 // pop channel + n, read n chars, push string
|
|
|
|
// ============================================================
|
|
// Fixed-length strings and MID$ assignment
|
|
// ============================================================
|
|
|
|
#define OP_STR_FIXLEN 0xC5 // [uint16 len] pop string, pad/truncate, push
|
|
#define OP_STR_MID_ASGN 0xC6 // pop replacement, len, start, str; push modified
|
|
|
|
// Length pushed for a MID$ statement without one: replace as much of the
|
|
// replacement as fits (an explicit 0 replaces nothing).
|
|
#define BAS_MID_LEN_OMITTED (-1)
|
|
|
|
// ============================================================
|
|
// String append: var = var + x / var = var & x
|
|
// ============================================================
|
|
|
|
// [uint8 flags] [uint8 scope] [uint16 varIdx] pop x, store var + x (or
|
|
// var & x with BAS_APPEND_AMP) into the variable, appending in place
|
|
// when the variable's string is not shared.
|
|
#define OP_STR_APPEND 0xCE
|
|
#define BAS_APPEND_AMP 0x01 // flags: '&' semantics (both sides as strings) instead of '+'
|
|
|
|
// ============================================================
|
|
// PRINT USING
|
|
// ============================================================
|
|
|
|
#define OP_PRINT_USING 0xC7 // [uint8 flags] pop format + value, push format + formatted string (BAS_USING_FIRST / BAS_USING_LAST)
|
|
|
|
// ============================================================
|
|
// SPC(n) and TAB(n) with stack-based argument
|
|
// ============================================================
|
|
|
|
#define OP_PRINT_TAB_N 0xC8 // pop column count, print spaces to reach column
|
|
#define OP_PRINT_SPC_N 0xC9 // pop count, print that many spaces
|
|
#define OP_FORMAT 0xCA // pop format string + value, push formatted string
|
|
#define OP_SHELL 0xCB // pop command string, call system(), push return value
|
|
#define OP_COMPARE_MODE 0xCC // [uint8 mode] set string compare mode (BAS_COMPARE_MODE_*)
|
|
|
|
// ============================================================
|
|
// External library calls (DECLARE LIBRARY)
|
|
// ============================================================
|
|
//
|
|
// Calls native functions exported by dynamically loaded libraries.
|
|
// The VM resolves library + function name on first call via a host
|
|
// callback, caches the result, and marshals arguments through a
|
|
// second callback. This allows BASIC programs to use any library
|
|
// (serial, security, third-party) without recompiling the runtime.
|
|
|
|
#define OP_CALL_EXTERN 0xCD // [uint16 libNameIdx] [uint16 funcNameIdx] [uint8 argc] [uint8 retType]
|
|
|
|
#define OP_GET_BLUE 0xD0 // pop LONG color; push color & 0xFF
|
|
|
|
// App object
|
|
#define OP_APP_PATH 0xDD // push App.Path string
|
|
#define OP_APP_CONFIG 0xDE // push App.Config string
|
|
#define OP_APP_DATA 0xDF // push App.Data string
|
|
|
|
// INI file operations
|
|
#define OP_INI_READ 0xE0 // pop default, pop key, pop section, pop file, push string
|
|
#define OP_INI_WRITE 0xE1 // pop value, pop key, pop section, pop file
|
|
|
|
// Filesystem operations
|
|
#define OP_FS_KILL 0xE2 // pop filename, delete file
|
|
#define OP_FS_NAME 0xE3 // pop newname, pop oldname, rename
|
|
#define OP_FS_FILECOPY 0xE4 // pop dst, pop src, copy file
|
|
#define OP_FS_MKDIR 0xE5 // pop path, create directory
|
|
#define OP_FS_RMDIR 0xE6 // pop path, remove directory
|
|
#define OP_FS_CHDIR 0xE7 // pop path, change directory
|
|
#define OP_FS_CHDRIVE 0xE8 // pop drive, change drive
|
|
#define OP_FS_CURDIR 0xE9 // push current directory string
|
|
#define OP_FS_DIR 0xEA // pop pattern, push first matching filename
|
|
#define OP_FS_DIR_NEXT 0xEB // push next matching filename (no args)
|
|
#define OP_FS_FILELEN 0xEC // pop filename, push file length
|
|
#define OP_FS_GETATTR 0xED // pop filename, push attributes integer
|
|
#define OP_FS_SETATTR 0xEE // pop attrs, pop filename, set attributes
|
|
|
|
// Debug
|
|
#define OP_LINE 0xEF // [uint16 lineNum] set current source line for debugger
|
|
|
|
// Dynamic form construction
|
|
#define OP_CREATE_FORM 0xF0 // pop height, pop width, pop nameStr, push formRef
|
|
#define OP_SET_EVENT 0xF1 // pop handlerNameStr, pop eventNameStr, pop ctrlRef
|
|
#define OP_REMOVE_CTRL 0xF2 // pop ctrlNameStr, pop formRef
|
|
#define OP_FILE_PUT_UDT 0xF6 // pop layoutStr, pop udt, pop recno, pop channel; write packed fields
|
|
#define OP_FILE_SEEK_GET 0xF7 // pop channel, push 1-based file position (SEEK function)
|
|
|
|
// ============================================================
|
|
// Sound, joysticks and frame pacing (BasDeviceCallbacksT)
|
|
// ============================================================
|
|
|
|
#define OP_BEEP 0xD1 // beep; waits in foreground music mode
|
|
#define OP_SOUND 0xD2 // pop ticks, pop hz; play a tone (waits in foreground mode)
|
|
#define OP_PLAY 0xD3 // pop macro string; play it (waits in foreground mode)
|
|
#define OP_PLAY_COUNT 0xD4 // pop n (ignored), push notes waiting in the background (PLAY(n))
|
|
#define OP_STICK 0xD5 // pop n, push joystick position (STICK(n))
|
|
#define OP_STRIG 0xD6 // pop n, push joystick button state (STRIG(n))
|
|
#define OP_FRAME_WAIT 0xD7 // pop frames per second; wait for the next frame
|
|
|
|
// ============================================================
|
|
// Halt
|
|
// ============================================================
|
|
|
|
#define OP_END 0xFE // explicit END statement -- terminates program
|
|
#define OP_HALT 0xFF // implicit end of module
|
|
|
|
// OP_PRINT_USING flag bits: FIRST rewinds the format scan to the start
|
|
// of the format string, LAST appends the literal text that follows the
|
|
// consumed field.
|
|
#define BAS_USING_FIRST 0x01
|
|
#define BAS_USING_LAST 0x02
|
|
|
|
// OP_LPRINT operand: BEGIN sends the PRINT opcodes that follow to the
|
|
// printer, END sends the statement's output and goes back to the screen.
|
|
#define BAS_LPRINT_END 0
|
|
#define BAS_LPRINT_BEGIN 1
|
|
|
|
// OP_ERASE modes.
|
|
#define BAS_ERASE_ZERO 0 // fixed array: reset every element, keep the bounds
|
|
#define BAS_ERASE_FREE 1 // dynamic array: release the storage (undimensioned until REDIM)
|
|
|
|
// TYPE layout descriptors (built by the compiler as a string constant,
|
|
// walked by the VM): one BAS_TYPE_* byte per field in declaration order.
|
|
// A STRING entry is followed by a little-endian uint16 fixed length (0 =
|
|
// variable length); a BAS_TYPE_UDT entry by the nested type's uint16
|
|
// typeId and uint16 fieldCount and then its own entries. DIM/REDIM use
|
|
// the layout to build fully initialised instances, GET/PUT to pack and
|
|
// unpack the fields (variable strings carry an int16 length prefix).
|
|
#define BAS_LAYOUT_MAX_ENTRY_LEN 5
|
|
#define BAS_LAYOUT_MAX_LEN 1024
|
|
#define BAS_LAYOUT_MAX_DEPTH 16 // nesting cap for TYPE fields inside a layout
|
|
|
|
// OP_COMPARE_MODE operand values (OPTION COMPARE BINARY / TEXT).
|
|
#define BAS_COMPARE_MODE_BINARY 0
|
|
#define BAS_COMPARE_MODE_TEXT 1
|
|
|
|
// Seed value pushed by RANDOMIZE TIMER: OP_MATH_RANDOMIZE seeds from the
|
|
// clock when it pops this value.
|
|
#define BAS_RANDOMIZE_TIMER_SEED (-1)
|
|
|
|
// ============================================================
|
|
// Operand-size table and bytecode pattern helpers
|
|
// ============================================================
|
|
//
|
|
// Single machine-readable source of truth for the operand byte count of
|
|
// every opcode (the trailing comments above are human hints). Shared by
|
|
// the VM (RESUME NEXT statement walk) and the release compactor.
|
|
//
|
|
// The BAS_OPERAND_* names give the operand SIZE in bytes, not its layout:
|
|
// OP_DIM_ARRAY's two uint8 operands are BAS_OPERAND_U16 (2 bytes), OP_REDIM's
|
|
// three uint8 operands BAS_OPERAND_U8_U16 (3 bytes), and OP_CALL's
|
|
// [uint16 addr][uint8 argc][uint8 baseSlot] BAS_OPERAND_I32 (4 bytes). The
|
|
// per-opcode comments above describe the real layout.
|
|
|
|
// Operand widths (bytes after the 1-byte opcode).
|
|
#define BAS_OPERAND_NONE 0
|
|
#define BAS_OPERAND_U8 1
|
|
#define BAS_OPERAND_U16 2
|
|
#define BAS_OPERAND_U8_U16 3
|
|
#define BAS_OPERAND_I32 4
|
|
#define BAS_OPERAND_FOR 5 // [uint16 varIdx] [uint8 scope] [int16 offset]
|
|
#define BAS_OPERAND_CALL_EXTERN 6 // [uint16 lib] [uint16 func] [uint8 argc] [uint8 retType]
|
|
#define BAS_OPERAND_F64 8
|
|
#define BAS_OPERAND_UNKNOWN (-1)
|
|
|
|
// Byte offsets inside the FOR opcodes' BAS_OPERAND_FOR operand.
|
|
#define BAS_FOR_OFFSET_OPERAND (1 + BAS_OPERAND_U16 + BAS_OPERAND_U8) // opcode-relative position of the int16 jump offset
|
|
|
|
static inline int32_t basOpcodeOperandSize(uint8_t op);
|
|
static inline int16_t basReadI16LE(const uint8_t *p);
|
|
static inline int32_t basReadI32LE(const uint8_t *p);
|
|
static inline uint16_t basReadU16LE(const uint8_t *p);
|
|
static inline void basWriteI16LE(uint8_t *p, int16_t v);
|
|
static inline void basWriteI32LE(uint8_t *p, int32_t v);
|
|
static inline void basWriteU16LE(uint8_t *p, uint16_t v);
|
|
|
|
|
|
// Returns operand byte count for op, or BAS_OPERAND_UNKNOWN.
|
|
static inline int32_t basOpcodeOperandSize(uint8_t op) {
|
|
switch (op) {
|
|
case OP_NOP:
|
|
case OP_PUSH_TRUE: case OP_PUSH_FALSE:
|
|
case OP_POP: case OP_DUP: case OP_STMT:
|
|
case OP_ADD_INT: case OP_SUB_INT: case OP_MUL_INT:
|
|
case OP_IDIV_INT: case OP_MOD_INT: case OP_NEG_INT:
|
|
case OP_DIV_FLT: case OP_POW:
|
|
case OP_STR_CONCAT: case OP_STR_LEFT: case OP_STR_RIGHT:
|
|
case OP_STR_MID: case OP_STR_MID2: case OP_STR_LEN:
|
|
case OP_STR_INSTR: case OP_STR_INSTR3:
|
|
case OP_STR_UCASE: case OP_STR_LCASE:
|
|
case OP_STR_TRIM: case OP_STR_LTRIM: case OP_STR_RTRIM:
|
|
case OP_STR_CHR: case OP_STR_ASC: case OP_STR_SPACE:
|
|
case OP_CMP_EQ: case OP_CMP_NE: case OP_CMP_LT:
|
|
case OP_CMP_GT: case OP_CMP_LE: case OP_CMP_GE:
|
|
case OP_AND: case OP_OR: case OP_NOT:
|
|
case OP_XOR: case OP_EQV: case OP_IMP:
|
|
case OP_GOSUB_RET: case OP_RET: case OP_RET_VAL:
|
|
case OP_FOR_POP:
|
|
case OP_CONV_INT_FLT: case OP_CONV_FLT_INT:
|
|
case OP_CONV_INT_STR: case OP_CONV_STR_INT:
|
|
case OP_CONV_STR_FLT: case OP_CONV_INT_LONG:
|
|
case OP_PRINT: case OP_PRINT_NL: case OP_PRINT_TAB:
|
|
case OP_INPUT:
|
|
case OP_FILE_CLOSE: case OP_FILE_PRINT: case OP_FILE_INPUT:
|
|
case OP_FILE_EOF: case OP_FILE_LINE_INPUT:
|
|
case OP_LOAD_PROP: case OP_STORE_PROP:
|
|
case OP_LOAD_FORM: case OP_UNLOAD_FORM:
|
|
case OP_HIDE_FORM: case OP_DO_EVENTS:
|
|
case OP_MSGBOX: case OP_INPUTBOX: case OP_ME_REF:
|
|
case OP_CREATE_CTRL: case OP_FIND_CTRL: case OP_FIND_CTRL_IDX:
|
|
case OP_CREATE_CTRL_EX:
|
|
case OP_RESUME: case OP_RESUME_NEXT:
|
|
case OP_RAISE_ERR: case OP_ERR_NUM:
|
|
case OP_MATH_ABS: case OP_MATH_INT: case OP_MATH_FIX:
|
|
case OP_MATH_SGN: case OP_MATH_SQR: case OP_MATH_SIN:
|
|
case OP_MATH_COS: case OP_MATH_TAN: case OP_MATH_ATN:
|
|
case OP_MATH_LOG: case OP_MATH_EXP: case OP_MATH_RND:
|
|
case OP_MATH_RANDOMIZE:
|
|
case OP_RGB:
|
|
case OP_GET_RED: case OP_GET_GREEN: case OP_GET_BLUE:
|
|
case OP_STR_VAL: case OP_STR_STRF: case OP_STR_HEX:
|
|
case OP_STR_STRING: case OP_STR_OCT: case OP_CONV_BOOL:
|
|
case OP_MATH_TIMER: case OP_DATE_STR: case OP_TIME_STR:
|
|
case OP_SLEEP: case OP_ENVIRON: case OP_COMMAND:
|
|
case OP_READ_DATA: case OP_RESTORE:
|
|
case OP_FILE_WRITE: case OP_FILE_WRITE_SEP: case OP_FILE_WRITE_NL:
|
|
case OP_FILE_GET: case OP_FILE_PUT: case OP_FILE_SEEK:
|
|
case OP_FILE_LOF: case OP_FILE_LOC: case OP_FILE_FREEFILE:
|
|
case OP_FILE_INPUT_N:
|
|
case OP_STR_MID_ASGN:
|
|
case OP_PRINT_TAB_N: case OP_PRINT_SPC_N:
|
|
case OP_FORMAT: case OP_SHELL:
|
|
case OP_APP_PATH: case OP_APP_CONFIG: case OP_APP_DATA:
|
|
case OP_INI_READ: case OP_INI_WRITE:
|
|
case OP_FS_KILL: case OP_FS_NAME: case OP_FS_FILECOPY:
|
|
case OP_FS_MKDIR: case OP_FS_RMDIR: case OP_FS_CHDIR:
|
|
case OP_FS_CHDRIVE: case OP_FS_CURDIR: case OP_FS_DIR:
|
|
case OP_FS_DIR_NEXT: case OP_FS_FILELEN:
|
|
case OP_FS_GETATTR: case OP_FS_SETATTR:
|
|
case OP_CREATE_FORM: case OP_SET_EVENT: case OP_REMOVE_CTRL:
|
|
case OP_FILE_PUT_UDT: case OP_ERL: case OP_FILE_SEEK_GET:
|
|
case OP_BEEP: case OP_SOUND: case OP_PLAY:
|
|
case OP_PLAY_COUNT: case OP_STICK: case OP_STRIG:
|
|
case OP_FRAME_WAIT:
|
|
case OP_END: case OP_HALT:
|
|
return BAS_OPERAND_NONE;
|
|
|
|
case OP_LOAD_ARRAY: case OP_STORE_ARRAY:
|
|
case OP_PUSH_ARR_ADDR:
|
|
case OP_FILE_OPEN:
|
|
case OP_CALL_METHOD: case OP_SHOW_FORM:
|
|
case OP_LBOUND: case OP_UBOUND:
|
|
case OP_COMPARE_MODE: case OP_PRINT_USING:
|
|
case OP_ERASE: case OP_LPRINT:
|
|
return BAS_OPERAND_U8;
|
|
|
|
case OP_PUSH_INT16: case OP_PUSH_STR:
|
|
case OP_LOAD_LOCAL: case OP_STORE_LOCAL:
|
|
case OP_LOAD_GLOBAL: case OP_STORE_GLOBAL:
|
|
case OP_LOAD_FIELD: case OP_STORE_FIELD:
|
|
case OP_PUSH_LOCAL_ADDR: case OP_PUSH_GLOBAL_ADDR:
|
|
case OP_JMP: case OP_JMP_TRUE: case OP_JMP_FALSE:
|
|
case OP_GOSUB:
|
|
case OP_LOAD_FORM_VAR: case OP_STORE_FORM_VAR:
|
|
case OP_PUSH_FORM_ADDR:
|
|
case OP_DIM_ARRAY:
|
|
case OP_ON_ERROR:
|
|
case OP_STR_FIXLEN:
|
|
case OP_LINE:
|
|
return BAS_OPERAND_U16;
|
|
|
|
case OP_STORE_ARRAY_FIELD:
|
|
case OP_REDIM:
|
|
return BAS_OPERAND_U8_U16;
|
|
|
|
case OP_PUSH_INT32:
|
|
case OP_CALL:
|
|
case OP_STR_APPEND:
|
|
return BAS_OPERAND_I32;
|
|
|
|
case OP_FOR_INIT:
|
|
case OP_FOR_NEXT:
|
|
return BAS_OPERAND_FOR;
|
|
|
|
case OP_CALL_EXTERN:
|
|
return BAS_OPERAND_CALL_EXTERN;
|
|
|
|
case OP_PUSH_FLT64:
|
|
return BAS_OPERAND_F64;
|
|
|
|
default:
|
|
return BAS_OPERAND_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// Little-endian operand access
|
|
// ============================================================
|
|
//
|
|
// Bytecode is always little-endian regardless of the host; the compiler,
|
|
// the compactor and the serializer all go through these.
|
|
|
|
static inline int16_t basReadI16LE(const uint8_t *p) {
|
|
return (int16_t)basReadU16LE(p);
|
|
}
|
|
|
|
|
|
static inline int32_t basReadI32LE(const uint8_t *p) {
|
|
return (int32_t)((uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24));
|
|
}
|
|
|
|
|
|
static inline uint16_t basReadU16LE(const uint8_t *p) {
|
|
return (uint16_t)((uint16_t)p[0] | ((uint16_t)p[1] << 8));
|
|
}
|
|
|
|
|
|
static inline void basWriteI16LE(uint8_t *p, int16_t v) {
|
|
basWriteU16LE(p, (uint16_t)v);
|
|
}
|
|
|
|
|
|
static inline void basWriteI32LE(uint8_t *p, int32_t v) {
|
|
uint32_t u = (uint32_t)v;
|
|
|
|
p[0] = (uint8_t)(u & 0xFF);
|
|
p[1] = (uint8_t)((u >> 8) & 0xFF);
|
|
p[2] = (uint8_t)((u >> 16) & 0xFF);
|
|
p[3] = (uint8_t)((u >> 24) & 0xFF);
|
|
}
|
|
|
|
|
|
static inline void basWriteU16LE(uint8_t *p, uint16_t v) {
|
|
p[0] = (uint8_t)(v & 0xFF);
|
|
p[1] = (uint8_t)((v >> 8) & 0xFF);
|
|
}
|
|
|
|
|
|
#endif // DVXBASIC_OPCODES_H
|