User Tools

Site Tools


notes:comporg:spring2024:virconref

Vircon32 ASM Reference Guide

This document is based on Vircon32 DevTools v24.02.04; older versions will contain inconsistencies.

Token Value
VirconVersion 1
VirconRevision 0
FramesPerSecond 60
CyclesPerSecond 15000000
CyclesPerFrame CyclesPerSecond / FramesPerSecond
ScreenWidth 640
ScreenHeight 360
ScreenPixels ScreenWidth * ScreenHeight
GPUTextureSize 1024
GPUMaximumCartridgeTextures 256
GPURegionsPerTexture 4096
GPUPixelCapacityPerFrame 9 * ScreenPixels
GPUClearScreenPenalty -0.50f
GPUScalingPenalty +0.15f
GPURotationPenalty +0.25f
SPUMaximumCartridgeSounds 1024
SPUMaximumCartridgeSamples 1024 * 1024 * 256
SPUMaximumBiosSamples 1024 * 1024 * 1
SPUSoundChannels 16
SPUSamplingRate 44100
SPUSamplesPerFrame SPUSamplingRate / FramesPerSecond
MaximumCartridgeProgramROM 1024 * 1024 * 128
MaximumBiosProgramROM 1024 * 1024 * 1
RAMSize 1024 * 1024 * 4
MemoryCardSize 1024 * 256
MemoryBusSlaves 4
ControlBusSlaves 8
GamepadPorts 4

Assembler Data Directives (ROM)

keyword description
integer specify one or more integers
float specify one or more floats
string specify string sequence(s?)
pointer specify pointer(s?)
datafile specify datafile(s?)

Use commas to separate values (create “array” of values)

Vircon32 Instruction Set

control branch compare data convert logic int arithmetic float arithmetic float math
HLT JMP IEQ MOV CIF NOT IADD FADD FLR
WAIT CALL INE LEA CFI AND ISUB FSUB CEIL
RET IGT PUSH CIB OR IMUL FMUL ROUND
JT IGE POP CFB XOR IDIV FDIV SIN
JF ILT IN BNOT IMOD FMOD ACOS
ILE OUT SHL ISGN FSGN ATAN2
FEQ MOVS IMIN FMIN LOG
FNE SETS IMAX FMAX POW
FGT CMPS IABS FABS
FGE
FLT
FLE

Vircon32 Memory Map

Name Address/Range Description
RAMFirstAddress 0x00000000-0x003FFFFF read/write memory (16MB)
stack init address 0x003FFFFF default location of SP (last RAM address)
BiosProgramROMFirstAddress 0x10000000 Vircon32 BIOS
CartridgeProgramROMFirstAddress 0x20000000 Cartridge Data
MemoryCardRAMFirstAddress 0x30000000 Memory Card Data

Vircon32 I/O Port Layout

Port Address Vircon32 ID Description
0x000 TIM_FirstPort time related functionality
0x100 RNG_FirstPort random number generator
0x200 GPU_FirstPort graphics
0x300 SPU_FirstPort sound processing
0x400 INP_FirstPort input (game controllers)
0x500 CAR_FirstPort cartridge interface
0x600 MEM_FirstPort memory card

IOPorts

TIME

Type Port Name Description
IN 0x000 TIM_CurrentDate retrieve current date
IN 0x001 TIM_CurrentTime retrieve current time
IN 0x002 TIM_FrameCounter retrieve current frame count
IN 0x003 TIM_CycleCounter retrieve current cycle count

example: get current frame count

    in R0,  TIM_FrameCounter    ; load current frame count into R0

RNG

Type Port Name Description
IN 0x100 RNG_CurrentValue obtain pseudorandom value
OUT 0x100 RNG_CurrentValue Seed random number generator

GPU

