--------------------------------------------------------------------------------
-- Zyelios VM (Zyelios CPU/GPU virtual machine)
--
-- Primary opcode set
--------------------------------------------------------------------------------




-- Initialize opcode count lookup table
ZVM.OperandCount = {}
for _,instruction in pairs(CPULib.InstructionTable) do
  ZVM.OperandCount[instruction.Opcode] = instruction.OperandCount
end



-- Initialize runlevel lookup table
ZVM.OpcodeRunLevel = {}
for _,instruction in pairs(CPULib.InstructionTable) do
  if instruction.Privileged then
    ZVM.OpcodeRunLevel[instruction.Opcode] = 0
  end
end

-- If an opcode is negative(like for extensions), the opcode will be absoluted
-- and use these lookup tables instead of the primary ones.
ZVM.ExtOperandCount = {}
ZVM.ExtOpcodeRunLevel = {}


--------------------------------------------------------------------------------
-- Hand-leg guide to writing ZCPU microcode
-- self:Dyn_Emit(code)
--   Emits microcode to output stream
--
-- self:Dyn_EmitOperand(OP,code,emitNow)
-- self:Dyn_EmitOperand(code)
--   Emits write to specific operand (if no operant specified - then operand #1)
--   Passing emitNow as true will emit operand at the current spot in microcode,
--   and set it later (must be done if operand is set inside block).
--
-- self:Dyn_EmitState()
--   Pushes state update - all global registers are set to their local values
--
-- self:Dyn_EmitBreak(emitIP)
--   Emits a return (does not push state). Emits new IP if required
--
-- self:Dyn_EmitForceRegisterGlobal(register)
--   Forces a specific register to be global (so VM.EAX has valid value)
--
-- self:Dyn_EmitForceRegisterLocal(register)
--   Forces a specific register to be local (so EAX has valid value)
--   Marks register as changed
--
-- self:Dyn_EmitRegisterValueChanged(register)
--   Marks that there now exists local state for the register
--
-- self:Dyn_EmitInterrupt(intNo,intParam)
--   Emits interrupt call
--
-- self:Dyn_EmitInterruptCheck()
--   Emits interrupt check



--------------------------------------------------------------------------------
-- Load all opcodes
ZVM.OpcodeTable = {}

ZVM.OpcodeTable[0] = function(self)  --END (STOP)
  self:Dyn_EmitInterrupt("2","0")
  self.PrecompileBreak = true -- Stop precompiler from following further
end
ZVM.OpcodeTable[1] = function(self)  --JNE
  self:Dyn_Emit("if VM.CMPR ~= 0 then")
    self:Dyn_Emit("VM:Jump($1)")
    self:Dyn_EmitState()
    self:Dyn_EmitBreak()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[2] = function(self)  --JMP
  self:Dyn_Emit("VM:Jump($1)")
  self:Dyn_EmitState()
  self:Dyn_EmitBreak()
  self.PrecompileBreak = true -- Stop precompiler from following further
end
ZVM.OpcodeTable[3] = function(self)  --JG
  self:Dyn_Emit("if VM.CMPR > 0 then")
    self:Dyn_Emit("VM:Jump($1)")
    self:Dyn_EmitState()
    self:Dyn_EmitBreak()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[4] = function(self)  --JGE
  self:Dyn_Emit("if VM.CMPR >= 0 then")
    self:Dyn_Emit("VM:Jump($1)")
    self:Dyn_EmitState()
    self:Dyn_EmitBreak()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[5] = function(self)  --JL
  self:Dyn_Emit("if VM.CMPR < 0 then")
    self:Dyn_Emit("VM:Jump($1)")
    self:Dyn_EmitState()
    self:Dyn_EmitBreak()
  self:Dyn_Emit("end")
--  self.PrecompileBreak = true
end
ZVM.OpcodeTable[6] = function(self)  --JLE
  self:Dyn_Emit("if VM.CMPR <= 0 then")
    self:Dyn_Emit("VM:Jump($1)")
    self:Dyn_EmitState()
    self:Dyn_EmitBreak()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[7] = function(self)  --JE
  self:Dyn_Emit("if VM.CMPR == 0 then")
    self:Dyn_Emit("VM:Jump($1)")
    self:Dyn_EmitState()
    self:Dyn_EmitBreak()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[8] = function(self)  --CPUID
  self:Dyn_Emit("EAX = VM:CPUID($1)")
  self:Dyn_EmitRegisterValueChanged("EAX")
end
ZVM.OpcodeTable[9] = function(self)  --PUSH
  self:Dyn_EmitForceRegisterGlobal("ESP")
  self:Dyn_Emit("VM:Push($1)")
  self:Dyn_EmitInterruptCheck()
end
--------------------------------------------------------------------------------
ZVM.OpcodeTable[10] = function(self)  --ADD
  self:Dyn_EmitOperand("$1 + $2")
end
ZVM.OpcodeTable[11] = function(self)  --SUB
  self:Dyn_EmitOperand("$1 - $2")
end
ZVM.OpcodeTable[12] = function(self)  --MUL
  self:Dyn_EmitOperand("$1 * $2")
end
ZVM.OpcodeTable[13] = function(self)  --DIV
  self:Dyn_Emit("$L OP = $2")
  self:Dyn_EmitOperand("$1 / OP")
  self:Dyn_Emit("if math.abs(OP) < 1e-12 then")
    self:Dyn_EmitInterrupt("3","0")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[14] = function(self)  --MOV
  self:Dyn_EmitOperand("$2")
end
ZVM.OpcodeTable[15] = function(self)  --CMP
  self:Dyn_Emit("VM.CMPR = $1 - $2")
end
ZVM.OpcodeTable[16] = function(self)  --RD
  self:Dyn_Emit("$L OP,ANS = $2,0")
  self:Dyn_EmitOperand("ANS")
  self:Dyn_BeginUnprivilegedCode(0)
    self:Dyn_Emit("if VM.PreqHandled == 1 then")
      self:Dyn_Emit("ANS = VM.PreqReturn or 0")
      self:Dyn_Emit("VM.PreqReturn = 0")
      self:Dyn_Emit("VM.PreqHandled = 0")
    self:Dyn_Emit("elseif VM.PreqHandled == 0 then")
      -- 16 is the opcode to send to LADD for int 13
      self:Dyn_EmitUnprivilegedRequestInterrupt(16)
    self:Dyn_Emit("else")
      self:Dyn_Emit("VM.PreqHandled = 0")
    self:Dyn_Emit("end")
  self:Dyn_Emit("else")
    -- In this case we're privileged, so just handle it as usual
    self:Dyn_Emit("if VM.Memory[OP] then")
      self:Dyn_Emit("ANS = VM.Memory[OP]")
    self:Dyn_Emit("end")
  self:Dyn_EndUnprivilegedCode()
end
ZVM.OpcodeTable[17] = function(self)  --WD
  self:Dyn_Emit("$L ADDR = math.floor($1)")
  self:Dyn_Emit("if (ADDR >= 0) and (ADDR <= 65535) then")
    self:Dyn_Emit("VM.Memory[ADDR] = $2")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[18] = function(self)  --MIN
  self:Dyn_EmitOperand("math.min($1,$2)")
end
ZVM.OpcodeTable[19] = function(self)  --MAX
  self:Dyn_EmitOperand("math.max($1,$2)")
end
--------------------------------------------------------------------------------
ZVM.OpcodeTable[20] = function(self)  --INC
  self:Dyn_EmitOperand("$1 + 1")
end
ZVM.OpcodeTable[21] = function(self)  --DEC
  self:Dyn_EmitOperand("$1 - 1")
end
ZVM.OpcodeTable[22] = function(self)  --NEG
  self:Dyn_EmitOperand("-$1")
end
ZVM.OpcodeTable[23] = function(self)  --RAND
  self:Dyn_EmitOperand("math.random()")
end
ZVM.OpcodeTable[24] = function(self)  --LOOP
  self:Dyn_EmitForceRegisterLocal("ECX")
  self:Dyn_Emit("ECX = ECX - 1")
  self:Dyn_Emit("if ECX ~= 0 then")
    self:Dyn_Emit("VM:Jump($1)")
    self:Dyn_EmitState()
    self:Dyn_EmitBreak()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[25] = function(self)  --LOOPA
  self:Dyn_EmitForceRegisterLocal("EAX")
  self:Dyn_Emit("EAX = EAX - 1")
  self:Dyn_Emit("if EAX ~= 0 then")
    self:Dyn_Emit("VM:Jump($1)")
    self:Dyn_EmitState()
    self:Dyn_EmitBreak()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[26] = function(self)  --LOOPB
  self:Dyn_EmitForceRegisterLocal("EBX")
  self:Dyn_Emit("EBX = EBX - 1")
  self:Dyn_Emit("if EBX ~= 0 then")
    self:Dyn_Emit("VM:Jump($1)")
    self:Dyn_EmitState()
    self:Dyn_EmitBreak()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[27] = function(self)  --LOOPD
  self:Dyn_EmitForceRegisterLocal("EDX")
  self:Dyn_Emit("EDX = EDX - 1")
  self:Dyn_Emit("if EDX ~= 0 then")
    self:Dyn_Emit("VM:Jump($1)")
    self:Dyn_EmitState()
    self:Dyn_EmitBreak()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[28] = function(self)  --SPG
  self:Dyn_Emit("$L IDX = math.floor($1 / 128)")
  self:Dyn_Emit("$L PAGE = VM:GetPageByIndex(IDX)")
  self:Dyn_EmitInterruptCheck()

  self:Dyn_Emit("if VM.CurrentPage.RunLevel <= PAGE.RunLevel then")
    self:Dyn_Emit("PAGE.Read = 1")
    self:Dyn_Emit("PAGE.Write = 0")
    self:Dyn_Emit("VM:SetPageByIndex(IDX)")
    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("else")
    self:Dyn_EmitInterrupt("11","IDX")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[29] = function(self)  --CPG
  self:Dyn_Emit("$L IDX = math.floor($1 / 128)")
  self:Dyn_Emit("$L PAGE = VM:GetPageByIndex(IDX)")
  self:Dyn_EmitInterruptCheck()

  self:Dyn_Emit("if VM.CurrentPage.RunLevel <= PAGE.RunLevel then")
    self:Dyn_Emit("PAGE.Read = 1")
    self:Dyn_Emit("PAGE.Write = 1")
    self:Dyn_Emit("VM:SetPageByIndex(IDX)")
    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("else")
    self:Dyn_EmitInterrupt("11","IDX")
  self:Dyn_Emit("end")
end
--------------------------------------------------------------------------------
ZVM.OpcodeTable[30] = function(self)  --POP
  self:Dyn_EmitForceRegisterGlobal("ESP")
  self:Dyn_EmitOperand(1,"VM:Pop()",true)
  self:Dyn_EmitInterruptCheck()
end
ZVM.OpcodeTable[31] = function(self)  --CALL
  self:Dyn_EmitForceRegisterGlobal("ESP")
  self:Dyn_Emit("if VM:Push(%d) then",self.PrecompileIP)
    self:Dyn_Emit("VM:Jump($1)")
    self:Dyn_EmitState()
    self:Dyn_EmitBreak()
  self:Dyn_Emit("end")
  self:Dyn_EmitInterruptCheck()

  self.PrecompileBreak = true
end
ZVM.OpcodeTable[32] = function(self)  --BNOT
  self:Dyn_Emit("if VM.IPREC > 32 then")
    self:Dyn_EmitOperand("VM:BinaryNot($1)")
  self:Dyn_Emit("else")
    self:Dyn_EmitOperand("VM:ClampBinaryToIPREC(bit.bnot($1))")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[33] = function(self)  --FINT
  self:Dyn_EmitOperand("math.floor($1)")
end
ZVM.OpcodeTable[34] = function(self)  --RND
  self:Dyn_EmitOperand("math.Round($1)")
end
ZVM.OpcodeTable[35] = function(self)  --FFRAC
  self:Dyn_Emit("$L OP = $1")
  self:Dyn_EmitOperand("OP - math.floor(OP)")
end
ZVM.OpcodeTable[36] = function(self)  --FINV
  self:Dyn_Emit("$L OP = $1")
  self:Dyn_EmitOperand("1 / OP")
  self:Dyn_Emit("if math.abs(OP) < 1e-12 then")
    self:Dyn_EmitInterrupt("3","1")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[37] = function(self)  --HALT
  self:Dyn_Emit("VM.HaltPort = math.floor($1)")
end
ZVM.OpcodeTable[38] = function(self)  --FSHL
  self:Dyn_EmitOperand("$1 * 2")
end
ZVM.OpcodeTable[39] = function(self)  --FSHR
  self:Dyn_EmitOperand("$1 / 2")
end
--------------------------------------------------------------------------------
ZVM.OpcodeTable[40] = function(self)  --RET
  self:Dyn_EmitForceRegisterGlobal("ESP")
  self:Dyn_Emit("$L IP = VM:Pop()")
  self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("VM:Jump(IP)")
  self:Dyn_EmitState()
  self:Dyn_EmitBreak()

  self.PrecompileBreak = true
end
ZVM.OpcodeTable[41] = function(self)  --IRET
  self:Dyn_EmitForceRegisterGlobal("ESP")
  self:Dyn_Emit("if VM.EF == 1 then")
    self:Dyn_Emit("$L CS = VM:Pop()")
    self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("$L IP = VM:Pop()")
    self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("VM:Jump(IP,CS)")
    self:Dyn_EmitState()
    self:Dyn_EmitBreak()
  self:Dyn_Emit("else")
    self:Dyn_Emit("$L IP = VM:Pop()")
    self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("VM:Jump(IP,CS)")
    self:Dyn_EmitState()
    self:Dyn_EmitBreak()
  self:Dyn_Emit("end")

  self.PrecompileBreak = true
end
ZVM.OpcodeTable[42] = function(self)  --STI
  self:Dyn_Emit("VM.NIF = 1")
end
ZVM.OpcodeTable[43] = function(self)  --CLI
  self:Dyn_Emit("VM.IF = 0")
end
ZVM.OpcodeTable[44] = function(self)  --STP
  self:Dyn_Emit("VM.PF = 1")
end
ZVM.OpcodeTable[45] = function(self)  --CLP
  self:Dyn_Emit("VM.PF = 0")
end
ZVM.OpcodeTable[46] = function(self)  --STD
    -- TODO: Remove microcode debugging, it's not possible to access normally.
  if self.MicrocodeDebug then
    self:Dyn_Emit("VM.Debug = true")
  end
end
ZVM.OpcodeTable[47] = function(self)  --RETF
  self:Dyn_EmitForceRegisterGlobal("ESP")
  self:Dyn_Emit("$L IP = VM:Pop()")
  self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("$L CS = VM:Pop()")
  self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("VM:Jump(IP,CS)")
  self:Dyn_EmitState()
  self:Dyn_EmitBreak()

  self.PrecompileBreak = true
end
ZVM.OpcodeTable[48] = function(self)  --STEF
  self:Dyn_Emit("VM.EF = 1")
end
ZVM.OpcodeTable[49] = function(self)  --CLEF
  self:Dyn_Emit("VM.EF = 0")
end
--------------------------------------------------------------------------------
ZVM.OpcodeTable[50] = function(self)  --AND
  self:Dyn_Emit("$L OP = 0")
  self:Dyn_EmitOperand("OP")
  self:Dyn_Emit("if ($1 > 0) and ($2 > 0) then")
    self:Dyn_Emit("OP = 1")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[51] = function(self)  --OR
  self:Dyn_Emit("$L OP = 0")
  self:Dyn_EmitOperand("OP")
  self:Dyn_Emit("if ($1 > 0) or ($2 > 0) then")
    self:Dyn_Emit("OP = 1")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[52] = function(self)  --XOR
  self:Dyn_Emit("$L OP1,OP2 = $1,$2")
  self:Dyn_Emit("$L OP = 0")
  self:Dyn_EmitOperand("OP")
  self:Dyn_Emit("if ((OP1 >  0) and (OP2 <= 0)) or")
  self:Dyn_Emit("   ((OP1 <= 0) and (OP2 >  0)) then")
    self:Dyn_Emit("OP = 1")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[53] = function(self)  --FSIN
  self:Dyn_EmitOperand("math.sin($2)")
end
ZVM.OpcodeTable[54] = function(self)  --FCOS
  self:Dyn_EmitOperand("math.cos($2)")
end
ZVM.OpcodeTable[55] = function(self)  --FTAN
  self:Dyn_EmitOperand("math.tan($2)")
end
ZVM.OpcodeTable[56] = function(self)  --FASIN
  self:Dyn_EmitOperand("math.asin($2)")
end
ZVM.OpcodeTable[57] = function(self)  --FACOS
  self:Dyn_EmitOperand("math.acos($2)")
end
ZVM.OpcodeTable[58] = function(self)  --FATAN
  self:Dyn_EmitOperand("math.atan($2)")
end
ZVM.OpcodeTable[59] = function(self)  --MOD
  self:Dyn_Emit("$L OP = $2")
  self:Dyn_EmitOperand("math.fmod($1,OP)")
  self:Dyn_Emit("if math.abs(OP) < 1e-12 then")
    self:Dyn_EmitInterrupt("3","0")
  self:Dyn_Emit("end")
end
--------------------------------------------------------------------------------
ZVM.OpcodeTable[60] = function(self)  --BIT
  self:Dyn_Emit("if VM.IPREC > 32 then")
    self:Dyn_Emit("$L BITS = VM:IntegerToBinary($1)")
    self:Dyn_Emit("VM.CMPR = BITS[math.floor($2)] or 0")
    self:Dyn_Emit("VM.TMR = VM.TMR + 30")
  self:Dyn_Emit("else")
    self:Dyn_Emit("if bit.band($1,VM:GetBit($2)) ~= 0 then")
      self:Dyn_Emit("VM.CMPR = 1")
    self:Dyn_Emit("else")
      self:Dyn_Emit("VM.CMPR = 0")
    self:Dyn_Emit("end")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[61] = function(self)  --SBIT
  self:Dyn_Emit("if VM.IPREC > 32 then")
    self:Dyn_Emit("$L BITS = VM:IntegerToBinary($1)")
    self:Dyn_Emit("BITS[math.floor($2)] = 1")
    self:Dyn_EmitOperand("VM:BinaryToInteger(BITS)")
    self:Dyn_Emit("VM.TMR = VM.TMR + 20")
  self:Dyn_Emit("else")
    self:Dyn_EmitOperand("VM:ClampBinaryToIPREC(bit.bor($1,VM:GetBit($2)))")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[62] = function(self)  --CBIT
  self:Dyn_Emit("if VM.IPREC > 32 then")
    self:Dyn_Emit("$L BITS = VM:IntegerToBinary($1)")
    self:Dyn_Emit("BITS[math.floor($2)] = 0")
    self:Dyn_EmitOperand("VM:BinaryToInteger(BITS)")
    self:Dyn_Emit("VM.TMR = VM.TMR + 20")
  self:Dyn_Emit("else")
    self:Dyn_EmitOperand("VM:ClampBinaryToIPREC(bit.band($1,VM:GetBit($2)))")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[63] = function(self)  --TBIT
  self:Dyn_Emit("if VM.IPREC > 32 then")
    self:Dyn_Emit("$L BITS = VM:IntegerToBinary($1)")
    self:Dyn_Emit("BITS[math.floor($2)] = 1 - (BITS[math.floor($2)] or 0)")
    self:Dyn_EmitOperand("VM:BinaryToInteger(BITS)")
    self:Dyn_Emit("VM.TMR = VM.TMR + 30")
  self:Dyn_Emit("else")
    self:Dyn_EmitOperand("VM:ClampBinaryToIPREC(bit.bxor($1,VM:GetBit($2)))")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[64] = function(self)   --BAND
  self:Dyn_Emit("if VM.IPREC > 32 then")
    self:Dyn_EmitOperand("VM:BinaryAnd($1,$2)")
    self:Dyn_Emit("VM.TMR = VM.TMR + 30")
  self:Dyn_Emit("else")
    self:Dyn_EmitOperand("VM:ClampBinaryToIPREC(bit.band($1,$2))")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[65] = function(self)  --BOR
  self:Dyn_Emit("if VM.IPREC > 32 then")
    self:Dyn_EmitOperand("VM:BinaryOr($1,$2)")
    self:Dyn_Emit("VM.TMR = VM.TMR + 30")
  self:Dyn_Emit("else")
    self:Dyn_EmitOperand("VM:ClampBinaryToIPREC(bit.bor($1,$2))")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[66] = function(self)   --BXOR
  self:Dyn_Emit("if VM.IPREC > 32 then")
    self:Dyn_EmitOperand("VM:BinaryXor($1,$2)")
    self:Dyn_Emit("VM.TMR = VM.TMR + 30")
  self:Dyn_Emit("else")
    self:Dyn_EmitOperand("VM:ClampBinaryToIPREC(bit.bxor($1,$2))")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[67] = function(self)  --BSHL
  self:Dyn_Emit("if VM.IPREC > 32 then")
    self:Dyn_EmitOperand("VM:BinarySHL($1,$2)")
    self:Dyn_Emit("VM.TMR = VM.TMR + 30")
  self:Dyn_Emit("else")
    self:Dyn_EmitOperand("VM:ClampBinaryToIPREC(bit.lshift($1,$2))")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[68] = function(self)  --BSHR
  self:Dyn_Emit("if VM.IPREC > 32 then")
    self:Dyn_EmitOperand("VM:BinarySHR($1,$2)")
    self:Dyn_Emit("VM.TMR = VM.TMR + 30")
  self:Dyn_Emit("else")
    self:Dyn_EmitOperand("VM:ClampBinaryToIPREC(bit.rshift($1,$2))")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[69] = function(self)  --JMPF
  self:Dyn_Emit("VM:Jump($1,$2)")
  self:Dyn_EmitState()
  self:Dyn_EmitBreak()
  self.PrecompileBreak = true
end
--------------------------------------------------------------------------------
ZVM.OpcodeTable[70] = function(self)  --EXTINT
  self:Dyn_EmitState()
  self:Emit("VM.IP = %d",(self.PrecompileIP or 0))
  self:Emit("VM.XEIP = %d",(self.PrecompileTrueXEIP or 0))
  self:Dyn_Emit("VM:ExternalInterrupt(math.floor($1))")
  self:Dyn_EmitBreak()
  self.PrecompileBreak = true
end
ZVM.OpcodeTable[71] = function(self)  --CNE
  self:Dyn_EmitForceRegisterGlobal("ESP")
  self:Dyn_Emit("if VM.CMPR ~= 0 then")
    self:Dyn_Emit("if VM:Push(%d) then",self.PrecompileIP)
      self:Dyn_Emit("VM:Jump($1)")
      self:Dyn_EmitState()
      self:Dyn_EmitBreak()
    self:Dyn_Emit("end")
    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[72] = function(self)  --CJMP
  self:Dyn_EmitForceRegisterGlobal("ESP")
  self:Dyn_Emit("if VM:Push(%d) then",self.PrecompileIP)
    self:Dyn_Emit("VM:Jump($1)")
    self:Dyn_EmitState()
    self:Dyn_EmitBreak()
  self:Dyn_Emit("end")
  self:Dyn_EmitInterruptCheck()
end
ZVM.OpcodeTable[73] = function(self)  --CG
  self:Dyn_EmitForceRegisterGlobal("ESP")
  self:Dyn_Emit("if VM.CMPR > 0 then")
    self:Dyn_Emit("if VM:Push(%d) then",self.PrecompileIP)
      self:Dyn_Emit("VM:Jump($1)")
      self:Dyn_EmitState()
      self:Dyn_EmitBreak()
    self:Dyn_Emit("end")
    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[74] = function(self)  --CGE
  self:Dyn_EmitForceRegisterGlobal("ESP")
  self:Dyn_Emit("if VM.CMPR >= 0 then")
    self:Dyn_Emit("if VM:Push(%d) then",self.PrecompileIP)
      self:Dyn_Emit("VM:Jump($1)")
      self:Dyn_EmitState()
      self:Dyn_EmitBreak()
    self:Dyn_Emit("end")
    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[75] = function(self)  --CL
  self:Dyn_EmitForceRegisterGlobal("ESP")
  self:Dyn_Emit("if VM.CMPR < 0 then")
    self:Dyn_Emit("if VM:Push(%d) then",self.PrecompileIP)
      self:Dyn_Emit("VM:Jump($1)")
      self:Dyn_EmitState()
      self:Dyn_EmitBreak()
    self:Dyn_Emit("end")
    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[76] = function(self)  --CLE
  self:Dyn_EmitForceRegisterGlobal("ESP")
  self:Dyn_Emit("if VM.CMPR <= 0 then")
    self:Dyn_Emit("if VM:Push(%d) then",self.PrecompileIP)
      self:Dyn_Emit("VM:Jump($1)")
      self:Dyn_EmitState()
      self:Dyn_EmitBreak()
    self:Dyn_Emit("end")
    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[77] = function(self)  --CE
  self:Dyn_EmitForceRegisterGlobal("ESP")
  self:Dyn_Emit("if VM.CMPR == 0 then")
    self:Dyn_Emit("if VM:Push(%d) then",self.PrecompileIP)
      self:Dyn_Emit("VM:Jump($1)")
      self:Dyn_EmitState()
      self:Dyn_EmitBreak()
    self:Dyn_Emit("end")
    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[78] = function(self)  --MCOPY
  self:Dyn_EmitForceRegisterLocal("ESI")
  self:Dyn_EmitForceRegisterLocal("EDI")
  self:Dyn_Emit("$L VAL")
  self:Dyn_Emit("for i = 1,math.Clamp($1,0,8192) do")
    self:Dyn_Emit("VAL = VM:ReadCell(ESI)")
    self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("VM:WriteCell(EDI,VAL)")
    self:Dyn_EmitInterruptCheck()
    -- The code in the QuotaOnlyCode block only executes if we've hit quota.
    self:Dyn_BeginQuotaOnlyCode()
        self:Dyn_Emit("VM.MCOPYWrapUpCount = math.Clamp($1,0,8192)-i")
    self:Dyn_EndQuotaOnlyCode()
    self:Dyn_Emit("EDI = EDI + 1")
    self:Dyn_Emit("ESI = ESI + 1")
    -- The code in the QuotaInterrupt block sets up a function to run and
    -- handle the memory copy across multiple frames, to prevent FPS drops.
    self:Dyn_StartQuotaInterrupt()
        self:Dyn_Emit("$L VAL")
        self:Dyn_Emit("for i = 1, math.min(8192,VM.MCOPYWrapUpCount) do")
          self:Dyn_Emit("VAL = VM:ReadCell(ESI)")
          self:Dyn_EmitInterruptCheck()
          self:Dyn_Emit("VM:WriteCell(EDI,VAL)")
          self:Dyn_EmitInterruptCheck()
          self:Dyn_Emit("EDI = EDI + 1")
          self:Dyn_Emit("ESI = ESI + 1")
          self:Dyn_BeginQuotaOnlyCode()
            self:Dyn_EmitState()
            self:Dyn_Emit("VM.MCOPYWrapUpCount = VM.MCOPYWrapUpCount - i")
            self:Dyn_Emit("return")
          self:Dyn_EndQuotaOnlyCode()
        self:Dyn_Emit("end")
        self:Dyn_Emit("if VM.MCOPYWrapUpCount <= 8192 then")
          self:Dyn_Emit("VM.MCOPYWrapUpCount = nil")
          self:Dyn_Emit("VM.QuotaOverrunFunc = nil")
          self:Dyn_Emit("else")
          self:Dyn_Emit("VM.MCOPYWrapUpCount = VM.MCOPYWrapUpCount - 8192")
        self:Dyn_Emit("end")
    self:Dyn_EndQuotaInterrupt()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[79] = function(self)  --MXCHG
  self:Dyn_EmitForceRegisterLocal("ESI")
  self:Dyn_EmitForceRegisterLocal("EDI")
  self:Dyn_Emit("for i = 1,math.Clamp($1,0,8192) do")
    self:Dyn_Emit("$L VAL1,VAL2")
    self:Dyn_Emit("VAL1 = VM:ReadCell(ESI)")
    self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("VAL2 = VM:ReadCell(EDI)")
    self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("VM:WriteCell(EDI,VAL1)")
    self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("VM:WriteCell(ESI,VAL2)")
    self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("EDI = EDI + 1")
    self:Dyn_Emit("ESI = ESI + 1")
  self:Dyn_Emit("end")
end
--------------------------------------------------------------------------------
ZVM.OpcodeTable[80] = function(self)  --FPWR
  self:Dyn_EmitOperand("$1^$2")
end
ZVM.OpcodeTable[81] = function(self)  --XCHG
  self:Dyn_Emit("$L L0,L1 = $1,$2")
  self:Dyn_EmitOperand(1,"L1")
  self:Dyn_EmitOperand(2,"L0")
end
ZVM.OpcodeTable[82] = function(self)  --FLOG
  self:Dyn_EmitOperand("math.log($2)")
end
ZVM.OpcodeTable[83] = function(self)  --FLOG10
  self:Dyn_EmitOperand("math.log10($2)")
end
ZVM.OpcodeTable[84] = function(self)  --IN
  self:Dyn_EmitOperand("VM:ReadPort($2)")
  self:Dyn_EmitInterruptCheck()
end
ZVM.OpcodeTable[85] = function(self)  --OUT
  self:Dyn_Emit("VM:WritePort($1,$2)")
  self:Dyn_EmitInterruptCheck()
end
ZVM.OpcodeTable[86] = function(self)  --FABS
  self:Dyn_EmitOperand("math.abs($2)")
end
ZVM.OpcodeTable[87] = function(self)  --FSGN
  self:Dyn_Emit("$L OP = $2")
  self:Dyn_Emit("if OP > 0 then")
    self:Dyn_EmitOperand(1,"1",true)
  self:Dyn_Emit("elseif OP < 0 then")
    self:Dyn_EmitOperand(1,"-1",true)
  self:Dyn_Emit("else")
    self:Dyn_EmitOperand(1,"0",true)
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[88] = function(self)  --FEXP
  self:Dyn_EmitOperand("math.exp($2)")
end
ZVM.OpcodeTable[89] = function(self)  --CALLF
  self:Dyn_EmitForceRegisterGlobal("CS")
  self:Dyn_EmitForceRegisterGlobal("ESP")
  self:Dyn_Emit("if VM:Push(VM.CS) and VM:Push(%d) then",self.PrecompileIP)
    self:Dyn_Emit("VM:Jump($1,$2)")
    self:Dyn_EmitState()
    self:Dyn_EmitBreak()
  self:Dyn_Emit("end")
  self:Dyn_EmitInterruptCheck()

  self.PrecompileBreak = true
end
--------------------------------------------------------------------------------
ZVM.OpcodeTable[90] = function(self) --FPI
  self:Dyn_EmitOperand("3.141592653589793")
end
ZVM.OpcodeTable[91] = function(self) --FE
  self:Dyn_EmitOperand("2.718281828459045")
end
ZVM.OpcodeTable[92] = function(self)  --INT
  self:Dyn_EmitInterrupt("$1","0")
end
ZVM.OpcodeTable[93] = function(self)  --TPG
  self:Dyn_Emit("$L TADD = math.floor($1*128)")
  self:Dyn_Emit("$L OLDIF = VM.IF")
  self:Dyn_Emit("$L OP = $1")
  self:Dyn_Emit("VM.IF = 0")
  self:Dyn_Emit("VM.CMPR = 0")
  self:Dyn_Emit("while TADD < OP*128+128 do")
    self:Dyn_Emit("$L VAL = VM:ReadCell(TADD)")
    self:Dyn_Emit("if VM.INTR == 1 then")
      self:Dyn_Emit("VM.CMPR = TADD")
      self:Dyn_Emit("TADD = OP*128+128")
    self:Dyn_Emit("end")
    self:Dyn_Emit("TADD = TADD+1")
  self:Dyn_Emit("end")
  self:Dyn_Emit("VM.INTR = 0")
  self:Dyn_Emit("VM.IF = OLDIF")
end
ZVM.OpcodeTable[94] = function(self)  --FCEIL
  self:Dyn_EmitOperand("math.ceil($1)")
end
ZVM.OpcodeTable[95] = function(self) --ERPG
  self:Dyn_Emit("$L OP = $1")
  self:Dyn_Emit("if (OP >= 0) and (OP < VM.ROMSize/128) then")
    self:Dyn_Emit("$L TADD = OP*128")
    self:Dyn_Emit("while TADD < OP*128+128 do")
      self:Dyn_Emit("VM.ROM[TADD] = nil")
      self:Dyn_Emit("TADD = TADD+1")
    self:Dyn_Emit("end")
  self:Dyn_Emit("else")
    self:Dyn_EmitInterrupt("12","0")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[96] = function(self)  --WRPG
  self:Dyn_Emit("$L OP = $1")
  self:Dyn_Emit("if (OP >= 0) and (OP < VM.ROMSize/128) then")
    self:Dyn_Emit("$L TADD = OP*128")
    self:Dyn_Emit("while TADD < OP*128+128 do")
      self:Dyn_Emit("VM.ROM[TADD] = VM.Memory[TADD]")
      self:Dyn_Emit("TADD = TADD+1")
    self:Dyn_Emit("end")
  self:Dyn_Emit("else")
    self:Dyn_EmitInterrupt("12","0")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[97] = function(self) --RDPG
  self:Dyn_Emit("$L OP = $1")
  self:Dyn_Emit("if (OP >= 0) and (OP < VM.ROMSize/128) then")
    self:Dyn_Emit("$L TADD = OP*128")
    self:Dyn_Emit("while TADD < OP*128+128 do")
      self:Dyn_Emit("VM.Memory[TADD] = VM.ROM[TADD]")
      self:Dyn_Emit("TADD = TADD+1")
    self:Dyn_Emit("end")
  self:Dyn_Emit("else")
    self:Dyn_EmitInterrupt("12","0")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[98] = function(self)  --TIMER
  self:Dyn_EmitOperand(string.format("(VM.TIMER+%d*VM.TimerDT)",(self.PrecompileInstruction or 0)))
end
ZVM.OpcodeTable[99] = function(self)  --LIDTR
  self:Dyn_Emit("VM.IDTR = $1")
end
--------------------------------------------------------------------------------
ZVM.OpcodeTable[100] = function(self)  --STATESTORE
  self:Dyn_EmitState()
  self:Dyn_Emit("VM:WriteCell($1 + 00,%d)",self.PrecompileIP)

  self:Dyn_Emit("VM:WriteCell($1 + 01,VM.EAX)")
  self:Dyn_Emit("VM:WriteCell($1 + 02,VM.EBX)")
  self:Dyn_Emit("VM:WriteCell($1 + 03,VM.ECX)")
  self:Dyn_Emit("VM:WriteCell($1 + 04,VM.EDX)")
  self:Dyn_Emit("VM:WriteCell($1 + 05,VM.ESI)")
  self:Dyn_Emit("VM:WriteCell($1 + 06,VM.EDI)")
  self:Dyn_Emit("VM:WriteCell($1 + 07,VM.ESP)")
  self:Dyn_Emit("VM:WriteCell($1 + 08,VM.EBP)")

  self:Dyn_Emit("VM:WriteCell($1 + 09,VM.CS)")
  self:Dyn_Emit("VM:WriteCell($1 + 10,VM.SS)")
  self:Dyn_Emit("VM:WriteCell($1 + 11,VM.DS)")
  self:Dyn_Emit("VM:WriteCell($1 + 12,VM.ES)")
  self:Dyn_Emit("VM:WriteCell($1 + 13,VM.GS)")
  self:Dyn_Emit("VM:WriteCell($1 + 14,VM.FS)")

  self:Dyn_Emit("VM:WriteCell($1 + 15,VM.CMPR)")
  self:Dyn_EmitInterruptCheck()
end
ZVM.OpcodeTable[101] = function(self)  --JNER
  self:Dyn_Emit("if VM.CMPR ~= 0 then")
    self:Dyn_Emit("VM:Jump(%d + $1)",self.PrecompileIP)
    self:Dyn_EmitState()
    self:Dyn_EmitBreak()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[102] = function(self)  --JMPR
  self:Dyn_Emit("VM:Jump(%d + $1)",self.PrecompileIP)
  self:Dyn_EmitState()
  self:Dyn_EmitBreak()

  self.PrecompileBreak = true
end
ZVM.OpcodeTable[103] = function(self)  --JGR
  self:Dyn_Emit("if VM.CMPR > 0 then")
    self:Dyn_Emit("VM:Jump(%d + $1)",self.PrecompileIP)
    self:Dyn_EmitState()
    self:Dyn_EmitBreak()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[104] = function(self)  --JGER
  self:Dyn_Emit("if VM.CMPR >= 0 then")
    self:Dyn_Emit("VM:Jump(%d + $1)",self.PrecompileIP)
    self:Dyn_EmitState()
    self:Dyn_EmitBreak()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[105] = function(self)  --JLR
  self:Dyn_Emit("if VM.CMPR < 0 then")
    self:Dyn_Emit("VM:Jump(%d + $1)",self.PrecompileIP)
    self:Dyn_EmitState()
    self:Dyn_EmitBreak()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[106] = function(self)  --JLER
  self:Dyn_Emit("if VM.CMPR <= 0 then")
    self:Dyn_Emit("VM:Jump(%d + $1)",self.PrecompileIP)
    self:Dyn_EmitState()
    self:Dyn_EmitBreak()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[107] = function(self)  --JER
  self:Dyn_Emit("if VM.CMPR == 0 then")
    self:Dyn_Emit("VM:Jump(%d + $1)",self.PrecompileIP)
    self:Dyn_EmitState()
    self:Dyn_EmitBreak()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[108] = function(self)  --LNEG
  self:Dyn_EmitOperand("1-math.Clamp($1,0,1)")
end
ZVM.OpcodeTable[109] = function(self)   --STATERESTORE
  self:Dyn_Emit("          VM:ReadCell($1 + 00)")

  self:Dyn_Emit("EAX =     VM:ReadCell($1 + 01) or 0")
  self:Dyn_Emit("EBX =     VM:ReadCell($1 + 02) or 0")
  self:Dyn_Emit("ECX =     VM:ReadCell($1 + 03) or 0")
  self:Dyn_Emit("EDX =     VM:ReadCell($1 + 04) or 0")
  self:Dyn_Emit("ESI =     VM:ReadCell($1 + 05) or 0")
  self:Dyn_Emit("EDI =     VM:ReadCell($1 + 06) or 0")
  self:Dyn_Emit("ESP =     VM:ReadCell($1 + 07) or 0")
  self:Dyn_Emit("EBP =     VM:ReadCell($1 + 08) or 0")

  self:Dyn_Emit("CS  =     VM:ReadCell($1 + 09) or 0")
  self:Dyn_Emit("SS  =     VM:ReadCell($1 + 10) or 0")
  self:Dyn_Emit("DS  =     VM:ReadCell($1 + 11) or 0")
  self:Dyn_Emit("ES  =     VM:ReadCell($1 + 12) or 0")
  self:Dyn_Emit("GS  =     VM:ReadCell($1 + 13) or 0")
  self:Dyn_Emit("FS  =     VM:ReadCell($1 + 14) or 0")

  self:Dyn_Emit("VM.CMPR = VM:ReadCell($1 + 15) or 0")

  self:Dyn_EmitInterruptCheck()

  self:Dyn_EmitRegisterValueChanged("EAX")
  self:Dyn_EmitRegisterValueChanged("EBX")
  self:Dyn_EmitRegisterValueChanged("ECX")
  self:Dyn_EmitRegisterValueChanged("EDX")
  self:Dyn_EmitRegisterValueChanged("ESI")
  self:Dyn_EmitRegisterValueChanged("EDI")
  self:Dyn_EmitRegisterValueChanged("ESP")
  self:Dyn_EmitRegisterValueChanged("EBP")

  self:Dyn_EmitRegisterValueChanged("CS")
  self:Dyn_EmitRegisterValueChanged("SS")
  self:Dyn_EmitRegisterValueChanged("DS")
  self:Dyn_EmitRegisterValueChanged("ES")
  self:Dyn_EmitRegisterValueChanged("GS")
  self:Dyn_EmitRegisterValueChanged("FS")
end
--------------------------------------------------------------------------------
ZVM.OpcodeTable[110] = function(self)  --EXTRET
  self:Dyn_EmitForceRegisterGlobal("ESP")
  self:Dyn_Emit("$L V = 0")
  self:Dyn_EmitState()

  self:Dyn_Emit("V = VM:Pop()") -- IRET CS
  self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("V = VM:Pop()") -- IRET EIP
  self:Dyn_EmitInterruptCheck()

  self:Dyn_Emit("V = VM:Pop()")  self:Dyn_EmitInterruptCheck()  self:Dyn_Emit("$L IP = V")
  self:Dyn_Emit("V = VM:Pop()")  self:Dyn_EmitInterruptCheck()  self:Dyn_Emit("VM.CMPR = V")

  self:Dyn_Emit("V = VM:Pop()")  self:Dyn_EmitInterruptCheck()  self:Dyn_Emit("VM.EAX = V")
  self:Dyn_Emit("V = VM:Pop()")  self:Dyn_EmitInterruptCheck()  self:Dyn_Emit("VM.EBX = V")
  self:Dyn_Emit("V = VM:Pop()")  self:Dyn_EmitInterruptCheck()  self:Dyn_Emit("VM.ECX = V")
  self:Dyn_Emit("V = VM:Pop()")  self:Dyn_EmitInterruptCheck()  self:Dyn_Emit("VM.EDX = V")
  self:Dyn_Emit("V = VM:Pop()")  self:Dyn_EmitInterruptCheck()  self:Dyn_Emit("VM.EBP = V")
  self:Dyn_Emit("V = VM:Pop()")  self:Dyn_EmitInterruptCheck()  -- Do not set ESP right now
  self:Dyn_Emit("V = VM:Pop()")  self:Dyn_EmitInterruptCheck()  self:Dyn_Emit("VM.ESI = V")
  self:Dyn_Emit("V = VM:Pop()")  self:Dyn_EmitInterruptCheck()  self:Dyn_Emit("VM.EDI = V")

  self:Dyn_Emit("V = VM:Pop()")  self:Dyn_EmitInterruptCheck()  self:Dyn_Emit("$L CS = V")
  self:Dyn_Emit("V = VM:Pop()")  self:Dyn_EmitInterruptCheck()  -- Do not set SS right now
  self:Dyn_Emit("V = VM:Pop()")  self:Dyn_EmitInterruptCheck()  self:Dyn_Emit("VM.DS = V")
  self:Dyn_Emit("V = VM:Pop()")  self:Dyn_EmitInterruptCheck()  self:Dyn_Emit("VM.FS = V")
  self:Dyn_Emit("V = VM:Pop()")  self:Dyn_EmitInterruptCheck()  self:Dyn_Emit("VM.GS = V")
  self:Dyn_Emit("V = VM:Pop()")  self:Dyn_EmitInterruptCheck()  self:Dyn_Emit("VM.ES = V")
  self:Dyn_Emit("V = VM:Pop()")  self:Dyn_EmitInterruptCheck()  self:Dyn_Emit("VM.KS = V")
  self:Dyn_Emit("V = VM:Pop()")  self:Dyn_EmitInterruptCheck()  self:Dyn_Emit("VM.LS = V")

  self:Dyn_Emit("VM:Jump(IP,CS)")
  self:Dyn_EmitBreak()

  self.PrecompileBreak = true
end
ZVM.OpcodeTable[111] = function(self)  --IDLE
  self:Dyn_Emit("VM.Idle = 1")
end
ZVM.OpcodeTable[112] = function(self)  --NOP
end
ZVM.OpcodeTable[113] = function(self)  --RLADD
  self:Dyn_Emit("EAX = VM.LADD")
  self:Dyn_EmitRegisterValueChanged("EAX")
end
ZVM.OpcodeTable[114] = function(self)  --PUSHA
  self:Dyn_EmitForceRegisterLocal("EAX")
  self:Dyn_EmitForceRegisterLocal("EBX")
  self:Dyn_EmitForceRegisterLocal("ECX")
  self:Dyn_EmitForceRegisterLocal("EDX")
  self:Dyn_EmitForceRegisterLocal("ESI")
  self:Dyn_EmitForceRegisterLocal("EDI")
  self:Dyn_EmitForceRegisterGlobal("ESP")
  self:Dyn_EmitForceRegisterLocal("EBP")

  self:Dyn_Emit("VM:Push(EDI)")    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("VM:Push(ESI)")    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("VM:Push(EBP)")    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("VM:Push(VM.ESP)") self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("VM:Push(EDX)")    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("VM:Push(ECX)")    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("VM:Push(EBX)")    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("VM:Push(EAX)")    self:Dyn_EmitInterruptCheck()
end
ZVM.OpcodeTable[115] = function(self)  --POPA
  self:Dyn_EmitForceRegisterGlobal("ESP")

  self:Dyn_Emit("EAX = VM:Pop()") self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("EBX = VM:Pop()") self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("ECX = VM:Pop()") self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("EDX = VM:Pop()") self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("$L SP = VM:Pop()") self:Dyn_EmitInterruptCheck() -- Do not write stack pointer
  self:Dyn_Emit("EBP = VM:Pop()") self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("ESI = VM:Pop()") self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("EDI = VM:Pop()") self:Dyn_EmitInterruptCheck()

  self:Dyn_EmitRegisterValueChanged("EAX")
  self:Dyn_EmitRegisterValueChanged("EBX")
  self:Dyn_EmitRegisterValueChanged("ECX")
  self:Dyn_EmitRegisterValueChanged("EDX")
  self:Dyn_EmitRegisterValueChanged("ESI")
  self:Dyn_EmitRegisterValueChanged("EDI")
  self:Dyn_EmitRegisterValueChanged("EBP")
end
ZVM.OpcodeTable[116] = function(self)  --STD2
  self:Dyn_Emit("VM.HWDEBUG = 1")
  self:Dyn_Emit("VM.DBGSTATE = 0")
end
ZVM.OpcodeTable[117] = function(self)  --LEAVE
  self:Dyn_EmitForceRegisterGlobal("ESP")
  self:Dyn_EmitForceRegisterGlobal("EBP")
  self:Dyn_Emit("VM.ESP = VM.EBP-1")

  self:Dyn_Emit("EBP = VM:Pop()")
  self:Dyn_EmitInterruptCheck()
  self:Dyn_EmitRegisterValueChanged("EBP")
end
ZVM.OpcodeTable[118] = function(self)  --STM
  self:Dyn_Emit("VM.MF = 1")
end
ZVM.OpcodeTable[119] = function(self)  --CLM
  self:Dyn_Emit("VM.MF = 0")
end
--------------------------------------------------------------------------------
ZVM.OpcodeTable[120] = function(self)  --CPUGET
  self:Dyn_Emit("$L REG = $2")
  self:Dyn_Emit("$L OP = 0")
  self:Dyn_EmitState()
  self:Dyn_EmitOperand("OP")
  self:Dyn_BeginUnprivilegedCode(0)
    self:Dyn_Emit("if VM.PreqHandled == 1 then")
      self:Dyn_Emit("OP = VM.PreqReturn or 0")
      self:Dyn_Emit("VM.PreqReturn = 0")
      self:Dyn_Emit("VM.PreqHandled = 0")
    self:Dyn_Emit("elseif VM.PreqHandled == 0 then")
      -- 120 is the opcode to send to LADD for int 13
      self:Dyn_EmitUnprivilegedRequestInterrupt(120)
    self:Dyn_Emit("else")
      self:Dyn_Emit("VM.PreqHandled = 0")
    self:Dyn_Emit("end")
  self:Dyn_Emit("else")
    -- In this case we're privileged, so just handle it as usual
    self:Dyn_Emit("if VM.InternalRegister[REG] then")
      self:Dyn_Emit("OP = VM[VM.InternalRegister[REG]]")
    self:Dyn_Emit("end")
  self:Dyn_EndUnprivilegedCode()
end
ZVM.OpcodeTable[121] = function(self)  --CPUSET
  self:Dyn_Emit("$L REG = $1")
  self:Dyn_Emit("if VM.InternalRegister[REG] and (not VM.ReadOnlyRegister[REG]) then")
    self:Dyn_Emit("$L OP = $2")
    self:Dyn_Emit("$L limit = VM.InternalLimits[REG]")
    self:Dyn_Emit("if VM.IntegerOnlyRegister[REG] then")
      self:Dyn_Emit("VM[VM.InternalRegister[REG]] = math.floor(limit and math.Clamp(OP, limit[1], limit[2]) or OP)")
    self:Dyn_Emit("else")
      self:Dyn_Emit("VM[VM.InternalRegister[REG]] = limit and math.Clamp(OP, limit[1], limit[2]) or OP")
    self:Dyn_Emit("end")
    self:Dyn_Emit("if (REG == 0) or (REG == 16) then")
      self:Dyn_Emit("VM:Jump(%d,VM.CS)",self.PrecompileIP)
      self:Dyn_EmitState()
      self:Dyn_EmitBreak()
    self:Dyn_Emit("else")
      self:Dyn_Emit("if REG == 1 then EAX = OP end")
      self:Dyn_Emit("if REG == 2 then EBX = OP end")
      self:Dyn_Emit("if REG == 3 then ECX = OP end")
      self:Dyn_Emit("if REG == 4 then EDX = OP end")
      self:Dyn_Emit("if REG == 5 then ESI = OP end")
      self:Dyn_Emit("if REG == 6 then EDI = OP end")
      self:Dyn_Emit("if REG == 7 then ESP = OP end")
      self:Dyn_Emit("if REG == 8 then EBP = OP end")
    self:Dyn_Emit("end")
  self:Dyn_Emit("end")

  -- FIXME: registers must be properly synced
end
ZVM.OpcodeTable[122] = function(self)  --SPP
  self:Dyn_Emit("$L FirstAddr")
  self:Dyn_Emit("$L LastAddr")
  self:Dyn_Emit("if VM.BlockSize > 0 then")
    self:Dyn_Emit("FirstAddr = VM.BlockStart")
    self:Dyn_Emit("LastAddr = FirstAddr + math.Clamp(VM.BlockSize,0,8192)")
    self:Dyn_Emit("VM.BlockSize = 0")
  self:Dyn_Emit("else")
    self:Dyn_Emit("FirstAddr = $1 * 128")
    self:Dyn_Emit("LastAddr = $1 * 128 + 127")
  self:Dyn_Emit("end")

  self:Dyn_Emit("$L ADDR = FirstAddr")
  self:Dyn_Emit("$L FLAG = $2")
  self:Dyn_Emit("while ADDR < LastAddr do")
    self:Dyn_Emit("$L IDX = math.floor(ADDR / 128)")
    self:Dyn_Emit("$L PAGE = VM:GetPageByIndex(IDX)")
    self:Dyn_EmitInterruptCheck()

    self:Dyn_Emit("if VM.CurrentPage.RunLevel <= PAGE.RunLevel then")
      self:Dyn_Emit("    if FLAG == 0 then PAGE.Read     = 1")
      self:Dyn_Emit("elseif FLAG == 1 then PAGE.Write    = 1")
      self:Dyn_Emit("elseif FLAG == 2 then PAGE.Execute  = 1")
      self:Dyn_Emit("elseif FLAG == 3 then PAGE.RunLevel = 1 end")
      self:Dyn_Emit("VM:SetPageByIndex(IDX)")
      self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("else")
      self:Dyn_EmitInterrupt("11","IDX")
    self:Dyn_Emit("end")
    self:Dyn_Emit("ADDR = ADDR + 128")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[123] = function(self)  --CPP
  self:Dyn_Emit("$L FirstAddr")
  self:Dyn_Emit("$L LastAddr")
  self:Dyn_Emit("if VM.BlockSize > 0 then")
    self:Dyn_Emit("FirstAddr = VM.BlockStart")
    self:Dyn_Emit("LastAddr = FirstAddr + math.Clamp(VM.BlockSize,0,8192)")
    self:Dyn_Emit("VM.BlockSize = 0")
  self:Dyn_Emit("else")
    self:Dyn_Emit("FirstAddr = $1 * 128")
    self:Dyn_Emit("LastAddr = $1 * 128 + 127")
  self:Dyn_Emit("end")

  self:Dyn_Emit("$L ADDR = FirstAddr")
  self:Dyn_Emit("$L FLAG = $2")
  self:Dyn_Emit("while ADDR < LastAddr do")
    self:Dyn_Emit("$L IDX = math.floor(ADDR / 128)")
    self:Dyn_Emit("$L PAGE = VM:GetPageByIndex(IDX)")
    self:Dyn_EmitInterruptCheck()

    self:Dyn_Emit("if VM.CurrentPage.RunLevel <= PAGE.RunLevel then")
      self:Dyn_Emit("    if FLAG == 0 then PAGE.Read     = 0")
      self:Dyn_Emit("elseif FLAG == 1 then PAGE.Write    = 0")
      self:Dyn_Emit("elseif FLAG == 2 then PAGE.Execute  = 0")
      self:Dyn_Emit("elseif FLAG == 3 then PAGE.RunLevel = 0 end")
      self:Dyn_Emit("VM:SetPageByIndex(IDX)")
      self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("else")
      self:Dyn_EmitInterrupt("11","IDX")
    self:Dyn_Emit("end")
    self:Dyn_Emit("ADDR = ADDR + 128")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[124] = function(self)  --SRL
  self:Dyn_Emit("$L FirstAddr")
  self:Dyn_Emit("$L LastAddr")
  self:Dyn_Emit("if VM.BlockSize > 0 then")
    self:Dyn_Emit("FirstAddr = VM.BlockStart")
    self:Dyn_Emit("LastAddr = FirstAddr + math.Clamp(VM.BlockSize,0,8192)")
    self:Dyn_Emit("VM.BlockSize = 0")
  self:Dyn_Emit("else")
    self:Dyn_Emit("FirstAddr = $1 * 128")
    self:Dyn_Emit("LastAddr = $1 * 128 + 127")
  self:Dyn_Emit("end")

  self:Dyn_Emit("$L ADDR = FirstAddr")
  self:Dyn_Emit("while ADDR < LastAddr do")
    self:Dyn_Emit("$L IDX = math.floor(ADDR / 128)")
    self:Dyn_Emit("$L PAGE = VM:GetPageByIndex(IDX)")
    self:Dyn_EmitInterruptCheck()

    self:Dyn_Emit("if VM.CurrentPage.RunLevel <= PAGE.RunLevel then")
      self:Dyn_Emit("PAGE.RunLevel = $2")
      self:Dyn_Emit("VM:SetPageByIndex(IDX)")
      self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("else")
      self:Dyn_EmitInterrupt("11","IDX")
    self:Dyn_Emit("end")
    self:Dyn_Emit("ADDR = ADDR + 128")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[125] = function(self)  --GRL
  self:Dyn_Emit("$L IDX = math.floor($2 / 128)")
  self:Dyn_Emit("$L PAGE = VM:GetPageByIndex(IDX)")
  self:Dyn_EmitInterruptCheck()
  self:Dyn_EmitOperand("PAGE.RunLevel")
end
ZVM.OpcodeTable[126] = function(self)  --LEA
  local emitText = self.OperandEffectiveAddress[self.EmitOperandRM[2]] or "0"
  emitText = string.gsub(emitText,"$BYTE",self.EmitOperandByte[2] or "0")
  emitText = string.format(string.gsub(emitText,"$SEG","VM[%q]"), self.EmitOperandSegment[2] or "DS")
  self:Dyn_EmitOperand(emitText)
end
ZVM.OpcodeTable[127] = function(self)  --BLOCK
  self:Dyn_Emit("VM.BlockStart = $1")
  self:Dyn_Emit("VM.BlockSize = $2")
end
ZVM.OpcodeTable[128] = function(self)  --CMPAND
  self:Dyn_Emit("if VM.CMPR ~= 0 then")
    self:Dyn_Emit("VM.CMPR = $1 - $2")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[129] = function(self)  --CMPOR
  self:Dyn_Emit("if VM.CMPR == 0 then")
    self:Dyn_Emit("VM.CMPR = $1 - $2")
  self:Dyn_Emit("end")
end
--------------------------------------------------------------------------------
ZVM.OpcodeTable[130] = function(self)  --MSHIFT  FIXME: Inoperative
  self:Dyn_EmitForceRegisterLocal("ESI")
  self:Dyn_Emit("$L Count = math.Clamp($1,0,8192)")
  self:Dyn_Emit("if Count ~= 0 then")
    self:Dyn_Emit("$L Offset = $2")
    self:Dyn_Emit("$L Buffer = {}")

    self:Dyn_Emit("if Offset > 0 then")
      self:Dyn_Emit("for i = 0,math.Clamp(Count-1-Offset,0,8191) do") --Shifted part
        self:Dyn_Emit("Buffer[i] = VM:ReadCell(ESI+i+Offset)")
      self:Dyn_Emit("end")
      self:Dyn_EmitInterruptCheck()
      self:Dyn_Emit("for i = math.Clamp(Count-1-Offset+1,0,8191),math.Clamp(Count,0,8191) do") --Remaining part
        self:Dyn_Emit("Buffer[i] = VM:ReadCell(ESI+i-(Count-1-Offset+1))")
      self:Dyn_Emit("end")
      self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("else")
      self:Dyn_Emit("for i = math.Clamp(-Offset,0,8191),math.Clamp(Count,0,8191) do") --Shifted part
        self:Dyn_Emit("Buffer[i] = VM:ReadCell(ESI+i+Offset)")
      self:Dyn_Emit("end")
      self:Dyn_EmitInterruptCheck()
      self:Dyn_Emit("for i = 0,math.Clamp(-Offset-1,0,8191) do") --Remaining part
        self:Dyn_Emit("Buffer[i] = VM:ReadCell(ESI+i+(Count-1+Offset+1))")
      self:Dyn_Emit("end")
      self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("end")

    self:Dyn_Emit("for i = 0,Count-1 do")
      self:Dyn_Emit("VM:WriteCell(ESI+i,Buffer[i] or 32)")
    self:Dyn_Emit("end")
    self:Dyn_EmitInterruptCheck()

    self:Dyn_Emit("ESI = ESI + Count")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[131] = function(self)  --SMAP
  self:Dyn_Emit("$L FirstAddr")
  self:Dyn_Emit("$L LastAddr")
  self:Dyn_Emit("if VM.BlockSize > 0 then")
    self:Dyn_Emit("FirstAddr = VM.BlockStart")
    self:Dyn_Emit("LastAddr = FirstAddr + math.Clamp(VM.BlockSize,0,8192)")
    self:Dyn_Emit("VM.BlockSize = 0")
  self:Dyn_Emit("else")
    self:Dyn_Emit("FirstAddr = $1 * 128")
    self:Dyn_Emit("LastAddr = $1 * 128 + 127")
  self:Dyn_Emit("end")

  self:Dyn_Emit("$L ADDR = FirstAddr")
  self:Dyn_Emit("while ADDR < LastAddr do")
    self:Dyn_Emit("$L IDX = math.floor(ADDR / 128)")
    self:Dyn_Emit("$L PAGE = VM:GetPageByIndex(IDX)")
    self:Dyn_EmitInterruptCheck()

    self:Dyn_Emit("if VM.CurrentPage.RunLevel <= PAGE.RunLevel then")
      self:Dyn_Emit("PAGE.MappedIndex = $2")
      self:Dyn_Emit("PAGE.Remapped = 1")
      self:Dyn_Emit("VM:SetPageByIndex(IDX)")
      self:Dyn_EmitInterruptCheck()

      self:Dyn_Emit("for address=IDX*128,IDX*128+127 do")
        self:Dyn_Emit("VM:InvalidateVirtualPrecompileAddress(address)")
      self:Dyn_Emit("end")
    self:Dyn_Emit("else")
      self:Dyn_EmitInterrupt("11","IDX")
    self:Dyn_Emit("end")
    self:Dyn_Emit("ADDR = ADDR + 128")
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[132] = function(self)  --GMAP
  self:Dyn_EmitOperand("OP")
  self:Dyn_BeginUnprivilegedCode(0)
    self:Dyn_Emit("if VM.PreqHandled == 1 then")
      self:Dyn_Emit("OP = VM.PreqReturn or 0")
      self:Dyn_Emit("VM.PreqReturn = 0")
      self:Dyn_Emit("VM.PreqHandled = 0")
    self:Dyn_Emit("elseif VM.PreqHandled == 0 then")
      -- 132 is the opcode to send to LADD for int 13
      self:Dyn_EmitUnprivilegedRequestInterrupt(132)
    self:Dyn_Emit("else")
      self:Dyn_Emit("VM.PreqHandled = 0")
    self:Dyn_Emit("end")
  self:Dyn_Emit("else")
    -- In this case we're privileged, so just handle it as usual
    self:Dyn_Emit("$L IDX = math.floor($2 / 128)")
    self:Dyn_Emit("$L PAGE = VM:GetPageByIndex(IDX)")
    self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("OP = (PAGE and PAGE.MappedIndex) or 0")
  self:Dyn_EndUnprivilegedCode()
end
ZVM.OpcodeTable[133] = function(self)  --RSTACK
  self:Dyn_EmitForceRegisterGlobal("ESP")
  self:Dyn_EmitOperand("VM:ReadFromStack($2)")
  self:Dyn_EmitInterruptCheck()
end
ZVM.OpcodeTable[134] = function(self)  --SSTACK
  self:Dyn_EmitForceRegisterGlobal("ESP")
  self:Dyn_Emit("VM:WriteToStack($1,$2)")
  self:Dyn_EmitInterruptCheck()
end
ZVM.OpcodeTable[135] = function(self)  --ENTER
  self:Dyn_EmitForceRegisterGlobal("ESP")
  self:Dyn_EmitForceRegisterLocal("EBP")

  self:Dyn_Emit("VM:Push(EBP)")
  self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("EBP = VM.ESP+1")
  self:Dyn_Emit("VM.ESP = VM.ESP-$1")
end
ZVM.OpcodeTable[136] = function(self)  --IRETP
  self:Dyn_Emit("VM.PTBL = $1")
  self.OpcodeTable[41](self) -- as IRET
end
ZVM.OpcodeTable[137] = function(self)  --EXTRETP
  self:Dyn_Emit("VM.PTBL = $1")
  self.OpcodeTable[110](self) -- as EXTRET
end
ZVM.OpcodeTable[139] = function(self)  --CLD
    -- TODO: Remove microcode debugging, it's not possible to access normally.
  if self.MicrocodeDebug then
    self:Dyn_Emit("VM.Debug = false")
  end
end
--------------------------------------------------------------------------------
ZVM.OpcodeTable[140] = function(self) --EXTRETA
  self:Dyn_EmitForceRegisterGlobal("ESP")
  self:Dyn_Emit("$L V = 0")
  self:Dyn_EmitState()

  self:Dyn_Emit("V = VM:Pop()") -- IRET CS
  self:Dyn_EmitInterruptCheck()

  self:Dyn_Emit("V = VM:Pop()") -- IRET EIP
  self:Dyn_EmitInterruptCheck()

  for i=0,31 do
    self:Dyn_Emit("V = VM:Pop()") self:Dyn_EmitInterruptCheck() self:Dyn_Emit("VM.R%d = V")
  end

  self:Dyn_Emit("V = VM:Pop()") self:Dyn_EmitInterruptCheck() self:Dyn_Emit("$L IP = V")
  self:Dyn_Emit("V = VM:Pop()") self:Dyn_EmitInterruptCheck() self:Dyn_Emit("VM.CMPR = V")

  self:Dyn_Emit("V = VM:Pop()") self:Dyn_EmitInterruptCheck() self:Dyn_Emit("VM.EAX = V")
  self:Dyn_Emit("V = VM:Pop()") self:Dyn_EmitInterruptCheck() self:Dyn_Emit("VM.EBX = V")
  self:Dyn_Emit("V = VM:Pop()") self:Dyn_EmitInterruptCheck() self:Dyn_Emit("VM.ECX = V")
  self:Dyn_Emit("V = VM:Pop()") self:Dyn_EmitInterruptCheck() self:Dyn_Emit("VM.EDX = V")
  self:Dyn_Emit("V = VM:Pop()") self:Dyn_EmitInterruptCheck() self:Dyn_Emit("VM.EBP = V")
  self:Dyn_Emit("V = VM:Pop()") self:Dyn_EmitInterruptCheck() -- Do not set ESP right now
  self:Dyn_Emit("V = VM:Pop()") self:Dyn_EmitInterruptCheck() self:Dyn_Emit("VM.ESI = V")
  self:Dyn_Emit("V = VM:Pop()") self:Dyn_EmitInterruptCheck() self:Dyn_Emit("VM.EDI = V")

  self:Dyn_Emit("V = VM:Pop()") self:Dyn_EmitInterruptCheck() self:Dyn_Emit("$L CS = V")
  self:Dyn_Emit("V = VM:Pop()") self:Dyn_EmitInterruptCheck() -- Do not set SS right now
  self:Dyn_Emit("V = VM:Pop()") self:Dyn_EmitInterruptCheck() self:Dyn_Emit("VM.DS = V")
  self:Dyn_Emit("V = VM:Pop()") self:Dyn_EmitInterruptCheck() self:Dyn_Emit("VM.FS = V")
  self:Dyn_Emit("V = VM:Pop()") self:Dyn_EmitInterruptCheck() self:Dyn_Emit("VM.GS = V")
  self:Dyn_Emit("V = VM:Pop()") self:Dyn_EmitInterruptCheck() self:Dyn_Emit("VM.ES = V")
  self:Dyn_Emit("V = VM:Pop()") self:Dyn_EmitInterruptCheck() self:Dyn_Emit("VM.KS = V")
  self:Dyn_Emit("V = VM:Pop()") self:Dyn_EmitInterruptCheck() self:Dyn_Emit("VM.LS = V")
  self:Dyn_Emit("VM:Jump(IP,CS)")

  self:Dyn_EmitBreak()
  self.PrecompileBreak = true
end
ZVM.OpcodeTable[141] = function(self) --EXTRETPA
  self:Dyn_Emit("VM.PTBL = $1")
  self.OpcodeTable[140](self) -- as EXTRETP
end


ZVM.OpcodeTable[150] = function(self) -- STERR
    self:Dyn_Emit("VM:SignalError(VM:MakeErrorCode($1,$2))")
end

ZVM.OpcodeTable[151] = function(self) -- CLERR
    self:Dyn_Emit("VM:SignalError(0)")
end

ZVM.OpcodeTable[152] = function(self) -- QUOCMP
  self:Dyn_Emit("VM.CMPR = VM.QUOFLAG")
  self:Dyn_Emit("VM.QUOFLAG = 0")
end

ZVM.OpcodeTable[153] = function(self) -- QUOTIMER
  self:Dyn_EmitOperand("VM.LASTQUO")
end

--------------------------------------------------------------------------------
ZVM.OpcodeTable[250] = function(self)  --VADD
  local seg1code = self.EmitOperandSegment[1] and "0" or "VM.DS"
  local seg2code = self.EmitOperandSegment[2] and "0" or "VM.DS"
  self:Dyn_Emit("if VM.VMODE == 2 then")
    self:Dyn_Emit("$L V1 = VM:ReadVector2f($1 + %s)",seg1code)
    self:Dyn_Emit("$L V2 = VM:ReadVector2f($2 + %s)",seg2code)
    self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("VM:WriteVector2f($1 + %s,",seg1code)
      self:Dyn_Emit("{x = V1.x+V2.x, y=V1.y+V2.y, z=0})")
    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("elseif VM.VMODE == 3 then")
    self:Dyn_Emit("$L V1 = VM:ReadVector3f($1 + %s)",seg1code)
    self:Dyn_Emit("$L V2 = VM:ReadVector3f($2 + %s)",seg2code)
    self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("VM:WriteVector3f($1 + %s,",seg1code)
      self:Dyn_Emit("{x = V1.x+V2.x, y=V1.y+V2.y, z=V1.z+V2.z})")
    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[251] = function(self)  --VSUB
  local seg1code = self.EmitOperandSegment[1] and "0" or "VM.DS"
  local seg2code = self.EmitOperandSegment[2] and "0" or "VM.DS"
  self:Dyn_Emit("if VM.VMODE == 2 then")
    self:Dyn_Emit("$L V1 = VM:ReadVector2f($1 + %s)",seg1code)
    self:Dyn_Emit("$L V2 = VM:ReadVector2f($2 + %s)",seg2code)
    self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("VM:WriteVector2f($1 + %s,",seg1code)
      self:Dyn_Emit("{x = V1.x-V2.x, y=V1.y-V2.y, z=0})")
    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("elseif VM.VMODE == 3 then")
    self:Dyn_Emit("$L V1 = VM:ReadVector3f($1 + %s)",seg1code)
    self:Dyn_Emit("$L V2 = VM:ReadVector3f($2 + %s)",seg2code)
    self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("VM:WriteVector3f($1 + %s,",seg1code)
      self:Dyn_Emit("{x = V1.x-V2.x, y=V1.y-V2.y, z=V1.z-V2.z})")
    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[252] = function(self)  --VMUL
  local seg1code = self.EmitOperandSegment[1] and "0" or "VM.DS"
  self:Dyn_Emit("if VM.VMODE == 2 then")
    self:Dyn_Emit("$L V1 = VM:ReadVector2f($1 + %s)",seg1code)
    self:Dyn_Emit("$L V2 = $2")
    self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("VM:WriteVector2f($1 + %s,",seg1code)
      self:Dyn_Emit("{x = V1.x*V2, y=V1.y*V2, z=0})")
    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("elseif VM.VMODE == 3 then")
    self:Dyn_Emit("$L V1 = VM:ReadVector3f($1 + %s)",seg1code)
    self:Dyn_Emit("$L V2 = $2")
    self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("VM:WriteVector3f($1 + %s,",seg1code)
      self:Dyn_Emit("{x = V1.x*V2, y=V1.y*V2, z=V1.z*V2})")
    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[253] = function(self)  --VDOT
  local seg1code = self.EmitOperandSegment[1] and "0" or "VM.DS"
  local seg2code = self.EmitOperandSegment[2] and "0" or "VM.DS"
  self:Dyn_Emit("if VM.VMODE == 2 then")
    self:Dyn_Emit("$L V1 = VM:ReadVector2f($1 + %s)",seg1code)
    self:Dyn_Emit("$L V2 = VM:ReadVector2f($2 + %s)",seg2code)
    self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("VM:WriteCell($1 + %s,",seg1code)
      self:Dyn_Emit("V1.x*V2.x+V1.y*V2.y)")
    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("elseif VM.VMODE == 3 then")
    self:Dyn_Emit("$L V1 = VM:ReadVector3f($1 + %s)",seg1code)
    self:Dyn_Emit("$L V2 = VM:ReadVector3f($2 + %s)",seg2code)
    self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("VM:WriteCell($1 + %s,",seg1code)
      self:Dyn_Emit("V1.x*V2.x+V1.y*V2.y+V1.z*V2.z)")
    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[254] = function(self)  --VCROSS
  local seg1code = self.EmitOperandSegment[1] and "0" or "VM.DS"
  local seg2code = self.EmitOperandSegment[2] and "0" or "VM.DS"
  self:Dyn_Emit("if VM.VMODE == 2 then")
    self:Dyn_Emit("$L V1 = VM:ReadVector2f($1 + %s)",seg1code)
    self:Dyn_Emit("$L V2 = VM:ReadVector2f($2 + %s)",seg2code)
    self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("VM:WriteCell($1 + %s,",seg1code)
      self:Dyn_Emit("V1.x*V2.y-V1.y*V2.x)")
    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("elseif VM.VMODE == 3 then")
    self:Dyn_Emit("$L V1 = VM:ReadVector3f($1 + %s)",seg1code)
    self:Dyn_Emit("$L V2 = VM:ReadVector3f($2 + %s)",seg2code)
    self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("VM:WriteVector3f($1 + %s,",seg1code)
      self:Dyn_Emit("{x = V1.y*V2.z-V1.z*V2.y, y=V1.z*V2.x-V1.x*V2.z, z=V1.x*V2.y-V1.y*V2.x})")
    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[255] = function(self)  --VMOV
  local seg1code = self.EmitOperandSegment[1] and "0" or "VM.DS"
  local seg2code = self.EmitOperandSegment[2] and "0" or "VM.DS"
  self:Dyn_Emit("if VM.VMODE == 2 then")
    self:Dyn_Emit("$L V = VM:ReadVector2f($2 + %s)",seg2code)
    self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("VM:WriteVector2f($1 + %s,V)",seg1code)
    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("elseif VM.VMODE == 3 then")
    self:Dyn_Emit("$L V = VM:ReadVector3f($2 + %s)",seg2code)
    self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("VM:WriteVector3f($1 + %s,V)",seg1code)
    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[256] = function(self)  --VNORM
  local seg1code = self.EmitOperandSegment[1] and "0" or "VM.DS"
  local seg2code = self.EmitOperandSegment[2] and "0" or "VM.DS"
  self:Dyn_Emit("if VM.VMODE == 2 then")
    self:Dyn_Emit("$L V = VM:ReadVector2f($2 + %s)",seg2code)
    self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("$L D = (V.x^2+V.y^2)^(1/2)+1e-8")
    self:Dyn_Emit("VM:WriteVector2f($1 + %s,",seg1code)
      self:Dyn_Emit("{x = V.x/D, y = V.y/D, z = 0})")
    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("elseif VM.VMODE == 3 then")
    self:Dyn_Emit("$L V = VM:ReadVector3f($2 + %s)",seg2code)
    self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("$L D = (V.x^2+V.y^2+V.z^2)^(1/2)+1e-8")
    self:Dyn_Emit("VM:WriteVector3f($1 + %s,",seg1code)
      self:Dyn_Emit("{x = V.x/D, y = V.y/D, z = V.z/D})")
    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[257] = function(self)  --VCOLORNORM
  local seg1code = self.EmitOperandSegment[1] and "0" or "VM.DS"
  local seg2code = self.EmitOperandSegment[2] and "0" or "VM.DS"
  self:Dyn_Emit("$L V = VM:ReadVector4f($2 + %s)",seg2code)
  self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("V.x = math.min(255,math.max(0,V.x))")
  self:Dyn_Emit("V.y = math.min(255,math.max(0,V.y))")
  self:Dyn_Emit("V.z = math.min(255,math.max(0,V.z))")
  self:Dyn_Emit("V.w = math.min(255,math.max(0,V.w))")
  self:Dyn_Emit("VM:WriteVector4f($1 + %s,V)",seg1code)
  self:Dyn_EmitInterruptCheck()
end
ZVM.OpcodeTable[259] = function(self)  --LOOPXY
  self:Dyn_EmitForceRegisterLocal("ECX")
  self:Dyn_EmitForceRegisterLocal("EDX")
  self:Dyn_Emit("EDX = EDX - 1")
  self:Dyn_Emit("if EDX ~= -1 then")
    self:Dyn_Emit("VM:Jump($2)")
    self:Dyn_EmitState()
    self:Dyn_EmitBreak()
  self:Dyn_Emit("else")
    self:Dyn_Emit("ECX = ECX - 1")
    self:Dyn_Emit("if ECX ~= -1 then")
      self:Dyn_Emit("VM:Jump($1)")
      self:Dyn_EmitState()
      self:Dyn_EmitBreak()
    self:Dyn_Emit("end")
  self:Dyn_Emit("end")
end
--------------------------------------------------------------------------------
ZVM.OpcodeTable[260] = function(self)  --MADD
  local seg1code = self.EmitOperandSegment[1] and "0" or "VM.DS"
  local seg2code = self.EmitOperandSegment[2] and "0" or "VM.DS"
  self:Dyn_Emit("$L M1 = VM:ReadMatrix($1 + %s)",seg1code)
  self:Dyn_Emit("$L M2 = VM:ReadMatrix($2 + %s)",seg2code)
  self:Dyn_EmitInterruptCheck()

  self:Dyn_Emit("$L RM = {}")
  self:Dyn_Emit("for i=0,15 do RM[i] = M1[i]+M2[i] end")

  self:Dyn_Emit("VM:WriteMatrix($1 + %s,RM)",seg1code)
  self:Dyn_EmitInterruptCheck()
end
ZVM.OpcodeTable[261] = function(self)   --MSUB
  local seg1code = self.EmitOperandSegment[1] and "0" or "VM.DS"
  local seg2code = self.EmitOperandSegment[2] and "0" or "VM.DS"
  self:Dyn_Emit("$L M1 = VM:ReadMatrix($1 + %s)",seg1code)
  self:Dyn_Emit("$L M2 = VM:ReadMatrix($2 + %s)",seg2code)
  self:Dyn_EmitInterruptCheck()

  self:Dyn_Emit("$L RM = {}")
  self:Dyn_Emit("for i=0,15 do RM[i] = M1[i]-M2[i] end")

  self:Dyn_Emit("VM:WriteMatrix($1 + %s,RM)",seg1code)
  self:Dyn_EmitInterruptCheck()
end
ZVM.OpcodeTable[262] = function(self)   --MMUL
  local seg1code = self.EmitOperandSegment[1] and "0" or "VM.DS"
  local seg2code = self.EmitOperandSegment[2] and "0" or "VM.DS"
  self:Dyn_Emit("$L M1 = VM:ReadMatrix($1 + %s)",seg1code)
  self:Dyn_Emit("$L M2 = VM:ReadMatrix($2 + %s)",seg2code)
  self:Dyn_EmitInterruptCheck()

  self:Dyn_Emit("$L RM = {}")
  self:Dyn_Emit("for i=0,3 do")
    self:Dyn_Emit("for j=0,3 do")
      self:Dyn_Emit("RM[i*4+j] = M1[i*4+0]*M2[0*4+j] +")
      self:Dyn_Emit("            M1[i*4+1]*M2[1*4+j] +")
      self:Dyn_Emit("            M1[i*4+2]*M2[2*4+j] +")
      self:Dyn_Emit("            M1[i*4+3]*M2[3*4+j]")
    self:Dyn_Emit("end")
  self:Dyn_Emit("end")

  self:Dyn_Emit("VM:WriteMatrix($1 + %s,RM)",seg1code)
  self:Dyn_EmitInterruptCheck()
end
ZVM.OpcodeTable[263] = function(self)   --MROTATE
  local seg1code = self.EmitOperandSegment[1] and "0" or "VM.DS"
  local seg2code = self.EmitOperandSegment[2] and "0" or "VM.DS"
  self:Dyn_Emit("$L VEC = VM:ReadVector4f($2 + %s)",seg2code)
  self:Dyn_EmitInterruptCheck()

  self:Dyn_Emit("$L MAG = math.sqrt(VEC.x^2+VEC.y^2+VEC.z^2)+1e-7")
  self:Dyn_Emit("VEC.x = VEC.x / MAG")
  self:Dyn_Emit("VEC.y = VEC.y / MAG")
  self:Dyn_Emit("VEC.z = VEC.z / MAG")

  self:Dyn_Emit("$L SIN = math.sin(VEC.w)")
  self:Dyn_Emit("$L COS = math.cos(VEC.w)")

  self:Dyn_Emit("$L ab = VEC.x * VEC.y * (1 - COS)")
  self:Dyn_Emit("$L bc = VEC.y * VEC.z * (1 - COS)")
  self:Dyn_Emit("$L ca = VEC.z * VEC.x * (1 - COS)")
  self:Dyn_Emit("$L tx = VEC.x * VEC.x")
  self:Dyn_Emit("$L ty = VEC.y * VEC.y")
  self:Dyn_Emit("$L tz = VEC.z * VEC.z")

  self:Dyn_Emit("$L RM = {}")
  self:Dyn_Emit("RM[0]  = tx + COS * (1 - tx)")
  self:Dyn_Emit("RM[1]  = ab + VEC.z * SIN")
  self:Dyn_Emit("RM[2]  = ca - VEC.y * SIN")
  self:Dyn_Emit("RM[3]  = 0")
  self:Dyn_Emit("RM[4]  = ab - VEC.z * SIN")
  self:Dyn_Emit("RM[5]  = ty + COS * (1 - ty)")
  self:Dyn_Emit("RM[6]  = bc + VEC.x * SIN")
  self:Dyn_Emit("RM[7]  = 0")
  self:Dyn_Emit("RM[8]  = ca + VEC.y * SIN")
  self:Dyn_Emit("RM[9]  = bc - VEC.x * SIN")
  self:Dyn_Emit("RM[10] = tz + COS * (1 - tz)")
  self:Dyn_Emit("RM[11] = 0")
  self:Dyn_Emit("RM[12] = 0")
  self:Dyn_Emit("RM[13] = 0")
  self:Dyn_Emit("RM[14] = 0")
  self:Dyn_Emit("RM[15] = 1")

  self:Dyn_Emit("VM:WriteMatrix($1 + %s,RM)",seg1code)
  self:Dyn_EmitInterruptCheck()
end
ZVM.OpcodeTable[264] = function(self)   --MSCALE
  local seg1code = self.EmitOperandSegment[1] and "0" or "VM.DS"
  local seg2code = self.EmitOperandSegment[2] and "0" or "VM.DS"
  self:Dyn_Emit("$L VEC = VM:ReadVector3f($2 + %s)",seg2code)
  self:Dyn_EmitInterruptCheck()

  self:Dyn_Emit("$L RM  = {}")
  self:Dyn_Emit("RM[0]  = VEC.x")
  self:Dyn_Emit("RM[1]  = 0")
  self:Dyn_Emit("RM[2]  = 0")
  self:Dyn_Emit("RM[3]  = 0")

  self:Dyn_Emit("RM[4]  = 0")
  self:Dyn_Emit("RM[5]  = VEC.y")
  self:Dyn_Emit("RM[6]  = 0")
  self:Dyn_Emit("RM[7]  = 0")

  self:Dyn_Emit("RM[8]  = 0")
  self:Dyn_Emit("RM[9]  = 0")
  self:Dyn_Emit("RM[10] = VEC.z")
  self:Dyn_Emit("RM[11] = 0")

  self:Dyn_Emit("RM[12] = 0")
  self:Dyn_Emit("RM[13] = 0")
  self:Dyn_Emit("RM[14] = 0")
  self:Dyn_Emit("RM[15] = 1")

  self:Dyn_Emit("VM:WriteMatrix($1 + %s,RM)",seg1code)
  self:Dyn_EmitInterruptCheck()
end
ZVM.OpcodeTable[265] = function(self)   --MPERSPECTIVE
  local seg1code = self.EmitOperandSegment[1] and "0" or "VM.DS"
  local seg2code = self.EmitOperandSegment[2] and "0" or "VM.DS"
  self:Dyn_Emit("$L VEC = VM:ReadVector4f($2 + %s)",seg2code)
  self:Dyn_EmitInterruptCheck()

  self:Dyn_Emit("$L DZ = VEC.w - VEC.z")
  self:Dyn_Emit("$L RADS = (VEC.x / 2.0) * math.pi / 180")
  self:Dyn_Emit("$L SIN = math.sin(RADS)")
  self:Dyn_Emit("$L CTG = math.cos(RADS)/SIN")

  self:Dyn_Emit("$L RM = {}")
  self:Dyn_Emit("RM[0]  = CTG / VEC.y")
  self:Dyn_Emit("RM[4]  = 0")
  self:Dyn_Emit("RM[8]  = 0")
  self:Dyn_Emit("RM[12] = 0")

  self:Dyn_Emit("RM[1]  = 0")
  self:Dyn_Emit("RM[5]  = CTG")
  self:Dyn_Emit("RM[9]  = 0")
  self:Dyn_Emit("RM[13] = 0")

  self:Dyn_Emit("RM[2]  = 0")
  self:Dyn_Emit("RM[6]  = 0")
  self:Dyn_Emit("RM[10] = -(VEC.z + VEC.w) / DZ")
  self:Dyn_Emit("RM[14] = -2*VEC.z*VEC.w / DZ")

  self:Dyn_Emit("RM[3]  = 0")
  self:Dyn_Emit("RM[7]  = 0")
  self:Dyn_Emit("RM[11] = -1")
  self:Dyn_Emit("RM[15] = 0")

  self:Dyn_Emit("VM:WriteMatrix($1 + %s,RM)",seg1code)
  self:Dyn_EmitInterruptCheck()
end
ZVM.OpcodeTable[266] = function(self)   --MTRANSLATE
  local seg1code = self.EmitOperandSegment[1] and "0" or "VM.DS"
  local seg2code = self.EmitOperandSegment[2] and "0" or "VM.DS"
  self:Dyn_Emit("$L VEC = VM:ReadVector3f($2 + %s)",seg2code)
  self:Dyn_EmitInterruptCheck()

  self:Dyn_Emit("$L RM  = {}")
  self:Dyn_Emit("RM[0]  = 1")
  self:Dyn_Emit("RM[1]  = 0")
  self:Dyn_Emit("RM[2]  = 0")
  self:Dyn_Emit("RM[3]  = VEC.x")

  self:Dyn_Emit("RM[4]  = 0")
  self:Dyn_Emit("RM[5]  = 1")
  self:Dyn_Emit("RM[6]  = 0")
  self:Dyn_Emit("RM[7]  = VEC.y")

  self:Dyn_Emit("RM[8]  = 0")
  self:Dyn_Emit("RM[9]  = 0")
  self:Dyn_Emit("RM[10] = 1")
  self:Dyn_Emit("RM[11] = VEC.z")

  self:Dyn_Emit("RM[12] = 0")
  self:Dyn_Emit("RM[13] = 0")
  self:Dyn_Emit("RM[14] = 0")
  self:Dyn_Emit("RM[15] = 1")

  self:Dyn_Emit("VM:WriteMatrix($1 + %s,RM)",seg1code)
  self:Dyn_EmitInterruptCheck()
end
ZVM.OpcodeTable[267] = function(self)   --MLOOKAT
  local seg1code = self.EmitOperandSegment[1] and "0" or "VM.DS"
  local seg2code = self.EmitOperandSegment[2] and "0" or "VM.DS"
  self:Dyn_Emit("$L EYE = VM:ReadVector3f($2 + %s+0)",seg2code)
  self:Dyn_Emit("$L CENTER = VM:ReadVector3f($2 + %s+3)",seg2code)
  self:Dyn_Emit("$L UP = VM:ReadVector3f($2 + %s+6)",seg2code)
  self:Dyn_EmitInterruptCheck()

  self:Dyn_Emit("$L X = { 0, 0, 0 }")
  self:Dyn_Emit("$L Y = { UP.x, UP.y, UP.z }")
  self:Dyn_Emit("$L Z = { EYE.x - CENTER.x, EYE.y - CENTER.y, EYE.z - CENTER.z }")

  self:Dyn_Emit("$L ZMAG = math.sqrt(Z[1]^2+Z[2]^2+Z[3]^2)+1e-7")
  self:Dyn_Emit("Z[1] = Z[1] / ZMAG")
  self:Dyn_Emit("Z[2] = Z[2] / ZMAG")
  self:Dyn_Emit("Z[3] = Z[3] / ZMAG")

  self:Dyn_Emit("X[1] =  Y[2]*Z[3] - Y[3]*Z[2]")
  self:Dyn_Emit("X[2] = -Y[1]*Z[3] + Y[3]*Z[1]")
  self:Dyn_Emit("X[3] =  Y[1]*Z[2] - Y[2]*Z[1]")

  self:Dyn_Emit("Y[1] =  Z[2]*X[3] - Z[3]*X[2]")
  self:Dyn_Emit("Y[2] = -Z[1]*X[3] + Z[3]*X[1]")
  self:Dyn_Emit("Y[3] =  Z[1]*X[2] - Z[2]*X[1]")

  self:Dyn_Emit("$L XMAG = math.sqrt(X[1]^2+X[2]^2+X[3]^2)+1e-7")
  self:Dyn_Emit("X[1] = X[1] / XMAG")
  self:Dyn_Emit("X[2] = X[2] / XMAG")
  self:Dyn_Emit("X[3] = X[3] / XMAG")

  self:Dyn_Emit("$L YMAG = math.sqrt(Y[1]^2+Y[2]^2+Y[3]^2)+1e-7")
  self:Dyn_Emit("Y[1] = Y[1] / YMAG")
  self:Dyn_Emit("Y[2] = Y[2] / YMAG")
  self:Dyn_Emit("Y[3] = Y[3] / YMAG")

  self:Dyn_Emit("$L RM  = {}")
  self:Dyn_Emit("RM[0]  = X[1]")
  self:Dyn_Emit("RM[1]  = X[2]")
  self:Dyn_Emit("RM[2]  = X[3]")
  self:Dyn_Emit("RM[3]  = -X[1]*EYE.x + -X[2]*EYE.y + -X[3]*EYE.z")

  self:Dyn_Emit("RM[4]  = Y[1]")
  self:Dyn_Emit("RM[5]  = Y[2]")
  self:Dyn_Emit("RM[6]  = Y[3]")
  self:Dyn_Emit("RM[7]  = -Y[1]*EYE.x + -Y[2]*EYE.y + -Y[3]*EYE.z")

  self:Dyn_Emit("RM[8]  = Z[1]")
  self:Dyn_Emit("RM[9]  = Z[2]")
  self:Dyn_Emit("RM[10] = Z[3]")
  self:Dyn_Emit("RM[11] = -Z[1]*EYE.x + -Z[2]*EYE.y + -Z[3]*EYE.z")

  self:Dyn_Emit("RM[12] = 0")
  self:Dyn_Emit("RM[13] = 0")
  self:Dyn_Emit("RM[14] = 0")
  self:Dyn_Emit("RM[15] = 1")

  self:Dyn_Emit("VM:WriteMatrix($1 + %s,RM)",seg1code)
  self:Dyn_EmitInterruptCheck()
end
ZVM.OpcodeTable[268] = function(self)   --MMOV
  local seg1code = self.EmitOperandSegment[1] and "0" or "VM.DS"
  local seg2code = self.EmitOperandSegment[2] and "0" or "VM.DS"
  self:Dyn_Emit("$L M = VM:ReadMatrix($2 + %s)",seg2code)
  self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("VM:WriteMatrix($1 + %s,M)",seg1code)
  self:Dyn_EmitInterruptCheck()
end
ZVM.OpcodeTable[269] = function(self)   --VLEN
  local seg1code = self.EmitOperandSegment[1] and "0" or "VM.DS"
  local seg2code = self.EmitOperandSegment[2] and "0" or "VM.DS"
  self:Dyn_Emit("if VM.VMODE == 2 then")
    self:Dyn_Emit("$L V = VM:ReadVector2f($2 + %s)",seg2code)
    self:Dyn_EmitInterruptCheck()
    self:Dyn_EmitOperand(1,"(V.x^2+V.y^2)^0.5",true)
  self:Dyn_Emit("elseif VM.VMODE == 3 then")
    self:Dyn_Emit("$L V = VM:ReadVector3f($2 + %s)",seg2code)
    self:Dyn_EmitInterruptCheck()
    self:Dyn_EmitOperand(1,"(V.x^2+V.y^2+V.z^2)^0.5",true)
  self:Dyn_Emit("end")
end
--------------------------------------------------------------------------------
ZVM.OpcodeTable[270] = function(self)   --MIDENT
  local seg1code
  if not self.EmitOperandSegment[1] then
    seg1code = "VM.DS"
  end
  self:Dyn_Emit("$L M = {}")
  self:Dyn_Emit("M[ 0]=1  M[ 1]=0  M[ 2]=0  M[ 3]=0")
  self:Dyn_Emit("M[ 4]=0  M[ 5]=1  M[ 6]=0  M[ 7]=0")
  self:Dyn_Emit("M[ 8]=0  M[ 9]=0  M[10]=1  M[11]=0")
  self:Dyn_Emit("M[12]=0  M[13]=0  M[14]=0  M[15]=1")
  self:Dyn_Emit("VM:WriteMatrix($1 + %s,M)",seg1code)
  self:Dyn_EmitInterruptCheck()
end
ZVM.OpcodeTable[273] = function(self)   --VMODE
  self:Dyn_Emit("VM.VMODE = $1")
end
--------------------------------------------------------------------------------
ZVM.OpcodeTable[295] = function(self)  --VDIV
  local seg1code = self.EmitOperandSegment[1] and "0" or "VM.DS"
  self:Dyn_Emit("$L SCALAR = $2")
  self:Dyn_Emit("if VM.VMODE == 2 then")
    self:Dyn_Emit("$L V = VM:ReadVector2f($1 + %s)",seg1code)
    self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("V.x = V.x / SCALAR")
    self:Dyn_Emit("V.y = V.y / SCALAR")
    self:Dyn_Emit("VM:WriteVector2f($1 + %s,V)",seg1code)
    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("elseif VM.VMODE == 3 then")
    self:Dyn_Emit("$L V = VM:ReadVector3f($1 + %s)",seg1code)
    self:Dyn_EmitInterruptCheck()
    self:Dyn_Emit("V.x = V.x / SCALAR")
    self:Dyn_Emit("V.y = V.y / SCALAR")
    self:Dyn_Emit("V.z = V.z / SCALAR")
    self:Dyn_Emit("VM:WriteVector3f($1 + %s,V)",seg1code)
    self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit("end")
end
ZVM.OpcodeTable[296] = function(self)  --VTRANSFORM
  local seg1code = self.EmitOperandSegment[1] and "0" or "VM.DS"
  local seg2code = self.EmitOperandSegment[2] and "0" or "VM.DS"
  self:Dyn_Emit("local address_1 = $1 + $s",seg1code)
  self:Dyn_Emit("local address_2 = $2 + %s",seg2code)
  self:Dyn_Emit [[
    local V = {0, 0, 0, 1}
    if address_1~=0 then
      for i = 1, VM.VMODE do
        V[i] = VM:ReadCell(address_1 + i - 1) or 0
      end
    end
  ]]
  self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit [[local M = VM:ReadMatrix(address_2)]]
  self:Dyn_EmitInterruptCheck()
  self:Dyn_Emit [[
    for i = 0, VM.VMODE-1 do
      local result = M[i*4 + 0] * V[1] +
                     M[i*4 + 1] * V[2] +
                     M[i*4 + 2] * V[3] +
                     M[i*4 + 3] * V[4]
      VM:WriteCell(address_1 + i, result)
    end
  ]]
  self:Dyn_EmitInterruptCheck()
end
