// 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 PC from eval stack, jump (GOSUB return)
#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)

// ============================================================
// 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_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  // array ref on stack
#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_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

// ============================================================
// 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 current position
#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

// ============================================================
// PRINT USING
// ============================================================

#define OP_PRINT_USING     0xC7  // pop format + value, push formatted string

// ============================================================
// 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

// ============================================================
// Halt
// ============================================================

#define OP_END             0xFE  // explicit END statement -- terminates program
#define OP_HALT            0xFF  // implicit end of module

// ============================================================
// 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.


// 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 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)

// GOSUB call sequence: OP_PUSH_INT32 <returnAddr> OP_JMP <offset>, where
// returnAddr equals the address right after the OP_JMP.
#define BAS_GOSUB_PATTERN_LEN   8   // PUSH_INT32(1+4) + JMP(1+2)
#define BAS_GOSUB_JMP_OFFSET    5   // byte offset of the OP_JMP inside the pattern

// 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_ERASE:
        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_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_USING:
        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_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:
            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_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:
            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 int32 operand read (bytecode is always LE regardless of host).
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));
}


// True when code[pos] starts a GOSUB call sequence (see BAS_GOSUB_PATTERN_LEN).
static inline bool basIsGosubPush(const uint8_t *code, int32_t codeLen, int32_t pos) {
    if (pos + BAS_GOSUB_PATTERN_LEN > codeLen) {
        return false;
    }

    if (code[pos] != OP_PUSH_INT32 || code[pos + BAS_GOSUB_JMP_OFFSET] != OP_JMP) {
        return false;
    }

    return basReadI32LE(code + pos + 1) == pos + BAS_GOSUB_PATTERN_LEN;
}

#endif // DVXBASIC_OPCODES_H
