1055 lines
48 KiB
C
1055 lines
48 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)
|
|
SCOPE_MEMBER = 3 // class member variable of the running procedure's Me
|
|
} 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
|
|
#define BAS_TYPE_DATE 11 // Double serial date (dates.h), held in dblVal
|
|
#define BAS_TYPE_EMPTY 12 // Variant that holds nothing yet (Empty)
|
|
#define BAS_TYPE_NULL 13 // Variant Null (no valid data)
|
|
#define BAS_TYPE_INSTANCE 14 // ref-counted class instance or Collection (BasObjectT)
|
|
#define BAS_TYPE_VARIANT 15 // declared type only (AS VARIANT, array element type): holds any value, never a value's tag
|
|
|
|
// ============================================================
|
|
// 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_SPECIAL 0x03 // [uint8 BasSpecialValE] push Empty, Null or Nothing
|
|
#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
|
|
#define OP_LOAD_MEMBER 0x14 // [uint16 idx] push member variable idx of the running procedure's Me
|
|
#define OP_STORE_MEMBER 0x15 // [uint16 idx] pop to member variable idx of Me
|
|
#define OP_PUSH_MEMBER_ADDR 0x1D // [uint16 idx] push address of member variable idx of Me (for ByRef)
|
|
|
|
// ============================================================
|
|
// 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
|
|
#define OP_IS 0x46 // pop b, a (objects); push a Is b (the same object)
|
|
#define OP_OBJ_CHECK 0x47 // [uint16 class] TOS must be an object (BAS_OBJ_CHECK_ANY) or an instance of class (Nothing passes); else error
|
|
|
|
// ============================================================
|
|
// 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
|
|
#define OP_CALL_ME 0x5B // [uint16 addr] [uint8 argc] [uint8 baseSlot] OP_CALL whose callee keeps the caller's Me (a class calling its own procedure)
|
|
|
|
// ============================================================
|
|
// 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
|
|
#define OP_CONV_DATE 0x67 // any -> DATE (CDATE; a string is parsed as a date)
|
|
|
|
// ============================================================
|
|
// 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]
|
|
#define OP_ME_OBJ 0x8D // push the running procedure's Me (class module code)
|
|
|
|
// ============================================================
|
|
// 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
|
|
#define OP_NEW_OBJ 0x9E // [uint16 class] push a new instance of class (BAS_NEW_COLLECTION: a Collection), running its initialisation
|
|
#define OP_PROP_ASSIGN 0x9F // [uint8 argc] [uint8 flags] pop value, args, memberName, objRef; assign obj.member(args) = value (BAS_ASSIGN_SET: Set)
|
|
|
|
// ============================================================
|
|
// 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
|
|
|
|
// ============================================================
|
|
// Library functions without an opcode of their own
|
|
// ============================================================
|
|
|
|
// [uint8 BasBuiltinFnE] [uint8 argc] pop argc arguments (pushed in source
|
|
// order), push the result. Optional trailing arguments are omitted from
|
|
// argc and take their defaults in the VM.
|
|
#define OP_BUILTIN 0xD8
|
|
|
|
// Most arguments one OP_BUILTIN call takes (CHOOSE: index + 31 choices).
|
|
#define BAS_BUILTIN_MAX_ARGS 32
|
|
|
|
// OP_BUILTIN function numbers. Part of the bytecode ABI -- append only.
|
|
typedef enum {
|
|
BAS_FN_REPLACE = 0, // REPLACE$(s, find, repl [, start [, count [, compare]]])
|
|
BAS_FN_SPLIT = 1, // SPLIT(s [, delim [, limit [, compare]]]) -> STRING array
|
|
BAS_FN_JOIN = 2, // JOIN$(array [, delim])
|
|
BAS_FN_INSTRREV = 3, // INSTRREV(s, find [, start [, compare]])
|
|
BAS_FN_STRCOMP = 4, // STRCOMP(a, b [, compare])
|
|
BAS_FN_STRREVERSE = 5, // STRREVERSE$(s)
|
|
BAS_FN_FILTER = 6, // FILTER(array, match [, include [, compare]]) -> STRING array
|
|
BAS_FN_LSET = 7, // LSET target = s: (current target, s) -> s left-justified in LEN(target)
|
|
BAS_FN_RSET = 8, // RSET target = s: (current target, s) -> s right-justified in LEN(target)
|
|
BAS_FN_IIF = 9, // IIF(cond, a, b)
|
|
BAS_FN_CHOOSE = 10, // CHOOSE(index, a [, b ...])
|
|
BAS_FN_NOW = 11, // NOW
|
|
BAS_FN_DATE = 12, // DATE
|
|
BAS_FN_TIME = 13, // TIME
|
|
BAS_FN_DATESERIAL = 14, // DATESERIAL(y, m, d)
|
|
BAS_FN_TIMESERIAL = 15, // TIMESERIAL(h, m, s)
|
|
BAS_FN_DATEVALUE = 16, // DATEVALUE(d)
|
|
BAS_FN_TIMEVALUE = 17, // TIMEVALUE(d)
|
|
BAS_FN_YEAR = 18, // YEAR(d)
|
|
BAS_FN_MONTH = 19, // MONTH(d)
|
|
BAS_FN_DAY = 20, // DAY(d)
|
|
BAS_FN_WEEKDAY = 21, // WEEKDAY(d [, firstDayOfWeek])
|
|
BAS_FN_HOUR = 22, // HOUR(d)
|
|
BAS_FN_MINUTE = 23, // MINUTE(d)
|
|
BAS_FN_SECOND = 24, // SECOND(d)
|
|
BAS_FN_DATEADD = 25, // DATEADD(interval, n, d)
|
|
BAS_FN_DATEDIFF = 26, // DATEDIFF(interval, d1, d2 [, firstDayOfWeek [, firstWeekOfYear]])
|
|
BAS_FN_DATEPART = 27, // DATEPART(interval, d [, firstDayOfWeek [, firstWeekOfYear]])
|
|
BAS_FN_ISDATE = 28, // ISDATE(v)
|
|
BAS_FN_MONTHNAME = 29, // MONTHNAME(m [, abbrev])
|
|
BAS_FN_WEEKDAYNAME = 30, // WEEKDAYNAME(w [, abbrev [, firstDayOfWeek]])
|
|
BAS_FN_PARAMARRAY = 31, // (elementType, v0, v1, ...) -> 0-based array of the values: a ParamArray argument (compiler-generated, no BASIC name)
|
|
// The program screen (runtime/screen.c): statements return 0, which
|
|
// the compiler pops. Omitted arguments are pushed as 0 and flagged in
|
|
// a BAS_GIVEN_* / BAS_GFX_* mask pushed last.
|
|
BAS_FN_CLS = 32, // CLS [n]: (n, -1 when omitted)
|
|
BAS_FN_LOCATE = 33, // LOCATE [row] [, [col] [, cursor]]: (row, col, cursor, given)
|
|
BAS_FN_COLOR = 34, // COLOR [fore] [, back]: (fore, back, given)
|
|
BAS_FN_SCREEN = 35, // SCREEN mode
|
|
BAS_FN_WIDTH = 36, // WIDTH [cols] [, rows]: (cols, rows, given)
|
|
BAS_FN_PSET = 37, // PSET / PRESET [STEP](x, y) [, color]: (x, y, color, flags)
|
|
BAS_FN_LINE = 38, // LINE [[STEP](x1, y1)]-[STEP](x2, y2) [, color] [, B|BF] [, style]: (x1, y1, x2, y2, color, style, flags)
|
|
BAS_FN_CIRCLE = 39, // CIRCLE [STEP](x, y), r [, color [, start [, end [, aspect]]]]: (x, y, r, color, start, end, aspect, flags)
|
|
BAS_FN_PAINT = 40, // PAINT [STEP](x, y) [, paint [, border]]: (x, y, paint, border, flags)
|
|
BAS_FN_DRAW = 41, // DRAW macro$
|
|
BAS_FN_PALETTE = 42, // PALETTE [attr, color]: (attr, color, given)
|
|
BAS_FN_PALETTE_USING = 43, // PALETTE USING array[(first)]: (array, first, given)
|
|
BAS_FN_VIEW = 44, // VIEW [[SCREEN] (x1, y1)-(x2, y2) [, [fill] [, border]]]: (x1, y1, x2, y2, fill, border, flags)
|
|
BAS_FN_WINDOW = 45, // WINDOW [[SCREEN] (x1, y1)-(x2, y2)]: (x1, y1, x2, y2, flags)
|
|
BAS_FN_FULLSCREEN = 46, // FULLSCREEN [ON | OFF | expr]: (on)
|
|
BAS_FN_INKEY = 47, // INKEY$ -> "" / one character / CHR$(0) + scan code
|
|
BAS_FN_CSRLIN = 48, // CSRLIN -> cursor row
|
|
BAS_FN_POS = 49, // POS(n) -> cursor column
|
|
BAS_FN_POINT = 50, // POINT(x, y) -> colour (-1 outside the view); POINT(n) -> the last point
|
|
BAS_FN_SCREENFN = 51, // SCREEN(row, col [, colorflag]) -> character code or colour
|
|
BAS_FN_LINE_INPUT = 52, // LINE INPUT [prompt$;] v$: (prompt) -> the line typed
|
|
// Variants and objects
|
|
BAS_FN_VARTYPE = 53, // VARTYPE(v) -> vbEmpty .. vbArray + element type
|
|
BAS_FN_TYPENAME = 54, // TYPENAME(v) -> "Integer", "Nothing", class name ...
|
|
BAS_FN_ISEMPTY = 55, // ISEMPTY(v)
|
|
BAS_FN_ISNULL = 56, // ISNULL(v)
|
|
BAS_FN_ISNUMERIC = 57, // ISNUMERIC(v)
|
|
BAS_FN_ISOBJECT = 58, // ISOBJECT(v)
|
|
BAS_FN_ISARRAY = 59, // ISARRAY(v)
|
|
BAS_FN_CVAR = 60, // CVAR(v) -> v as it is
|
|
BAS_FN_TYPEOF = 61, // TYPEOF obj IS name: (obj, name) -> the object is of that type
|
|
BAS_FN_EACH_COUNT = 62, // FOR EACH (compiler-generated): (group) -> items in the array or Collection
|
|
BAS_FN_EACH_ITEM = 63 // FOR EACH (compiler-generated): (group, i) -> item i (0-based)
|
|
} BasBuiltinFnE;
|
|
|
|
// The Variant and object functions are numbered BAS_FN_VARTYPE through
|
|
// BAS_FN_EACH_ITEM.
|
|
#define BAS_FN_OBJECT_FIRST BAS_FN_VARTYPE
|
|
#define BAS_FN_OBJECT_LAST BAS_FN_EACH_ITEM
|
|
|
|
// OP_PUSH_SPECIAL operand. Part of the bytecode ABI.
|
|
typedef enum {
|
|
BAS_SPECIAL_EMPTY = 0,
|
|
BAS_SPECIAL_NULL = 1,
|
|
BAS_SPECIAL_NOTHING = 2
|
|
} BasSpecialValE;
|
|
|
|
// OP_NEW_OBJ / OP_OBJ_CHECK class operand values besides a class index.
|
|
#define BAS_NEW_COLLECTION 0xFFFF // OP_NEW_OBJ / OP_OBJ_CHECK: a Collection
|
|
#define BAS_OBJ_CHECK_ANY 0xFFFE // OP_OBJ_CHECK: any object
|
|
|
|
// OP_PROP_ASSIGN flags.
|
|
#define BAS_ASSIGN_SET 0x01 // Set obj.member = value (Property Set)
|
|
|
|
// VARTYPE results (VB's vbEmpty .. vbArray).
|
|
#define BAS_VT_EMPTY 0
|
|
#define BAS_VT_NULL 1
|
|
#define BAS_VT_INTEGER 2
|
|
#define BAS_VT_LONG 3
|
|
#define BAS_VT_SINGLE 4
|
|
#define BAS_VT_DOUBLE 5
|
|
#define BAS_VT_CURRENCY 6
|
|
#define BAS_VT_DATE 7
|
|
#define BAS_VT_STRING 8
|
|
#define BAS_VT_OBJECT 9
|
|
#define BAS_VT_ERROR 10
|
|
#define BAS_VT_BOOLEAN 11
|
|
#define BAS_VT_VARIANT 12
|
|
#define BAS_VT_DATAOBJECT 13
|
|
#define BAS_VT_USERTYPE 36
|
|
#define BAS_VT_ARRAY 8192
|
|
|
|
// The program-screen functions are numbered BAS_FN_CLS through
|
|
// BAS_FN_LINE_INPUT (the VM hands them to runtime/screen.c).
|
|
#define BAS_FN_SCREEN_FIRST BAS_FN_CLS
|
|
#define BAS_FN_SCREEN_LAST BAS_FN_LINE_INPUT
|
|
|
|
// Given-argument masks of LOCATE, COLOR, WIDTH and PALETTE (bit n: the
|
|
// n-th argument was written).
|
|
#define BAS_GIVEN_1 0x01
|
|
#define BAS_GIVEN_2 0x02
|
|
#define BAS_GIVEN_3 0x04
|
|
|
|
// Flags of the drawing argument blocks (PSET, LINE, CIRCLE, PAINT). The
|
|
// same blocks are the arguments of the Form / PictureBox PSet, Line and
|
|
// Circle methods, so both compile and run through one path.
|
|
#define BAS_GFX_STEP 0x01 // the (first) point is relative to the last point
|
|
#define BAS_GFX_COLOR 0x02 // a colour was given
|
|
#define BAS_GFX_PRESET 0x04 // PSET: PRESET (the default colour is the background)
|
|
#define BAS_GFX_START 0x08 // LINE: the start point was given
|
|
#define BAS_GFX_STEP2 0x10 // LINE: the end point is relative to the start point
|
|
#define BAS_GFX_BOX 0x20 // LINE: ,B (a rectangle)
|
|
#define BAS_GFX_BOX_FILL 0x40 // LINE: ,BF (a filled rectangle)
|
|
#define BAS_GFX_STYLE 0x80 // LINE: a style mask was given
|
|
#define BAS_GFX_ARC_START 0x08 // CIRCLE: a start angle was given
|
|
#define BAS_GFX_ARC_END 0x10 // CIRCLE: an end angle was given
|
|
#define BAS_GFX_ASPECT 0x20 // CIRCLE: an aspect was given
|
|
#define BAS_GFX_BORDER 0x08 // PAINT: a border colour was given
|
|
|
|
// Argument counts of the drawing blocks.
|
|
#define BAS_GFX_PSET_ARGS 4
|
|
#define BAS_GFX_LINE_ARGS 7
|
|
#define BAS_GFX_CIRCLE_ARGS 8
|
|
#define BAS_GFX_PAINT_ARGS 5
|
|
|
|
// Argument counts of the other screen statements with optional parts.
|
|
#define BAS_LOCATE_ARGS 4
|
|
#define BAS_COLOR_ARGS 3
|
|
#define BAS_WIDTH_ARGS 3
|
|
#define BAS_PALETTE_ARGS 3
|
|
#define BAS_VIEW_ARGS 7
|
|
#define BAS_WINDOW_ARGS 5
|
|
|
|
// VIEW / WINDOW flags.
|
|
#define BAS_VIEW_COORDS 0x01 // a rectangle was given (none: reset)
|
|
#define BAS_VIEW_SCREEN 0x02 // VIEW SCREEN / WINDOW SCREEN
|
|
#define BAS_VIEW_FILL 0x04 // VIEW: a fill colour was given
|
|
#define BAS_VIEW_BORDER 0x08 // VIEW: a border colour was given
|
|
|
|
// Object Print (Form / PictureBox .Print): one method call per item with
|
|
// (value, mode).
|
|
#define BAS_OBJ_PRINT_TEXT 0 // the value's text
|
|
#define BAS_OBJ_PRINT_NEWLINE 1 // end of the line (value unused)
|
|
#define BAS_OBJ_PRINT_ZONE 2 // ',' : the next print zone (value unused)
|
|
#define BAS_OBJ_PRINT_TAB 3 // TAB(n): column n
|
|
#define BAS_OBJ_PRINT_SPC 4 // SPC(n): n spaces
|
|
#define BAS_OBJ_PRINT_ARGS 2
|
|
|
|
// Compare argument of the string functions: OPTION COMPARE decides when
|
|
// it is omitted (vbUseCompareOption).
|
|
#define BAS_COMPARE_USE_OPTION (-1)
|
|
|
|
// ============================================================
|
|
// 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 bool basBuiltinArity(uint8_t fn, int32_t *minArgs, int32_t *maxArgs);
|
|
static inline uint8_t basConvOpForStore(uint8_t dataType);
|
|
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);
|
|
|
|
|
|
// Argument counts an OP_BUILTIN function accepts (the compiler checks
|
|
// calls against them, the VM checks the bytecode). False for an unknown
|
|
// function number.
|
|
static inline bool basBuiltinArity(uint8_t fn, int32_t *minArgs, int32_t *maxArgs) {
|
|
switch (fn) {
|
|
case BAS_FN_NOW: case BAS_FN_DATE: case BAS_FN_TIME:
|
|
case BAS_FN_INKEY: case BAS_FN_CSRLIN:
|
|
*minArgs = 0;
|
|
*maxArgs = 0;
|
|
return true;
|
|
|
|
case BAS_FN_STRREVERSE: case BAS_FN_DATEVALUE: case BAS_FN_TIMEVALUE:
|
|
case BAS_FN_YEAR: case BAS_FN_MONTH: case BAS_FN_DAY:
|
|
case BAS_FN_HOUR: case BAS_FN_MINUTE: case BAS_FN_SECOND:
|
|
case BAS_FN_ISDATE: case BAS_FN_CLS: case BAS_FN_SCREEN:
|
|
case BAS_FN_DRAW: case BAS_FN_FULLSCREEN: case BAS_FN_POS:
|
|
case BAS_FN_LINE_INPUT: case BAS_FN_VARTYPE: case BAS_FN_TYPENAME:
|
|
case BAS_FN_ISEMPTY: case BAS_FN_ISNULL: case BAS_FN_ISNUMERIC:
|
|
case BAS_FN_ISOBJECT: case BAS_FN_ISARRAY: case BAS_FN_CVAR:
|
|
case BAS_FN_EACH_COUNT:
|
|
*minArgs = 1;
|
|
*maxArgs = 1;
|
|
return true;
|
|
|
|
case BAS_FN_POINT:
|
|
*minArgs = 1;
|
|
*maxArgs = 2;
|
|
return true;
|
|
|
|
case BAS_FN_SCREENFN:
|
|
*minArgs = 2;
|
|
*maxArgs = 3;
|
|
return true;
|
|
|
|
case BAS_FN_COLOR:
|
|
*minArgs = BAS_COLOR_ARGS;
|
|
*maxArgs = BAS_COLOR_ARGS;
|
|
return true;
|
|
|
|
case BAS_FN_WIDTH:
|
|
*minArgs = BAS_WIDTH_ARGS;
|
|
*maxArgs = BAS_WIDTH_ARGS;
|
|
return true;
|
|
|
|
case BAS_FN_PALETTE: case BAS_FN_PALETTE_USING:
|
|
*minArgs = BAS_PALETTE_ARGS;
|
|
*maxArgs = BAS_PALETTE_ARGS;
|
|
return true;
|
|
|
|
case BAS_FN_LOCATE:
|
|
*minArgs = BAS_LOCATE_ARGS;
|
|
*maxArgs = BAS_LOCATE_ARGS;
|
|
return true;
|
|
|
|
case BAS_FN_WINDOW:
|
|
*minArgs = BAS_WINDOW_ARGS;
|
|
*maxArgs = BAS_WINDOW_ARGS;
|
|
return true;
|
|
|
|
case BAS_FN_VIEW:
|
|
*minArgs = BAS_VIEW_ARGS;
|
|
*maxArgs = BAS_VIEW_ARGS;
|
|
return true;
|
|
|
|
case BAS_FN_PSET:
|
|
*minArgs = BAS_GFX_PSET_ARGS;
|
|
*maxArgs = BAS_GFX_PSET_ARGS;
|
|
return true;
|
|
|
|
case BAS_FN_LINE:
|
|
*minArgs = BAS_GFX_LINE_ARGS;
|
|
*maxArgs = BAS_GFX_LINE_ARGS;
|
|
return true;
|
|
|
|
case BAS_FN_CIRCLE:
|
|
*minArgs = BAS_GFX_CIRCLE_ARGS;
|
|
*maxArgs = BAS_GFX_CIRCLE_ARGS;
|
|
return true;
|
|
|
|
case BAS_FN_PAINT:
|
|
*minArgs = BAS_GFX_PAINT_ARGS;
|
|
*maxArgs = BAS_GFX_PAINT_ARGS;
|
|
return true;
|
|
|
|
case BAS_FN_JOIN: case BAS_FN_WEEKDAY: case BAS_FN_MONTHNAME:
|
|
*minArgs = 1;
|
|
*maxArgs = 2;
|
|
return true;
|
|
|
|
case BAS_FN_WEEKDAYNAME:
|
|
*minArgs = 1;
|
|
*maxArgs = 3;
|
|
return true;
|
|
|
|
case BAS_FN_SPLIT:
|
|
*minArgs = 1;
|
|
*maxArgs = 4;
|
|
return true;
|
|
|
|
case BAS_FN_LSET: case BAS_FN_RSET: case BAS_FN_TYPEOF:
|
|
case BAS_FN_EACH_ITEM:
|
|
*minArgs = 2;
|
|
*maxArgs = 2;
|
|
return true;
|
|
|
|
case BAS_FN_STRCOMP:
|
|
*minArgs = 2;
|
|
*maxArgs = 3;
|
|
return true;
|
|
|
|
case BAS_FN_INSTRREV: case BAS_FN_FILTER: case BAS_FN_DATEPART:
|
|
*minArgs = 2;
|
|
*maxArgs = 4;
|
|
return true;
|
|
|
|
case BAS_FN_CHOOSE:
|
|
*minArgs = 2;
|
|
*maxArgs = BAS_BUILTIN_MAX_ARGS;
|
|
return true;
|
|
|
|
case BAS_FN_PARAMARRAY:
|
|
*minArgs = 1;
|
|
*maxArgs = BAS_BUILTIN_MAX_ARGS;
|
|
return true;
|
|
|
|
case BAS_FN_IIF: case BAS_FN_DATESERIAL: case BAS_FN_TIMESERIAL:
|
|
case BAS_FN_DATEADD:
|
|
*minArgs = 3;
|
|
*maxArgs = 3;
|
|
return true;
|
|
|
|
case BAS_FN_DATEDIFF:
|
|
*minArgs = 3;
|
|
*maxArgs = 5;
|
|
return true;
|
|
|
|
case BAS_FN_REPLACE:
|
|
*minArgs = 3;
|
|
*maxArgs = 6;
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
// The conversion a value goes through when it is stored where dataType
|
|
// is declared: a numeric, BOOLEAN or DATE type is converted to it, every
|
|
// other type (STRING, OBJECT, VARIANT, TYPE) stores the value as it is
|
|
// (OP_NOP). One source for the compiler's typed stores and the VM's
|
|
// stores into class member variables.
|
|
static inline uint8_t basConvOpForStore(uint8_t dataType) {
|
|
switch (dataType) {
|
|
case BAS_TYPE_INTEGER:
|
|
return OP_CONV_FLT_INT;
|
|
|
|
case BAS_TYPE_LONG:
|
|
return OP_CONV_INT_LONG;
|
|
|
|
case BAS_TYPE_SINGLE:
|
|
return OP_CONV_INT_FLT;
|
|
|
|
case BAS_TYPE_BOOLEAN:
|
|
return OP_CONV_BOOL;
|
|
|
|
case BAS_TYPE_DATE:
|
|
return OP_CONV_DATE;
|
|
|
|
default:
|
|
return OP_NOP;
|
|
}
|
|
}
|
|
|
|
|
|
// 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_IS: case OP_ME_OBJ:
|
|
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_CONV_DATE:
|
|
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:
|
|
case OP_PUSH_SPECIAL:
|
|
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:
|
|
case OP_BUILTIN:
|
|
case OP_LOAD_MEMBER: case OP_STORE_MEMBER:
|
|
case OP_PUSH_MEMBER_ADDR:
|
|
case OP_OBJ_CHECK: case OP_NEW_OBJ:
|
|
case OP_PROP_ASSIGN:
|
|
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_CALL_ME:
|
|
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
|