Type Port Name Description
OUT 0x200 GPU_Command perform GPU operation
??? 0x201 GPU_RemainingPixels ???
OUT 0x202 GPU_ClearColor color to clear the screen with
??? 0x203 GPU_MultiplyColor ???
??? 0x204 GPU_ActiveBlending ???
OUT 0x204 GPU_SelectedTexture texture ID to select (-1 for BIOS)
OUT 0x205 GPU_SelectedRegion region ID to select
OUT 0x206 GPU_DrawingPointX set X position to draw selected region
OUT 0x207 GPU_DrawingPointY set Y position to draw selected region
??? 0x208 GPU_DrawingScaleX sets X scaling with a float as input
??? 0x209 GPU_DrawingScaleY sets Y scaling with a float as input
??? 0x20A GPU_DrawingAngle ???
OUT 0x20B GPU_RegionMinX set Min X coordinate for region
OUT 0x20C GPU_RegionMinY set Min Y coordinate for region
OUT 0x20D GPU_RegionMaxX set Max X coordinate for region
OUT 0x20E GPU_RegionMaxY set Max Y coordinate for region
OUT 0x20F GPU_RegionHotspotX set region Hotspot X coordinate
OUT 0x210 GPU_RegionHotspotY set region Hotspot Y coordinate

Commands that can be issued to the GPU:

value name description
0x10 GPUCommand_ClearScreen clears the screen using current clear color
0x11 GPUCommand_DrawRegion draws the selected region: Rotation off, Zoom off
0x12 GPUCommand_DrawRegionZoomed draws the selected region: Rotation off, Zoom on
0x13 GPUCommand_DrawRegionRotated draws the selected region: Rotation on , Zoom off
0x14 GPUCommand_DrawRegionRotozoomed draws the selected region: Rotation on , Zoom on

GPU Active Blending Port Commands

Active blending:

value name description
0x20 GPUBlendingMode_Alpha default rendering, uses alpha channel as transparency
0x21 GPUBlendingMode_Add colors are added (light effect), also called linear dodge
0x22 GPUBlendingMode_Subtract colors are subtracted (shadow effect), also called difference

SPU

Type Port Name Description
??? 0x300 SPU_Command ???
??? 0x301 SPU_GlobalVolume ???
OUT 0x302 SPU_SelectedSound ???
OUT 0x303 SPU_SelectedChannel ???
??? 0x304 SPU_SoundLength ???
??? 0x305 SPU_SoundPlayWithLoop ???
??? 0x306 SPU_SoundLoopStart ???
??? 0x307 SPU_SoundLoopEnd ???
??? 0x308 SPU_ChannelState ???
??? 0x309 SPU_ChannelAssignedSound ???
??? 0x30A SPU_ChannelVolume ???
??? 0x30B SPU_ChannelSpeed ???
??? 0x30C SPU_ChannelLoopEnabled ???
??? 0x30D SPU_ChannelPosition ???

SPU Commands

Commands for the SPU:

value name description
0x30 SPUCommand_PlaySelectedChannel if paused, it is resumed; if already playing, it is retriggered
0x31 SPUCommand_PauseSelectedChannel no effect if the channel was not playing
0x32 SPUCommand_StopSelectedChannel position is rewinded to sound start
0x33 SPUCommand_PauseAllChannels same as applying PauseChannel to all channels
0x34 SPUCommand_ResumeAllChannels same as applying PlayChannel to all paused channels
0x35 SPUCommand_StopAllChannels same as applying StopChannel to all channels

SPU Channel States

States of the sound channels:

value name description
0x40 SPUChannelState_Stopped channel is not playing, and will begin new reproduction on play
0x41 SPUChannelState_Paused channel is paused, and will resume reproduction on play
0x42 SPUChannelState_Playing channel is currently playing, until its assigned sound ends

INPUT

Type Port Name Description
IN 0x400 INP_SelectedGamepad Which gamepad is selected (0-3)
OUT 0x400 INP_SelectedGamepad Select indicated gamepad (0-3)
??? 0x401 INP_GamepadConnected ???
IN 0x402 INP_GamepadLeft Left Key input
IN 0x403 INP_GamepadRight Right Key input
IN 0x404 INP_GamepadUp Up key input
IN 0x405 INP_GamepadDown Down key input
IN 0x406 INP_GamepadButtonStart Enter key input
IN 0x407 INP_GamepadButtonA X key input
IN 0x408 INP_GamepadButtonB Z key input
IN 0x409 INP_GamepadButtonX S key input
IN 0x40A INP_GamepadButtonY A key input
IN 0x40B INP_GamepadButtonL Q key input
IN 0x40C INP_GamepadButtonR W key input
Type Port Name Description
IN? 0x500 CAR_Connected status of cartridge being connected
IN? 0x501 CAR_ProgramROMSize size of program ROM
IN? 0x502 CAR_NumberOfTextures number of cartridge textures
IN? 0x503 CAR_NumberOfSounds number of cartridge sounds

MEMCARD

