--------------------------------------------------------------------------------
-- Zyelios VM (Zyelios CPU/GPU virtual machine)
--
-- Implementation for most default ZCPU features (including the data bus)
--------------------------------------------------------------------------------




--------------------------------------------------------------------------------
-- Default configuration
ZVM.RAMSize = 65536 -- Internal RAM size
ZVM.ROMSize = 65536 -- Internal ROM size
ZVM.PCAP    = 1     -- Paging capability
ZVM.RQCAP   = 1     -- Memory request delaying capability
ZVM.CPUVER  = 1000  -- Version reported by CPUID
ZVM.CPUTYPE = 0     -- Processor type
ZVM.ROM = {}

-- CPUID instruction result
function ZVM:CPUID(index)
  if index == 0 then
    return self.CPUVER  -- CPU version
  elseif index == 1 then
    return self.RAMSize -- Amount of internal RAM
  elseif index == 2 then
    return self.CPUTYPE -- 0: ZCPU, 1: ZGPU, 2: ZSPU
  elseif index == 3 then
    return self.ROMSize -- Amount of internal ROM
  end
end




--------------------------------------------------------------------------------
-- VM state reset
function ZVM:Reset()
  self.IP = 0        -- Instruction pointer

  self.EAX = 0       -- General purpose registers
  self.EBX = 0
  self.ECX = 0
  self.EDX = 0
  self.ESI = 0
  self.EDI = 0
  self.ESP = math.max(0,self.RAMSize-1)
  self.EBP = 0

  self.CS = 0        -- Segment pointer registers
  self.SS = 0
  self.DS = 0
  self.ES = 0
  self.GS = 0
  self.FS = 0
  self.KS = 0
  self.LS = 0

  -- Extended registers
  for reg=0,31 do self["R"..reg] = 0 end

  -- Stack size register
  self.ESZ = math.max(0,self.RAMSize-1)

  self.IDTR = 0      -- Interrupt descriptor table register
  self.NIDT = 256    -- Size of interrupt descriptor table
  self.EF = 0        -- Extended mode flag
  self.PF = 0        -- Protected mode flag
  self.MF = 0        -- Memory extended mode flag
  self.IF = 1        -- Interrupts enabled flag
  self.NIF = nil     -- Value of IF flag for next frame

  self.PTBL = 0      -- Page table offset
  self.PTBE = 0      -- Page table size

  self.CMPR = 0      -- Compare register
  self.XEIP = 0      -- Current instruction address register
  self.LADD = 0      -- Last interrupt parameter
  self.LINT = 0      -- Last interrupt number
  self.TMR = 0       -- Internal timer
  self.TIMER = 0     -- Internal clock
  self.CPAGE = 0     -- Current page ID
  self.PPAGE = 0     -- Previous page ID

  self.BPREC = 48    -- Binary precision for integer emulation mode (default: 48)
  self.IPREC = 48    -- Integer precision (48 - floating point mode, 8, 16, 32, 64 - integer mode)
  self.VMODE = 2     -- Vector mode (2D, 3D)

  self.CODEBYTES = 0 -- Executed size of code
  self.HWDEBUG = 0   -- Hardware debug mode
  self.DBGSTATE = 0  -- 0: halt; 1: reset; 2: step fwd and halt; 3: run; 4: read registers; 5: write registers
  self.DBGADDR = 0   -- 0: external ports, everything else: absolute memory address

  -- Timer system registers
  self.TimerMode = 0      -- 0: disable; NMI: 1: every X seconds; 2: every N ticks
  self.TimerRate = 0      -- Seconds or ticks
  self.TimerPrevTime = 0  -- Previous fire time
  self.TimerAddress  = 32 -- Interrupt number to call (modes 1,2)
  self.TimerPrevMode = 0  -- Previous timer mode

  -- Internal operation registers
  self.MEMRQ = 0           -- Handling a memory request (1: delayed request, 2: read request, 3: write request)
  self.MEMADDR = 0         -- Address of the memory request
  self.INTR = 0            -- Handling an interrupt
  self.BusLock = 0         -- Bus is locked for read/write
  self.Idle = 0            -- Idle flag
  self.External = 0        -- External IO operation

  -- Misc registers
  self.BlockStart = 0      -- Start of the block
  self.BlockSize = 0       -- Size of the block
  self.HaltPort = 0        -- Unused/obsolete
  self.TimerDT = 0         -- Timer deltastep within cached instructions block
  self.QUOTIMER = 0
  self.QUOCMP = 0
  self.PreqOperand1 = 0    -- Privileged Request Operands (used in interrupt 13 for opcodes requiring runlevel 0)
  self.PreqOperand2 = 0 
  self.PreqHandled = 0
  self.PreqReturn = 0

  -- Runlevel registers
  self.CRL  = 0            -- Current runlevel
  self.XTRL = 1            -- Runlevel for external IO

  -- Reset internal memory, precompiler data, page table
  self.Memory = {}
  self.PrecompiledData = {}
  self.IsAddressPrecompiled = {}
  self.PageData = {}

  -- Restore ROM to memory
  self.INTR = 1
  if self.ROMSize > 0 then
    for address,value in pairs(self.ROM) do
      self:WriteCell(address,value)
    end
  end

  -- Reset pages
  self:SetCurrentPage(0)
  self:SetPreviousPage(0)
  self.INTR = 0
