DVX_GUI/src/apps/kpunch/dvxbasic/compiler/opcodes.h
2026-09-29 17:03:51 -05:00

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 &currentFormVars[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