Type Port Name Description
IN? 0x600 MEM_Connected status of memory card being connected

Instructions

There are 64 CPU opcodes, so instructions encode them in 6 bits. No invalid opcodes can exist. HLT is opcode 0 for safety: if an empty or invalid instruction is found, the CPU will stop execution.

opcode mneumonic category description
0x00 HLT control halt processing
0x01 WAIT control pause processing, wait for next frame
0x02 JMP branch unconditional jump to address
0x03 CALL branch call subroutine
0x04 RET branch return from subroutine
0x05 JT branch jump if true (1)
0x06 JF branch jump if false (0)
0x07 IEQ compare integer equal
0x08 INE compare integer not equal
0x09 IGT compare integer greater than
0x0A IGE compare integer greater than or equal
0x0B ILT compare integer less than
0x0C ILE compare integer less than or equal

HLT

WAIT

JMP

Unconditional jump. Forcibly redirect program flow to indicated address. The address is somewhere else in the program logic, likely identified by some set label.

Structure and variants

  • Variant 1:
    JMP { ImmediateValue }
  • Variant 2:
    JMP { Register1 }

Processing actions

  • Variant 1:
    InstructionPointer = ImmediateValue
  • Variant 2:
    InstructionPointer = Register1

Description

JMP performs an unconditional jump to the address specified by its operand. After processing this instruction the CPU will continue execution at the new address.

Examples

Jumping to a label (memory address/offset):

    jmp _label
    ...
_label:

Jumping to address stored in register:

    jmp R0

CALL

RET

JT

Jump if True: a conditional jump typically used following a comparison instruction, should the queried register contain a true (1) value, jump to indicated address.

NOTE

For the purposes of comparisons and conditional jumps on Vircon32:

  • true is 1 (technically non-zero)
  • false is 0

Structure and variants

  • Variant 1:
    JT { Register1 }, { ImmediateValue }
  • Variant 2:
    JT { Register1 }, { Register2 }

Effect

  • Variant 1:
    if Register1 != 0 then InstructionPointer = ImmediateValue
  • Variant 2:
    if Register1 != 0 then InstructionPointer = Register2

Description

JT performs a jump only if its first operand is true, i.e. non zero when taken as an integer. In that case its behavior is the same as an unconditional jump. Otherwise it has no effect.

JF

Jump if False: a conditional jump typically used following a comparison instruction, should the queried register contain a false (0) value, jump to indicated address.

NOTE

For the purposes of comparisons and conditional jumps on Vircon32:

  • true is 1 (technically non-zero)
  • false is 0

Structure and variants

  • Variant 1:
    JF { Register1 }, { ImmediateValue }
  • Variant 2:
    JF { Register1 }, { Register2 }

Effect

  • Variant 1:
    if Register1 == 0 then InstructionPointer = ImmediateValue
  • Variant 2:
    if Register1 == 0 then InstructionPointer = Register2

Description

JF performs a jump only if its first operand is false, i.e. zero when taken as an integer. In that case its behavior is the same as an unconditional jump. Otherwise it has no effect.

IEQ

Integer Compare Equality: comparisons allow us typically to evaluate two values, in accordance with some relational operation, resulting in a true (1) or false (0) result.

Should the first operand contain the same information as the second operand, the result will be true. Otherwise, false.

NOTE

For the purposes of comparisons and conditional jumps on Vircon32:

  • true is 1 (technically non-zero)
  • false is 0

There are six relational operations:

  • is equal to
  • is not equal to
  • is less than
  • is than or equal to
  • is greater than
  • is greater than or equal to

Structure and variants

  • Variant 1:
    IEQ { Register1 }, { ImmediateValue }
  • Variant 2:
    IEQ { Register1 }, { Register2 }

Processing actions

  • Variant 1:
    if Register1 == ImmediateValue then Register1 = 1 else Register1 = 0
  • Variant 2:
    if Register1 == Register2 then Register1 = 1 else Register1 = 0

Description

IEQ takes two operands interpreted as integers, and checks if they are equal. It will store the boolean result in the first operand, which is always a register.

INE

Integer Not Equal: comparisons allow us typically to evaluate two values, in accordance with some relational operation, resulting in a true (1) or false (0) result.

Here, we test to see if the first operand is not equal to the second operand. If they are equal, the result is false, otherwise, not being equal yields a result of true.

NOTE