end




--------------------------------------------------------------------------------
-- Checks if address is valid
local function IsValidAddress(n)
  return n and (math.floor(n) == n) and (n >= -140737488355327) and (n <= 140737488355328)
end




--------------------------------------------------------------------------------
function ZVM:SignalError(errorCode)

end

function ZVM:SignalShutdown()

end

function ZVM:ExternalWrite(Address,Value)
  if Address >= 0
  then self:Interrupt(7,Address) return false -- MemBus
  else return true                            -- IOBus
  end
end

function ZVM:ExternalRead(Address,Value)
  if Address >= 0
  then self:Interrupt(7,Address) return -- MemBus
  else return 0                         -- IOBus
  end
end




--------------------------------------------------------------------------------
-- Default WritePort handler
function ZVM:WritePort(Port,Value)
  self:WriteCell(-Port-1,Value)
end




--------------------------------------------------------------------------------
-- Default ReadPort handler
function ZVM:ReadPort(Port)
  return self:ReadCell(-Port-1)
end




--------------------------------------------------------------------------------
-- Default ReadCell handler
function ZVM:ReadCell(Address)
  -- Check bus lock flag
  if self.BusLock == 1 then return end

  -- Cycles required to perform memory read
  self.TMR = self.TMR + (self.MemoryReadCycles or 5)

  -- Check if address is valid
  if not IsValidAddress(Address) then
    self:Interrupt(15,Address)
    return
  end

  -- Do we need to perform page checking
  if self.PCAP == 1 and self.MF == 1 then
    -- Fetch page
    local PageIndex = math.floor(Address / 128)
    local Page = self:GetPageByIndex(PageIndex)

    if Page.Trapped == 1 then
      self:Interrupt(30,Address) --generate interrupt and continue
    end

    -- Check if page is disabled
    if Page.Disabled == 1 then
      self:Interrupt(7,Address)
      return
    end


    -- Permission and remap checks need to happen before override check
    -- so that we have data for the override interrupt to process
    -- Page permissions
    if (self.EF == 1) and (self.CurrentPage.RunLevel > Page.RunLevel) and (Page.Read == 0) then
      self:Interrupt(12,Address)
      return
    end

    -- Page remapping
    if (Page.Remapped == 1) and (Page.MappedIndex ~= PageIndex) then
      Address = Address % 128 + Page.MappedIndex * 128
    end
    local value
    -- Perform I/O operation
    if (Address >= 0) and (Address < self.RAMSize) then
      value = tonumber(self.Memory[Address]) or 0
    else
    -- Extra cycles for the external operation
      self.TMR = self.TMR + (self.ExternalReadCycles or 15)
      value = self:ExternalRead(Address)
    end

    -- Check if page is overriden
    if Page.Override == 1 then
      if self.MEMRQ == 4 then -- Data available
        self.MEMRQ = 0
        return tonumber(self.LADD) or 0
      else -- No data: generate a request
        self.MEMRQ = 2
        self.MEMADDR = Address
        self.LADD = value
        -- Extra cycles for early termination
        self.TMR = self.TMR + 10
        return
      end
    end
  end

  -- Perform I/O operation
  if (Address >= 0) and (Address < self.RAMSize) then
    return tonumber(self.Memory[Address]) or 0
  else
    -- Extra cycles for the external operation
    self.TMR = self.TMR + (self.ExternalReadCycles or 15)
    return self:ExternalRead(Address)
  end
