Fifth - virtual machine opcodes 20 – 29
Table of Contents
1. Overview
This document describes virtual machine opcodes 20 through 29. These are fundamental instructions for memory access, stack manipulation, arithmetic operations, and value comparison.
1.1. Quick Reference
| Opcode | Name | Stack Effect | Bytecode Size | Purpose |
|---|---|---|---|---|
| 20 | @ | addr – value | 1 byte | Fetch 32-bit value from memory |
| 21 | ! | value addr -- | 1 byte | Store 32-bit value to memory |
| 22 | over | n1 n2 – n1 n2 n1 | 1 byte | Duplicate second stack item |
| 23 | swap | n1 n2 – n2 n1 | 1 byte | Exchange top two stack items |
| 24 | + | n1 n2 – n1+n2 | 1 byte | Add two 32-bit values |
| 25 | - | n1 n2 – n1-n2 | 1 byte | Subtract two 32-bit values |
| 26 | * | n1 n2 – n1*n2 | 1 byte | Multiply two 32-bit values |
| 27 | / | n1 n2 – n1/n2 | 1 byte | Divide two 32-bit values |
| 28 | > | n1 n2 – flag | 1 byte | Greater than comparison |
| 29 | < | n1 n2 – flag | 1 byte | Less than comparison |
2. 20: @ (Fetch)
| Property | Value |
|---|---|
| Opcode | 20 |
| Name | @ |
| Stack | ( addr – value ) |
| Bytecode | Single byte: 14 |
2.1. Description
Reads a 32-bit value from the specified memory address and replaces the address with the fetched value on the data stack. This is the primary operation for reading data from memory.
2.2. Memory Access
The operation performs virtual address translation:
- Takes virtual address from top of data stack
- Converts to physical address by adding
xms_addr - Reads 32-bit value from physical memory
- Replaces address with value on stack
2.3. When to Use
- Reading variables and data structures from memory
- Accessing array elements
- Loading values for computation
- Implementing high-level words that need memory access
- Reading hardware registers (when mapped to memory)
2.4. Example
Read a value from address 0x1000:
03 00 10 00 00 ; num 0x1000 - push address 0x1000
14 ; @ - read 32-bit value from 0x1000
; stack now contains the value from address 0x1000
In Fifth source code:
0x1000 @ \ read 32-bit value from address 0x1000
2.5. Important Notes
- This is the 32-bit equivalent of
c@(opcode 16) - Virtual address translation provides memory management
- Reads full 32-bit words, not bytes
- The address is consumed and replaced with the value
- No bounds checking is performed
2.6. Common Patterns
2.6.1. Read and Use
addr @ . \ read from address and display
2.6.2. Array Access
base index 4 * + @ \ read array[index] (32-bit elements)
3. 21: ! (Store)
| Property | Value |
|---|---|
| Opcode | 21 |
| Name | ! |
| Stack | ( value addr – ) |
| Bytecode | Single byte: 15 |
3.1. Description
Stores a 32-bit value to the specified memory address. Both the value and address are consumed from the data stack, leaving a clean stack.
3.2. Memory Access
The operation performs virtual address translation:
- Takes address from top of stack
- Takes value to store from second on stack
- Converts virtual address to physical address
- Stores 32-bit value to physical memory
- Removes both arguments from stack
3.3. When to Use
- Writing variables and data structures to memory
- Storing array elements
- Saving computation results
- Implementing high-level words that modify memory
- Writing to hardware registers (when mapped to memory)
3.4. Example
Store value 0x12345678 to address 0x1000:
03 78 56 34 12 ; num 0x12345678 - push value to store
03 00 10 00 00 ; num 0x1000 - push destination address
15 ; ! - store value to address
; stack is now empty
In Fifth source code:
0x12345678 0x1000 ! \ store 0x12345678 at address 0x1000
3.5. Important Notes
- This is the 32-bit equivalent of
c!(opcode 17) - Virtual address translation provides memory protection
- Order is value then addr (Forth standard: value to address)
- Both arguments are consumed (stack cleaned up)
- No bounds checking is performed
- Overwrites existing data at the target address
3.6. Common Patterns
3.6.1. Store Constant
42 addr ! \ store constant 42 at addr
3.6.2. Array Assignment
value base index 4 * + ! \ array[index] = value (32-bit elements)
3.6.3. Computed Store
a b + addr ! \ store (a + b) at addr
4. 22: over (Over)
| Property | Value |
|---|---|
| Opcode | 22 |
| Name | over |
| Stack | ( n1 n2 – n1 n2 n1 ) |
| Bytecode | Single byte: 16 |
4.1. Description
Duplicates the second item on the data stack, placing a copy on top. This allows access to the second item without disturbing the top item.
4.2. Stack Behavior
The operation preserves the original order while adding a copy:
Before: [ n1 n2 ] (n2 = TOS, n1 = second) After: [ n1 n2 n1 ] (new n1 = TOS)
4.3. When to Use
- Preserving values while using the top item
- Accessing the second item when you need to keep the top
- Setting up multiple uses of the same value
- Implementing arithmetic operations that need the original value
- Creating stack patterns for complex operations
4.4. Example
Duplicate the second item:
03 01 00 00 00 ; num 1 - stack: [ 1 ] 03 02 00 00 00 ; num 2 - stack: [ 1 2 ] 16 ; over - stack: [ 1 2 1 ]
In Fifth source code:
1 2 over \ stack: 1 2 1
4.5. Important Notes
- Essential for preserving values while using the top item
- More efficient than dup+swap+drop combination
- Increases stack depth by 1
- Often paired with arithmetic operations
- Fundamental stack manipulation word
4.6. Common Patterns
4.6.1. Compare Without Consuming
a b over > \ compare a with b, keeping both on stack
4.6.2. Use Value Twice
a b over + \ compute a + b, keeping a on stack
4.6.3. Save Address for Later Use
addr over @ \ read from addr, keeping addr for later use
5. 23: swap (Swap)
| Property | Value |
|---|---|
| Opcode | 23 |
| Name | swap |
| Stack | ( n1 n2 – n2 n1 ) |
| Bytecode | Single byte: 17 |
5.1. Description
Exchanges the top two values on the data stack. This is essential for reordering arguments when the natural stack order doesn't match the required order for operations.
5.2. Stack Behavior
The operation reverses the order of the top two items:
Before: [ n1 n2 ] (n2 = TOS, n1 = second) After: [ n2 n1 ] (n1 = TOS, n2 = second)
5.3. When to Use
- Reordering arguments for arithmetic operations
- Fixing stack order after computations
- Implementing algorithms that need specific operand order
- Exchanging values in sorting algorithms
- Correcting stack order for function calls
5.4. Example
Exchange the top two values:
03 01 00 00 00 ; num 1 - stack: [ 1 ] 03 02 00 00 00 ; num 2 - stack: [ 1 2 ] 17 ; swap - stack: [ 2 1 ]
In Fifth source code:
1 2 swap \ stack: 2 1
5.5. Important Notes
- Fundamental stack manipulation operation
- Used extensively for argument reordering
- More efficient than multiple push/pop operations
- Stack depth remains unchanged
- Essential for correct operand order in arithmetic
5.6. Common Patterns
5.6.1. Fix Subtraction Order
min max swap - \ compute max - min
5.6.2. Correct Division Order
divisor dividend swap / \ compute dividend / divisor
5.6.3. Exchange Variables
addr1 @ addr2 @ swap \ read two values and exchange them addr1 ! addr2 ! \ store them back in exchanged order
6. 24: + (Add)
| Property | Value |
|---|---|
| Opcode | 24 |
| Name | + |
| Stack | ( n1 n2 – n1+n2 ) |
| Bytecode | Single byte: 18 |
6.1. Description
Adds the top two values on the data stack, replacing them with the sum. This is a fundamental arithmetic operation for mathematical computations.
6.2. Operation
The operation performs unsigned 32-bit addition:
Before: [ n1 n2 ] (n2 = TOS, n1 = second) After: [ sum ] (sum = n1 + n2)
6.3. When to Use
- Basic arithmetic calculations
- Computing sums and totals
- Pointer arithmetic
- Incrementing values by arbitrary amounts
- Mathematical expressions and formulas
6.4. Example
Add two numbers:
03 05 00 00 00 ; num 5 - stack: [ 5 ] 03 03 00 00 00 ; num 3 - stack: [ 5 3 ] 18 ; + - stack: [ 8 ]
In Fifth source code:
5 3 + \ result: 8
6.5. Important Notes
- Performs unsigned 32-bit addition
- Overflow wraps around modulo 2^32
- Operates in place for efficiency (no extra stack slot needed)
- Reduces stack depth by 1
- Commutative operation (a + b = b + a)
6.6. Common Patterns
6.6.1. Increment by Value
value increment + \ value = value + increment
6.6.2. Pointer Arithmetic
ptr 4 + \ advance pointer by 4 bytes
6.6.3. Sum Multiple Values
a b + c + d + \ compute a + b + c + d
6.7. Edge Cases
0xFFFFFFFF + 1 = 0(wrap around)Any number + 0 = same number- Large additions may overflow
7. 25: - (Subtract)
| Property | Value |
|---|---|
| Opcode | 25 |
| Name | - |
| Stack | ( n1 n2 – n1-n2 ) |
| Bytecode | Single byte: 19 |
7.1. Description
Subtracts the top value from the second value on the data stack. The result replaces both values. This operation is not commutative - order matters.
7.2. Operation
The operation performs unsigned 32-bit subtraction:
Before: [ n1 n2 ] (n2 = TOS, n1 = second) After: [ diff ] (diff = n1 - n2)
7.3. When to Use
- Computing differences between values
- Decrementing by arbitrary amounts
- Distance calculations
- Mathematical expressions requiring subtraction
- Reverse arithmetic operations
7.4. Example
Subtract two numbers:
03 0A 00 00 00 ; num 10 - stack: [ 10 ] 03 03 00 00 00 ; num 3 - stack: [ 10 3 ] 19 ; - - stack: [ 7 ]
In Fifth source code:
10 3 - \ result: 7 (10 - 3)
7.5. Important Notes
- Performs unsigned 32-bit subtraction
- Underflow wraps around modulo 2^32
- Operates in place for efficiency
- Reduces stack depth by 1
- NOT commutative (a - b ≠ b - a)
- Order: second item minus top item
7.6. Common Patterns
7.6.1. Decrement by Value
value decrement - \ value = value - decrement
7.6.2. Distance Calculation
end start - \ compute distance from start to end
7.6.3. Reverse Subtraction
a b swap - \ compute b - a instead of a - b
7.7. Edge Cases
0 - 1 = 0xFFFFFFFF(wrap around)Any number - 0 = same numbern - n = 0for any n
8. 26: * (Multiply)
| Property | Value |
|---|---|
| Opcode | 26 |
| Name | * |
| Stack | ( n1 n2 – n1*n2 ) |
| Bytecode | Single byte: 1A |
8.1. Description
Multiplies the top two values on the data stack using signed 32-bit multiplication. The result is the low 32 bits of the product.
8.2. Operation
The operation performs signed 32-bit multiplication:
Before: [ n1 n2 ] (n2 = TOS, n1 = second) After: [ product ] (product = n1 * n2)
8.3. When to Use
- Multiplication operations
- Area calculations
- Scaling values
- Mathematical formulas
- Computing products and multiples
8.4. Example
Multiply two numbers:
03 05 00 00 00 ; num 5 - stack: [ 5 ] 03 03 00 00 00 ; num 3 - stack: [ 5 3 ] 1A ; * - stack: [ 15 ]
In Fifth source code:
5 3 * \ result: 15
8.5. Important Notes
- Uses IMUL instruction (signed multiplication)
- Result is low 32 bits only (high bits in edx are discarded)
- Handles both positive and negative numbers correctly
- Overflow behavior: result wraps around modulo 2^32
- Reduces stack depth by 1
- Commutative operation (a * b = b * a)
8.6. Common Patterns
8.6.1. Area Calculation
width height * \ compute area of rectangle
8.6.2. Scaling
value scale * \ scale value by factor
8.6.3. Power of Two
value 2 * \ multiply by 2 (equivalent to shift left by 1)
8.7. Edge Cases
0 * anything = 01 * anything = anything(-1) * (-1) = 1- Large products may overflow (wrap around)
9. 27: / (Divide)
| Property | Value |
|---|---|
| Opcode | 27 |
| Name | / |
| Stack | ( n1 n2 – n1/n2 ) |
| Bytecode | Single byte: 1B |
9.1. Description
Divides the second value by the top value using signed 32-bit division. Returns the quotient only (remainder is discarded).
9.2. Operation
The operation performs signed 32-bit division:
Before: [ n1 n2 ] (n2 = divisor = TOS, n1 = dividend = second) After: [ quotient ] (quotient = n1 / n2)
9.3. When to Use
- Division operations
- Averaging calculations
- Ratio computations
- Mathematical formulas requiring division
- Splitting quantities into equal parts
9.4. Example
Divide two numbers:
03 0F 00 00 00 ; num 15 - stack: [ 15 ] 03 03 00 00 00 ; num 3 - stack: [ 15 3 ] 1B ; / - stack: [ 5 ]
In Fifth source code:
15 3 / \ result: 5 (15 / 3)
9.5. Important Notes
- Uses IDIV instruction (signed division)
- Returns quotient only (remainder discarded)
- Handles both positive and negative numbers correctly
- Division by zero causes CPU exception (not handled)
- Reduces stack depth by 1
- NOT commutative (a / b ≠ b / a)
- Order: second item divided by top item
9.6. Common Patterns
9.6.1. Average Calculation
a b + 2 / \ compute average of a and b
9.6.2. Split into Parts
total parts / \ divide total into equal parts
9.6.3. Correct Division Order
dividend divisor swap / \ compute dividend / divisor
9.7. Edge Cases
- Division by zero: CPU exception (crash)
Anything / 1 = anything0 / anything = 0- Negative division follows standard signed rules
9.8. WARNING: Division by zero will crash the emulator!
10. 28: > (Greater Than)
| Property | Value |
|---|---|
| Opcode | 28 |
| Name | > |
| Stack | ( n1 n2 – flag ) |
| Bytecode | Single byte: 1C |
10.1. Description
Compares the second value (a) with the top value (b). Returns -1 (true) if a > b, otherwise returns 0 (false). This is a fundamental comparison operation for conditional logic.
10.2. Operation
The operation performs signed 32-bit comparison:
Before: [ n1 n2 ] (n2 = TOS, n1 = second) After: [ flag ] (flag = -1 if n1 > n2, 0 otherwise)
10.3. Truth Value Convention
-1 = true(all bits set, non-zero)0 = false(zero)
10.4. When to Use
- Conditional branching and logic
- Sorting algorithms
- Range checking
- Decision making in programs
- Implementing high-level comparison words
10.5. Example
Compare two numbers:
03 05 00 00 00 ; num 5 - stack: [ 5 ] 03 03 00 00 00 ; num 3 - stack: [ 5 3 ] 1C ; > - stack: [ 0 ] (false, 5 is not > 3)
In Fifth source code:
3 5 > \ result: 0 (false) 5 3 > \ result: -1 (true)
10.6. Important Notes
- Returns -1 for true (all bits set), 0 for false
- Standard Forth truth value convention
- Signed comparison (works for negative numbers)
- Reduces stack depth by 1
- NOT commutative (a > b ≠ b > a)
10.7. Common Patterns
10.7.1. Conditional Execution
a b > if \ code for when a > b then
10.7.2. Range Check
value max > abort" Value too large"
10.7.3. Sorting Logic
a b > if swap then \ ensure smaller value is on top
10.8. Edge Cases
- Equal values return 0 (false)
- Negative numbers compared correctly
- Works with full 32-bit signed range
11. 29: < (Less Than)
| Property | Value |
|---|---|
| Opcode | 29 |
| Name | < |
| Stack | ( n1 n2 – flag ) |
| Bytecode | Single byte: 1D |
11.1. Description
Compares the second value (a) with the top value (b). Returns -1 (true) if a < b, otherwise returns 0 (false). This is the complement of the greater than operation and essential for conditional logic.
11.2. Operation
The operation performs signed 32-bit comparison:
Before: [ n1 n2 ] (n2 = TOS, n1 = second) After: [ flag ] (flag = -1 if n1 < n2, 0 otherwise)
11.3. Truth Value Convention
-1 = true(all bits set, non-zero)0 = false(zero)
11.4. When to Use
- Conditional branching and logic
- Sorting algorithms
- Range checking
- Decision making in programs
- Implementing high-level comparison words
- Complementary logic to greater than
11.5. Example
Compare two numbers:
03 03 00 00 00 ; num 3 - stack: [ 3 ] 03 05 00 00 00 ; num 5 - stack: [ 3 5 ] 1D ; < - stack: [ -1 ] (true, 3 < 5)
In Fifth source code:
3 5 < \ result: -1 (true) 5 3 < \ result: 0 (false)
11.6. Important Notes
- Returns -1 for true (all bits set), 0 for false
- Standard Forth truth value convention
- Signed comparison (works for negative numbers)
- Reduces stack depth by 1
- NOT commutative (a < b ≠ b < a)
- Complementary to > (greater than) operation
11.7. Common Patterns
11.7.1. Conditional Execution
a b < if \ code for when a < b then
11.7.2. Range Check
value min < abort" Value too small"
11.7.3. Sorting Logic
a b < if swap then \ ensure larger value is on top
11.7.4. Equality Check
a b < if
\ a < b
else
a b > if
\ a > b
else
\ a = b
then
then
11.8. Edge Cases
- Equal values return 0 (false)
- Negative numbers compared correctly
- Works with full 32-bit signed range
- Relationship to other comparisons:
a b <is equivalent to=b a > not
11.9. Relationship to Other Comparisons
The less than operation is complementary to greater than:
a b < is equivalent tob a >a b <(less than or equal) can be implemented as=:over over < > ora b <> (not equal) can be implemented as:over over <> = drop