For the purposes of comparisons and conditional jumps on Vircon32:

  • true is 1 (technically non-zero)
  • false is 0

There are six relational operations:

  • is equal to
  • is not equal to
  • is less than
  • is than or equal to
  • is greater than
  • is greater than or equal to

Structure and variants

  • Variant 1:
    INE { Register1 }, { ImmediateValue }
  • Variant 2:
    INE { Register1 }, { Register2 }

Processing actions

  • Variant 1:
    if Register1 != ImmediateValue then Register1 = 1 else Register1 = 0
  • Variant 2:
    if Register1 != Register2 then Register1 = 1 else Register1 = 0

Description

INE takes two operands interpreted as integers, and checks if they are different. It will store the boolean result in the first operand, which is always a register.

IGT

Integer Greater Than: comparisons allow us typically to evaluate two values, in accordance with some relational operation, resulting in a true (1) or false (0) result.

In this case, we are testing if the first operand is greater than the second operand.

NOTE

For the purposes of comparisons and conditional jumps on Vircon32:

  • true is 1 (technically non-zero)
  • false is 0

There are six relational operations:

  • is equal to
  • is not equal to
  • is less than
  • is than or equal to
  • is greater than
  • is greater than or equal to

Structure and variants

  • Variant 1:
    IGT { Register1 }, { ImmediateValue }
  • Variant 2:
    IGT { Register1 }, { Register2 }

Processing actions

  • Variant 1:
    if Register1 > ImmediateValue then Register1 = 1 else Register1 = 0
  • Variant 2:
    if Register1 > Register2 then Register1 = 1 else Register1 = 0

Description

IGT takes two operands interpreted as integers, and checks if the first one is greater than the second. It will store the boolean result in the first operand, which is always a register.

IGE

Integer Greater Than Or Equal: comparisons allow us typically to evaluate two values, in accordance with some relational operation, resulting in a true (1) or false (0) result.

In this case, we are testing if the first operand is greater than or equal to the second operand.

NOTE

For the purposes of comparisons and conditional jumps on Vircon32:

  • true is 1 (technically non-zero)
  • false is 0

There are six relational operations:

  • is equal to
  • is not equal to
  • is less than
  • is than or equal to
  • is greater than
  • is greater than or equal to

Structure and variants

  • Variant 1:
    IGE { Register1 }, { ImmediateValue }
  • Variant 2:
    IGE { Register1 }, { Register2 }

Processing actions

  • Variant 1:
    if Register1 >= ImmediateValue then Register1 = 1 else Register1 = 0
  • Variant 2:
    if Register1 >= Register2 then Register1 = 1 else Register1 = 0

Description

IGE takes two operands interpreted as integers, and checks if the first one is greater or equal to the second. It will store the boolean result in the first operand, which is always a register.

ILT

Integer Less Than: comparisons allow us typically to evaluate two values, in accordance with some relational operation, resulting in a true (1) or false (0) result.

In this case, we are testing if the first operand is less than the second operand.

NOTE

For the purposes of comparisons and conditional jumps on Vircon32:

  • true is 1 (technically non-zero)
  • false is 0

There are six relational operations:

  • is equal to
  • is not equal to
  • is less than
  • is than or equal to
  • is greater than
  • is greater than or equal to

Structure and variants

  • Variant 1:
    ILT { Register1 }, { ImmediateValue }
  • Variant 2:
    ILT { Register1 }, { Register2 }

Processing actions

  • Variant 1:
    if Register1 < ImmediateValue then Register1 = 1 else Register1 = 0
  • Variant 2:
    if Register1 < Register2 then Register1 = 1 else Register1 = 0

Description

ILT takes two operands interpreted as integers, and checks if the first one is less than the second. It will store the boolean result in the first operand, which is always a register.

ILE

Integer Less Than Or Equal: comparisons allow us typically to evaluate two values, in accordance with some relational operation, resulting in a true (1) or false (0) result.

In this case, we are testing if the first operand is less than or equal to the second operand.

NOTE

For the purposes of comparisons and conditional jumps on Vircon32:

  • true is 1 (technically non-zero)
  • false is 0

There are six relational operations:

  • is equal to
  • is not equal to
  • is less than
  • is than or equal to
  • is greater than
  • is greater than or equal to

Structure and variants

  • Variant 1:
    ILE { Register1 }, { ImmediateValue }
  • Variant 2:
    ILE { Register1 }, { Register2 }