end




--------------------------------------------------------------------------------
-- Default WriteCell handler
function ZVM:WriteCell(Address,Value)
  if not isnumber(Value) then Value = 0 end
  -- Check bus lock flag
  if self.BusLock == 1 then return false end

  -- Cycles required to perform memory write
  self.TMR = self.TMR + (self.MemoryWriteCycles or 5)

  -- Check if address is valid
  if not IsValidAddress(Address) then
    self:Interrupt(15,Address)
    return false
  end

  -- Do we need to perform page checking
  if self.PCAP == 1 and self.MF == 1 then
    -- Fetch page
    local PageIndex = math.floor(Address / 128)
    local Page = self:GetPageByIndex(PageIndex)

    if Page.Trapped == 1 then
        self:Interrupt(30,Address) -- Generate interrupt and continue
    end

    -- Check if page is disabled
    if Page.Disabled == 1 then
      self:Interrupt(7,Address)
      return false
    end

    -- MEMRQ: 0 - no action
    --        1 - ???
    --        2 - read interrupt requested
    --        3 - write interrupt requested
    --        4 - read interrupt handled
    --        5 - write address / value changed, write request is allowed
    --        6 - write request handled by interrupt, skip performing the write ourselves
    -- Check if page is overriden
    if Page.Override == 1 then
      if self.MEMRQ == 6 then -- Skip performing the write ourselves, the interrupt did it for us.
        self.MEMRQ = 0
        return true
      end
      if self.MEMRQ == 5 then -- write IRQ handled, new address/value available
        self.MEMRQ = 0
        Address = self.MEMADDR
        Value = self.LADD
        --return true
      else
        self.MEMRQ = 3
        self.MEMADDR = Address
        self.LADD = Value

        -- Extra cycles for early termination
        self.TMR = self.TMR + 10
        return false
      end
    end

    -- Page permissions
    if (self.EF == 1) and (self.CurrentPage.RunLevel > Page.RunLevel) and (Page.Write == 0) then
      self:Interrupt(9,Address)
      return false
    end

    -- Page remapping
    if (Page.Remapped == 1) and (Page.MappedIndex ~= PageIndex) then
      Address = Address % 128 + Page.MappedIndex * 128
    end
  end

  -- Invalidate precompiled data (Moved so the address will be changed to physical if necessary)
    self:InvalidatePrecompileAddress(Address)
    -- Perform I/O operation
  if (Address >= 0) and (Address < self.RAMSize) then
    self.Memory[Address] = Value
  else
    -- Extra cycles for the external operation
    self.TMR = self.TMR + (self.ExternalWriteCycles or 15)
    return self:ExternalWrite(Address,Value)
  end
end




--------------------------------------------------------------------------------
function ZVM:Push(Value)
  -- Check bus lock flag
  if self.BusLock == 1 then return false end

  -- Write to stack
  self:WriteCell(self.ESP+self.SS, Value)
  self.ESP = self.ESP - 1

  -- Stack check
  if self.ESP < 0 then
    self.ESP = 0
    self:Interrupt(6,self.ESP)
    return false
  end
  return true
end




--------------------------------------------------------------------------------
function ZVM:Pop()
  -- Check bus lock flag
  if self.BusLock == 1 then return 0 end

  -- Read from stack
  self.ESP = self.ESP + 1
  if self.ESP > self.ESZ then
    self.ESP = self.ESZ
    self:Interrupt(6,self.ESP)
    return 0
  end

  local Value = self:ReadCell(self.ESP+self.SS)
  if Value then return Value
  else self:Interrupt(6,self.ESP) return 0 end
end




--------------------------------------------------------------------------------
-- Write value to stack (SSTACK implementation)
function ZVM:WriteToStack(Index,Value)
  -- Check bus lock flag
  if self.BusLock == 1 then return false end

  -- Write to stack
  if (Index > self.ESZ) or (Index < 0) then self:Interrupt(6,Index) return false end
  self:WriteCell(self.SS + Index,Value)
