Trimble 4000SX Virtual Machine

Opcode 01: NOP

Opcode 02: Execute Inline Native

Executes 68k instructions starting at the next 16 bit aligned address. Execution is terminated with an RTS instruction.

Opcode 03: Not Sure What To Call This

Operand: Z (8 bits)

This instruction seems to be an alternate path to opcodes 20 (ADD.F) through to… hmm…

Opcode 04 through 07: PUSH.X

Operands: X (10 bits, overlaps opcode)

X - N1

Push 10 bit value onto stack

Opcode 08 through 0F: JR

Not investigated yet.

Opcode 10 through 17: JR

Operands: Y (11 bits, overlaps opcode)

Location in ROM: $00202c

Signed relative jump to Y

Opcode 18 through 1F: JRZ

Operands: Y (11 bits, overlaps opcode)

Location in ROM: $00202c

N1 - 

Signed relative jump to Y if N1 is zero

Opcode 20: ADD.F

Location in ROM: $00279a

IEEE 754 64 bit float add (adds 2 64 bit floats on stack) pushes result onto stack

+ N1 N2 - N3

Opcode 21: SUB.F

Location in ROM: $002792

IEEE 754 64 bit float subtract (subtracts second number on stack from first) pushes result onto stack

- N1 N2 - N3

Opcode 22: MUL.F

Location in ROM: $0028d6

IEEE 754 64 bit float multiply (multiplies second number by first) pushes result onto stack

* N1 N2 - N3

Opcode 23: DIV.F

Location in ROM: $002a1c

IEEE 754 64 bit float divide (divides first number on stack by second) pushes result onto stack

÷ N1 N2 - N3

Opcode 24: GT.F

Location in ROM: $0024e4

>  N1 N2 - FLAG

Pushes a true FLAG if N1 is greater than N2. Can be used to compare any 64 bit values on the stack.

Opcode 25: GTE.F

Location in ROM: $0024f0

>=  N1 N2 - FLAG

Pushes a true FLAG if N1 is greater than or equal to N2. Can be used to compare any 64 bit values on the stack.

Opcode 26: EQ.F

Location in ROM: $0024d8

=  N1 N2 - FLAG

Pushes a true FLAG if N1 is equal to N2. Can be used to compare any 64 bit values on the stack.

Opcode 27: ADD.L

Location in ROM: $002566

+ N1 N2 - N3

Add N1 and N2, giving sum N3.

Opcode 28: SUB.L

Location in ROM: $00256c

- N1 N2 - N3

Subtract N2 from N1, giving difference N3.

Opcode 29: MUL.L

Location in ROM: $002572

* N1 N2 - N3

Perform unsigned integer multiplication on N1 and N2, giving result N3

Opcode 2A: EQ.L

Location in ROM: $00249e

=  N1 N2 - FLAG

Pushes a true FLAG if N1 is equal to N2.

Opcode 2B: NEQ.L

Location in ROM: $0024a6

≠  N1 N2 - FLAG

Pushes a true FLAG if N1 is not equal to N2.

Opcode 2C: AND.L

Location in ROM: $002554

AND N1 N2 - N3

Perform a bitwise AND on N1 and N2, giving result N3.

Opcode 2D: OR.L

Location in ROM: $00255a

OR N1 N2 - N3

Perform a bitwise OR on N1 and N2, giving result N3.

Opcode 2E: NOT.L

Location in ROM: $002550

NOT N1 - N2

Perform a bitwise NOT on N1, giving result N2.

Opcode 2F: MOD.L

Location in ROM: $0025b4

might be divide, need to test

Opcode 30: JSR.68K

Operands: W

Location in ROM: $00212c

Subtracts operand from VPC, performs 68k JSR to that address. Typically used to call native functions defined in the jump table at the beginning of the function. Often these native functions are simply jumps to the entry point of a VM function.

Opcode 31: SUBSP.B

Operands: B

Location in ROM: $002444

Subtracts operand from stack pointer.

Opcode 32: ADDSP.B

Operands: B

Location in ROM: $002458

Adds operand to stack pointer.

Opcode 33: ???

Location in ROM: $002300

Uses top of stack to compute something then copies bytes from stack to A4 stuff.

Opcode 34: RETURN

Location in ROM: $00213c

Always Called at the end of a function. Fiddles with A0. Still figuring out what it’s doing. Sometimes this opcode is followed by 0xFF, which is just a padding byte to ensure the function ends on a word boundary.

Opcode 35: SWITCH

Location in ROM: $002b48

The switch opcode represents a higher level structural concept. followed by a number of repeating blocks, one for each case statement. Each block is preceded with a variable length header:

Block Header

  ┌─ Number of keys; 0 = default
  │ 
  │        ┌─ Record Length in bytes
  │        │ 
  │        │          ┌─ Keys for this record
  │        │          │
  │        │          │ 

0001 000000001001 [00000000]

Each block can multiple keys. The first 4 bits specifies how many keys the record specifies. The record length calculation includes the keys.

The VCPU searches for matching keys in a linear fashion. It searches the each key in the first record, followed by each key in the second record. It operates on a first match basis, so if multiple keys exist, only the earliest will ever match.

The end of each block must have a JR to the far end of the switch statement, otherwise the VCPU will attempt to execute the header of the next block as VCPU code, which most likely won’t end well.

Default Header

The final record in the statement is marked by a ‘default’ header, which is 8 bits of zeros. The Default record must be specified last, because it signals to the VCPU to stop searching for additional blocks.

   ┌─ All zeros
   │
   │ 

00000000

The default record contains no code; it simply signals to the CPU to begin executing VCPU code after the 0.

Example

Addr    Opcode          Instruction     Operands
-------------------------------------------------------
0A02DF  86 03           READ.L          $03
0A02E1  35              SWITCH      
0A02E2  10 09 00        CASE (0A02E1)   $00
0A02E5  03 30 03 53...  TRAP1           "SUN"
0A02EB  10 3E           JR              $03E => $0A032B
0A02ED  10 09 01        CASE (0A02E1)   $01
0A02F0  03 30 03 4D...  TRAP1           "MON"
0A02F6  10 33           JR              $033 => $0A032B
0A02F8  10 09 02        CASE (0A02E1)   $02
0A02FB  03 30 03 54...  TRAP1           "TUE"
0A0301  10 28           JR              $028 => $0A032B
0A0303  10 09 03        CASE (0A02E1)   $03
0A0306  03 30 03 57...  TRAP1           "WED"
0A030C  10 1D           JR              $01D => $0A032B
0A030E  10 09 04        CASE (0A02E1)   $04
0A0311  03 30 03 54...  TRAP1           "THU"
0A0317  10 12           JR              $012 => $0A032B
0A0319  10 09 05        CASE (0A02E1)   $05
0A031C  03 30 03 46...  TRAP1           "FRI"
0A0322  10 07           JR              $007 => $0A032B
0A0324  00              DEFAULT (0A02E1)    
0A0325  03 30 03 53...  TRAP1           "SAT"
0A032B  32 04           ADDSP.B         $04
0A032D  34              RETURN