Processing actions

  • Variant 1:
    if Register1 <= ImmediateValue then Register1 = 1 else Register1 = 0
  • Variant 2: if Register1 ⇐ Register2 then Register1 = 1 else Register1 = 0</code>

Description

ILE takes two operands interpreted as integers, and checks if the first one is less or equal to the second. It will store the boolean result in the first operand, which is always a register.

FEQ

FNE

FGT

FGE

FLT

FLE

MOV

MOVE: your general purpose data-copying instruction.

Addressing

MOVE, like other data-centric instructions, makes use of various addressing modes:

  • register: source/destination is an inbuilt CPU register
  • immediate: some literal constant (be it data or a memory address)
  • indirect: value isn't the data, but a memory address to where the data is. Think pointer dereference. It comes in 3 varieties:
  • indexed: an offset to some existing piece of data.
    • immediate: a literal constant (data or memory address)
    • indexed: used with immediate/register, but we can do additional math to get an offset from the address. Think pointer dereference on an array.
    • register: a CPU register

Indirect processing is accomplished with the [ ] (square brackets) surrounding the value we wish to dereference (we're not interested in the direct thing, but indirectly in what that thing contains).

Structure and variants

  • Variant 1:
    MOV { Register1 }, { ImmediateValue }
  • Variant 2:
    MOV { Register1 }, { Register2 }
  • Variant 3:
    MOV { Register1 }, [ { ImmediateValue } ]
  • Variant 4:
    MOV { Register1 }, [ { Register2 } ]
  • Variant 5:
    MOV { Register1 }, [ { Register2 } + { ImmediateValue } ]
  • Variant 6:
    MOV [ { ImmediateValue } ], { Register2 }
  • Variant 7:
    MOV [ { Register1 } ], { Register2 }
  • Variant 8:
    MOV [ { Register1 } + { ImmediateValue } ], { Register2 }

Processing actions

Register Destination
  • Immediate:
    Register1 = ImmediateValue
  • Register:
    Register1 = Register2
  • Indirect with Immediate reference:
    Register1 = Memory[ImmediateValue]
  • Indirect with Register reference:
    Register1 = Memory[Register2]
  • Indirect Indexed with Register:
    Register1 = Memory[Register2 + ImmediateValue]
Memory Destination
  • Indirect with Immediate reference:
    Memory[ImmediateValue] = Register2
  • Indirect with Register reference:
    Memory[Register1] = Register2
  • Indirect with Indexed reference:
    Memory[Register1 + ImmediateValue] = Register2

Description

MOV copies the value indicated in its second operand into the register or memory address indicated by its first operand. MOV is the most complex instruction to process because it needs to distinguish between 8 different addressing modes.

The instruction specifies which of the 8 modes to use in its “Addressing mode” field, being the possible values interpreted as follows:

MOV Addressing modes
Binary Destination Source
000 Register 1 Immediate Value
001 Register 1 Register 2
010 Register 1 Memory [Immediate Value]
011 Register 1 Memory [Register 2]
100 Register 1 Memory [Register 2 + Immediate Value]
101 Memory[Immediate Value] Register 2
110 Memory[Register 1] Register 2
111 Memory[Register 1 + Immediate Value] Register 2

LEA

Load Effective Address of a memory position.

Addressing

MOVE, like other data-centric instructions, makes use of various addressing modes:

  • register: source/destination is an inbuilt CPU register
  • immediate: some literal constant (be it data or a memory address)
  • indirect: value isn't the data, but a memory address to where the data is. Think pointer dereference. It comes in 3 varieties:
  • indexed: an offset to some existing piece of data.
    • immediate: a literal constant (data or memory address)
    • indexed: used with immediate/register, but we can do additional math to get an offset from the address. Think pointer dereference on an array.
    • register: a CPU register

Indirect processing is accomplished with the [ ] (square brackets) surrounding the value we wish to dereference (we're not interested in the direct thing, but indirectly in what that thing contains).

Structure and variants

  • Variant 1:
    LEA { Register1 }, [ { Register2 } ]
  • Variant 2:
    LEA { Register1 }, [ { Register2 } + { ImmediateValue } ]

Processing actions

  • Register:
    Register1 = Register2
  • Indexed:
    Register1 = Register2 + ImmediateValue

Description

LEA takes a memory address as second operand. It stores that address (not its contents) into the register given as first operand. The most useful case is when the address is given in the form pointer + offset, since the addition is automatically performed.

PUSH

Save to top of stack

Structure and variants

  • PUSH { Register1 }

Processing actions

  • Stack.Push(Register1)

Description

PUSH uses the CPU hardware stack to add the value contained in the given register at the top of the stack. When you PUSH a value onto the stack, the STACK POINTER (SP) is adjusted downward by one address offset (stack grows down).

POP

Load from top of stack

Structure and variants

  • POP { Register1 }

Processing actions

  • Register1 = Stack.Pop()

Description

POP uses the CPU hardware stack to remove a value from the top of the stack and write it in the given register. When you POP a value off the stack, the STACK POINTER (SP) is adjusted upward by one address offset (stack grows down, shrinks up).

        IN,         // Read from an I/O port
        OUT,        // Write to an I/O port
        
        // string operations
        MOVS,       // Copy string (HW memcpy)
        SETS,       // Set string (HW memset)
        CMPS,       // Compare string (HW memcmp)
        
        // data conversion
        CIF,        // Convert Integer to Float
        CFI,        // Convert Float to Integer
        CIB,        // Convert Integer to Boolean
        CFB,        // Convert Float to Boolean
        
        // binary operations
        NOT,        // Bitwise NOT
        AND,        // Bitwise AND
        OR,         // Bitwise OR
        XOR,        // Bitwise XOR
        BNOT,       // Boolean NOT
        SHL,        // Bit shift left
        
        // integer arithmetic
        IADD,       // Integer Addition
        ISUB,       // Integer Subtraction
        IMUL,       // Integer Multiplication
        IDIV,       // Integer Division
        IMOD,       // Integer Modulus
        ISGN,       // Integer Sign change
        IMIN,       // Integer Minimum
        IMAX,       // Integer Maximum
        IABS,       // Integer Absolute value
        
        // float arithmetic
        FADD,       // Float Addition
        FSUB,       // Float Subtraction
        FMUL,       // Float Multiplication
        FDIV,       // Float Division
        FMOD,       // Float Modulus
        FSGN,       // Float Sign change
        FMIN,       // Float Minimum
        FMAX,       // Float Maximum
        FABS,       // Float Absolute value
        
        // extended float operations
        FLR,        // Round down
        CEIL,       // Round up
        ROUND,      // Round to nearest integer
        SIN,        // Sine
        ACOS,       // Arc cosine
        ATAN2,      // Arc Tangent from x and y
        LOG,        // Natural logarithm
        POW         // Raise to a Power
    };
    
    // -----------------------------------------------------------------------------
    
    enum class CPURegisters: int
    {
        // all 16 general-purpose registers
        Register00 = 0,
        Register01,
        Register02,
        Register03,
        Register04,
        Register05,
        Register06,
        Register07,
        Register08,
        Register09,
        Register10,
        Register11,
        Register12,
        Register13,
        Register14,
        Register15,
        
        // alternate names for specific registers
        CountRegister       = 11,
        SourceRegister      = 12,
        DestinationRegister = 13,
        BasePointer         = 14,
        StackPointer        = 15
    };
    
    // -----------------------------------------------------------------------------
    
    enum class AddressingModes : unsigned int
    {
        RegisterFromImmediate = 0,      // syntax: MOV R1, 25
        RegisterFromRegister,           // syntax: MOV R1, R2
        RegisterFromImmediateAddress,   // syntax: MOV R1, [25]
        RegisterFromRegisterAddress,    // syntax: MOV R1, [R2]
        RegisterFromAddressOffset,      // syntax: MOV R1, [R2+25]
        ImmediateAddressFromRegister,   // syntax: MOV [25], R2
        RegisterAddressFromRegister,    // syntax: MOV [R1], R2
        AddressOffsetFromRegister       // syntax: MOV [R1+25], R2
    };
    
    // -----------------------------------------------------------------------------
    
    enum class CPUErrorCodes: uint32_t
    {
        InvalidMemoryRead = 0,
        InvalidMemoryWrite,
        InvalidPortRead,
        InvalidPortWrite,
        StackOverflow,
        StackUnderflow,
        DivisionError,
        ArcCosineError,
        ArcTangent2Error,
        LogarithmError,
        PowerError
    };
}
notes/comporg/spring2024/virconref.txt · Last modified: 2024/04/24 23:17 by gsalce