end




--------------------------------------------------------------------------------
-- Read a value from stack (RSTACK implementation)
function ZVM:ReadFromStack(Index)
  -- Check bus lock flag
  if self.BusLock == 1 then return 0 end

  -- Read from stack
  if (Index > self.ESZ) or (Index < 0) then self:Interrupt(6,Index) return 0 end
  local Value = self:ReadCell(self.SS + Index)
  if Value then return Value else self:Interrupt(6,Index) return 0 end
end




--------------------------------------------------------------------------------
-- Extended mode stuff
local defaultPage = {
  Disabled = 0,    -- 00 Is page disabled? Set to 1 to disable this page
  Remapped = 0,    -- 01 Is page remapped? Set to 1 to remap this page
  Trapped = 0,     -- 02 Page must generate NMI 30 (page trap) upon access
  Override = 0,    -- 03 Page overrides reading/writing from it
  Read = 0,        -- 05 Read permissions (0: allowed, 1: disabled)
  Write = 0,       -- 06 Write permissions (0: allowed, 1: disabled)
  Execute = 0,     -- 07 Execute permissions (0: allowed, 1: disabled)
  RunLevel = 0,
  MappedIndex = 0,
}

local errorPage = {
  Disabled = 1,
  Remapped = 0,
  Trapped = 0,
  Override = 0,
  Read = 0,
  Write = 0,
  Execute = 0,
  RunLevel = 0,
  MappedIndex = 0,
}

function ZVM:ResetPage(index)
  local newPage = {}
  newPage.Disabled    = 0
  newPage.Remapped    = 0
  newPage.Trapped     = 0
  newPage.Override    = 0
  newPage.Read        = 1
  newPage.Write       = 1
  newPage.Execute     = 1
  newPage.RunLevel    = 0
  newPage.MappedIndex = 0

  self.PageData[index] = newPage
end

function ZVM:SetPagePermissions(index,permissionMask,mappedPage)
  if not self.PageData[index] then self:ResetPage(index) end
  local targetPage = self.PageData[index]

  local permissionBits = self:IntegerToBinary(permissionMask)
  local runlevel = math.floor(permissionMask / 256) % 256

  targetPage.Disabled    = permissionBits[0]
  targetPage.Remapped    = permissionBits[1]
  targetPage.Trapped     = permissionBits[2]
  targetPage.Override    = permissionBits[3]
  targetPage.Read        = 1-permissionBits[5]
  targetPage.Write       = 1-permissionBits[6]
  targetPage.Execute     = 1-permissionBits[7]
  targetPage.RunLevel    = runlevel
  targetPage.MappedIndex = mappedPage
end

function ZVM:GetPagePermissions(index)
  if not self.PageData[index] then self:ResetPage(index) end
  local sourcePage = self.PageData[index]

  local permissionBits = {}
  permissionBits[0] = sourcePage.Disabled
  permissionBits[1] = sourcePage.Remapped
  permissionBits[2] = sourcePage.Trapped
  permissionBits[3] = sourcePage.Override
  permissionBits[5] = 1-sourcePage.Read
  permissionBits[6] = 1-sourcePage.Write
  permissionBits[7] = 1-sourcePage.Execute

  return self:BinaryToInteger(permissionBits) + sourcePage.RunLevel * 256,sourcePage.MappedIndex
end




--------------------------------------------------------------------------------
function ZVM:GetPageByIndex(index)
  if self.PCAP == 1 then
    if self.MF == 1 then
      -- Find page entry offset
      local pageEntryOffset
      if (index >= self.PTBE) or (index < 0)
      then pageEntryOffset = self.PTBL
      else pageEntryOffset = self.PTBL+(index+1)*2
      end

      -- Read page entry
      self.PCAP = 0 -- Stop infinite recursive page table lookup
      local pagePermissionMask = self:ReadCell(pageEntryOffset+0)
      local pageMappedTo = self:ReadCell(pageEntryOffset+1)
      self.PCAP = 1

      if (not pagePermissionMask) or (not pageMappedTo) then
        self:Interrupt(13,8)
        return errorPage
      end

      self:SetPagePermissions(index,pagePermissionMask,pageMappedTo)
      return self.PageData[index]
    else
      if not self.PageData[index] then self:ResetPage(index) end
      return self.PageData[index]
    end
  else
    return defaultPage
  end
end




--------------------------------------------------------------------------------
function ZVM:SetPageByIndex(index)
  if self.PCAP == 1 then
    if self.MF == 1 then
      -- Find page entry offset
      local pageEntryOffset
      if (index >= self.PTBE) or (index < 0)
      then pageEntryOffset = self.PTBL
      else pageEntryOffset = self.PTBL+(index+1)*2
      end

      -- Write page entry
      local pagePermissionMask,pageMappedTo = self:GetPagePermissions(index)
      self.PCAP = 0 -- Stop possible infinite recursive page redirection
      self:WriteCell(pageEntryOffset+0,pagePermissionMask)
      self:WriteCell(pageEntryOffset+1,pageMappedTo)
      self.PCAP = 1
    end
  end
end




--------------------------------------------------------------------------------
function ZVM:SetCurrentPage(index)
  if self.PCAP == 1 then
    self.CurrentPage = self:GetPageByIndex(index)
  else
    self.CurrentPage = defaultPage
  end
end




--------------------------------------------------------------------------------
function ZVM:SetPreviousPage(index)
  if self.PCAP == 1 then
    self.PreviousPage = self:GetPageByIndex(index)
  else
    self.PreviousPage = defaultPage
  end
end




--------------------------------------------------------------------------------
function ZVM:Jump(newIP,newCS)
  local targetXEIP = newIP + (newCS or self.CS)
  local targetPage = self:GetPageByIndex(math.floor(targetXEIP/128))

  -- Do not allow execution if not calling from kernel page
  if (self.PCAP == 1) and (targetPage.Execute == 0) and (self.CurrentPage.RunLevel ~= 0) then
    self:Interrupt(14,newIP)
    return -- Jump failed
  end

  self.IP = newIP
  if newCS then
    self.CS = newCS
  end
end




--------------------------------------------------------------------------------
function ZVM:ExternalInterrupt(interruptNo)
  if ((self.IF == 1) and
       self:Push(self.LS) and
       self:Push(self.KS) and
       self:Push(self.ES) and
       self:Push(self.GS) and
       self:Push(self.FS) and
       self:Push(self.DS) and
       self:Push(self.SS) and
       self:Push(self.CS) and

       self:Push(self.EDI) and
       self:Push(self.ESI) and
       self:Push(self.ESP) and
       self:Push(self.EBP) and
       self:Push(self.EDX) and
       self:Push(self.ECX) and
       self:Push(self.EBX) and
       self:Push(self.EAX) and

       self:Push(self.CMPR) and
       self:Push(self.IP)) then
      self:Interrupt(interruptNo,0,1)
  end
end


function ZVM:MakeErrorCode(code,param) 
  return code+(param * (10^math.floor(-math.log10(math.abs(param)+1e-12)-1)))
end

--------------------------------------------------------------------------------
function ZVM:Interrupt(interruptNo,interruptParameter,isExternal,cascadeInterrupt)
  -- Do not allow cascade interrupts unless they are explicty stated as such
  if (not cascadeInterrupt) and (self.INTR == 1) then return end

  -- Interrupt is active, lock the bus to prevent any further read/write
  self.INTR = 1

  -- Set registers
  self.LINT = interruptNo
  self.LADD = interruptParameter or self.XEIP

  -- Output an error externally
  self:SignalError(self:MakeErrorCode(interruptNo,self.LADD))

  -- Check if interrupts handling is enabled
  if self.IF == 1 then
    if self.EF == 1 then -- Extended mode
      -- Boundary check
      if (interruptNo < 0) or (interruptNo > 255) then
        if not cascadeInterrupt then self:Interrupt(13,3,false,true) end
        return
      end

      -- Check if basic logic must be used
      if interruptNo > self.NIDT-1 then
        if interruptNo == 0 then
          self:Reset()
        end
        if interruptNo == 1 then
          self:SignalShutdown()
        end
        return
      end

      -- Calculate absolute offset in the interrupt table
      local interruptOffset = self.IDTR + interruptNo*4

      -- Disable bus lock, set the current page for read operations to succeed
      self.BusLock = 0
      self:SetCurrentPage(math.floor(interruptOffset/128))

      self.IF = 0
      self.INTR = 0
      local prevPCAP = self.PCAP
      self.PCAP = 0 -- Use absolute addressing
      local IP     =                      self:ReadCell(interruptOffset+0)
      local CS     =                      self:ReadCell(interruptOffset+1)
      local NewPTB =                      self:ReadCell(interruptOffset+2)
      local FLAGS  = self:IntegerToBinary(self:ReadCell(interruptOffset+3))
      self.PCAP = prevPCAP
      self.IF = 1
      if self.INTR == 1 then
        if not cascadeInterrupt then self:Interrupt(13,2,false,true) end
        return
      else
        self.INTR = 1
      end

      -- Set previous page to trigger same logic as if CALL-ing from a privilegied page
      self:SetCurrentPage(math.floor(self.XEIP/128))
      self:SetPreviousPage(math.floor(interruptOffset/128))
      self.BusLock = 1

      --Flags:
      --3  [8 ] = CMPR shows if interrupt occured
      --4  [16] = Interrupt does not set CS
      --5  [32] = Interrupt enabled
      --6  [64] = NMI interrupt
      --7  [128] = Replace PTBL with NewPTE (overrides #8)
      --8  [256] = Replace PTBE with NewPTE
      --9  [512] = Push extended registers (R0-R31)

      if isExternal and (FLAGS[6] ~= 1) then
        if not cascadeInterrupt then self:Interrupt(13,4,false,true) end
        return
      end

      if FLAGS[5] == 1 then -- Interrupt enabled
        -- Push extended registers
        self.BusLock = 0
        if FLAGS[9] == 1 then
          for i=31,0,-1 do
            self:Push(self["R"..i])
          end
        end

        -- Push return data
        self.IF = 0
        self.INTR = 0
        self:Push(self.IP)
        self:Push(self.CS)
        self.IF = 1
        if self.INTR == 1 then
          if not cascadeInterrupt then self:Interrupt(13,6,false,true) end
          return
        else
          self.INTR = 1
        end
        --self.BusLock = 1

        -- Perform a short or a long jump
        self.IF = 0
        self.INTR = 0
        if FLAGS[4] == 0
        then self:Jump(IP,CS)
        else self:Jump(IP)
        end
        self.IF = 1
        if self.INTR == 1 then
          if not cascadeInterrupt then self:Interrupt(13,7,false,true) end
          return
        else
          self.INTR = 1
        end

        -- Set CMPR
        if FLAGS[3] == 1 then
          self.CMPR = 1
        end
      else
        if interruptNo == 0 then
          self:Reset()
        end
        if interruptNo == 1 then
          self:SignalShutdown()
        end
        if FLAGS[3] == 1 then
          self.CMPR = 1
        end
      end

      if FLAGS[7] == 1 then
        self.PTBL = NewPTB
      elseif FLAGS[8] == 1 then
        self.PTBE = NewPTB
      end

    elseif self.PF == 1 then -- Compatibility extended mode
      -- Boundary check
      if (interruptNo < 0) or (interruptNo > 255) then
        if not cascadeInterrupt then self:Interrupt(13,3,false,true) end
        return
      end

      -- Memory size check
      if self.RAMSize < 512 then
        if not cascadeInterrupt then self:Interrupt(13,5,false,true) end
        return
      end

      -- Calculate absolute offset in the interrupt table
      local interruptOffset = self.IDTR + interruptNo*2

      if interruptOffset > self.RAMSize-2 then interruptOffset = self.RAMSize-2 end
      if interruptOffset < 0              then interruptOffset = 0 end

      interruptOffset = self.Memory[interruptOffset]
      local interruptFlags = self.Memory[interruptOffset+1]
      if (interruptFlags == 32) or (interruptFlags == 96) then
        self.BusLock = 0
        self.IF = 0
        self.INTR = 0
        if (interruptNo == 4 ) or
           (interruptNo == 7 ) or
           (interruptNo == 9 ) or
           (interruptNo == 10) then
          self:Push(self.LADD)
        end
        if (interruptNo == 4 ) or
           (interruptNo == 31) then
          self:Push(self.LINT)
        end
        if self:Push(self.IP) and self:Push(self.XEIP) then
          self:Jump(interruptOffset)
        end
        self.IF = 1
        if self.INTR == 1 then
          if not cascadeInterrupt then self:Interrupt(13,6,false,true) end
          return
        else
          self.INTR = 1
        end
        self.CMPR = 0
        self.BusLock = 1
      else
        if interruptNo == 1 then
          self:SignalShutdown()
        end
        self.CMPR = 1
      end
    else
      if (interruptNo < 0) or (interruptNo > 255) or (interruptNo > self.NIDT-1) then
        -- Interrupt not handled
        return
      end
      if interruptNo == 0 then
        self:Reset()
        return
      end
      if interruptNo ~= 31 then --Don't die on the debug trap
        self:SignalShutdown()
      end
    end
  end

  -- Unlock the bus
  self.BusLock = 0
end




--------------------------------------------------------------------------------
-- Timer firing checks
function ZVM:TimerLogic()
  if self.TimerMode ~= self.TimerPrevMode then
    if self.TimerMode == 1 then
      self.TimerPrevTime = self.TIMER
    elseif self.TimerMode == 2 then
      self.TimerPrevTime = self.TMR
    end
    self.TimerPrevMode = self.TimerMode
  end

  if self.TimerMode ~= 0 then
    if self.TimerMode == 1 then
      if (self.TIMER - self.TimerPrevTime) >= self.TimerRate then
        self:ExternalInterrupt(math.floor(self.TimerAddress))
        self.TimerPrevTime = self.TIMER
      end
    elseif self.TimerMode == 2 then
      if (self.TMR - self.TimerPrevTime) >= self.TimerRate then
        self:ExternalInterrupt(math.floor(self.TimerAddress))
        self.TimerPrevTime = self.TMR
      end
    end
  end
end




--------------------------------------------------------------------------------
-- Vector reading/writing instructions
function ZVM:ReadVector2f(address)
  if address == 0 then
    return { x = 0, y = 0, z = 0, w = 0 }
  else
    return { x = self:ReadCell(address+0) or 0,
             y = self:ReadCell(address+1) or 0,
             z = 0,
             w = 0 }
  end
end

function ZVM:ReadVector3f(address)
  if address == 0 then
    return { x = 0, y = 0, z = 0, w = 0 }
  else
    return { x = self:ReadCell(address+0) or 0,
             y = self:ReadCell(address+1) or 0,
             z = self:ReadCell(address+2) or 0,
             w = 0 }
  end
end

function ZVM:ReadVector4f(address)
  if address == 0 then
    return { x = 0, y = 0, z = 0, w = 0 }
  else
    return { x = self:ReadCell(address+0) or 0,
             y = self:ReadCell(address+1) or 0,
             z = self:ReadCell(address+2) or 0,
             w = self:ReadCell(address+3) or 0 }
  end
end

function ZVM:ReadMatrix(address)
  local resultMatrix = {}
  for i= 0,15 do resultMatrix[i] = self:ReadCell(address+i) or 0 end
  return resultMatrix
end

function ZVM:WriteVector2f(address,vector)
  self:WriteCell(address+0,vector.x)
  self:WriteCell(address+1,vector.y)
end

function ZVM:WriteVector3f(address,vector)
  self:WriteCell(address+0,vector.x)
  self:WriteCell(address+1,vector.y)
  self:WriteCell(address+2,vector.z)
end

function ZVM:WriteVector4f(address,vector)
  self:WriteCell(address+0,vector.x)
  self:WriteCell(address+1,vector.y)
  self:WriteCell(address+2,vector.z)
  self:WriteCell(address+3,vector.w)
end

function ZVM:WriteMatrix(address,matrix)
  for i=0,15 do self:WriteCell(address+i,matrix[i]) end
end




--------------------------------------------------------------------------------
-- Converts integer to binary representation
function ZVM:IntegerToBinary(n)
  -- Check sign
  n = math.floor(n or 0)
  if n < 0 then
    local bits = self:IntegerToBinary(2^self.IPREC + n)
    bits[self.IPREC-1] = 1
    return bits
  end

  -- Convert to binary
  local bits = {}
  local cnt = 0
  while (n > 0) and (cnt < self.IPREC) do
    local bit = n % 2
    bits[cnt] = bit

    n = (n-bit)/2
    cnt = cnt + 1
  end

  -- Fill in missing zero bits
  while cnt < self.IPREC do
    bits[cnt] = 0
    cnt = cnt + 1
  end

  return bits
end




--------------------------------------------------------------------------------
-- Converts binary representation back to integer
function ZVM:BinaryToInteger(bits)
  local result = 0

  -- Convert to integer
  for i = 0, self.IPREC-2 do
    result = result + (bits[i] or 0) * (2 ^ i)
  end

  -- Add sign
  if bits[self.IPREC-1] == 1 then
    return -2^(self.IPREC-1)+result
  else
    return result
  end
end




--------------------------------------------------------------------------------
-- Binary OR
function ZVM:BinaryOr(m,n)
  local bits_m = self:IntegerToBinary(m)
  local bits_n = self:IntegerToBinary(n)
  local bits = {}

  for i = 0, self.IPREC-1 do
    bits[i] = math.min(1,bits_m[i]+bits_n[i])
  end

  return self:BinaryToInteger(bits)
end




--------------------------------------------------------------------------------
-- Binary AND
function ZVM:BinaryAnd(m,n)
  local bits_m = self:IntegerToBinary(m)
  local bits_n = self:IntegerToBinary(n)
  local bits = {}

  for i = 0, self.IPREC-1 do
    bits[i] = bits_m[i]*bits_n[i]
  end

  return self:BinaryToInteger(bits)
end




--------------------------------------------------------------------------------
-- Binary NOT
function ZVM:BinaryNot(n)
  local bits_n = self:IntegerToBinary(n)
  local bits = {}

  for i = 0, self.IPREC-1 do
    bits[i] = 1-bits_n[i]
  end
  return self:BinaryToInteger(bits)
end




--------------------------------------------------------------------------------
-- Binary XOR
function ZVM:BinaryXor(m,n)
  local bits_m = self:IntegerToBinary(m)
  local bits_n = self:IntegerToBinary(n)
  local bits = {}

  for i = 0, self.IPREC-1 do
    bits[i] = (bits_m[i]+bits_n[i]) % 2
  end

  return self:BinaryToInteger(bits)
end




--------------------------------------------------------------------------------
-- Binary shift right
function ZVM:BinarySHR(n,cnt)
  local bits_n = self:IntegerToBinary(n)
  local bits = {}

  local rslt = #bits_n
  for i = 0, self.IPREC-cnt-1 do
    bits[i] = bits_n[i+cnt]
  end
  for i = self.IPREC-cnt,rslt-1 do
    bits[i] = 0
  end

  return self:BinaryToInteger(bits)
end




--------------------------------------------------------------------------------
-- Binary shift left
function ZVM:BinarySHL(n,cnt)
  local bits_n = self:IntegerToBinary(n)
  local bits = {}

  for i = cnt,self.IPREC-1 do
    bits[i] = bits_n[i-cnt]
  end
  for i = 0,cnt-1 do
    bits[i] = 0
  end

  return self:BinaryToInteger(bits)
end

--------------------------------------------------------------------------------
-- Clamps numbers to within a certain binary range using BAND
-- if IPREC were 4 then 2^4 = 16, or 0b10000
-- subtracting 1 from 16 would make it 0b01111, which is a mask for the first 4 bits
function ZVM:ClampBinaryToIPREC(num)
  local finalvalue = bit.band(num,math.ldexp(1,self.IPREC)-1)
  if self.IPREC < 32 then
    local msb = math.ldexp(1,self.IPREC-1)
    if bit.band(msb,finalvalue) ~= 0 then 
      return finalvalue - msb*2
    end
  end
  return finalvalue
end

--------------------------------------------------------------------------------
-- Performs ldexp with constant 1 significand and exp floored automatically.
-- to get a value with only bit [num] enabled
-- required because ldexp normally errors when a non-integer is passed as exp
function ZVM:GetBit(num)
  return math.ldexp(1,math.floor(num))
end

--------------------------------------------------------------------------------
-- Reset to initial state
ZVM:Reset()
