Skip to content

Commit

Permalink
Privileged instructions can now be proxied by interrupt 13 (#66)
Browse files Browse the repository at this point in the history
* Privileged instructions can now be proxied by int13

* Fix regular runlevel zero
  • Loading branch information
DerelictDrone authored Sep 6, 2024
1 parent 5efb2a5 commit 7adeecf
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 18 deletions.
6 changes: 6 additions & 0 deletions lua/entities/gmod_wire_gpu/cl_gpuvm.lua
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,12 @@ function VM:Reset()
self.INTR = 0 -- Handling an interrupt
self.BlockStart = 0 -- Start of the block
self.BlockSize = 0 -- Size of the block
self.QUOTIMER = 0
self.QUOCMP = 0
self.PreqOperand1 = 0
self.PreqOperand2 = 0
self.PreqHandled = 0
self.PreqReturn = 0

-- Reset internal GPU registers
-- [131072]..[2097151] - Extended GPU memory (2MB GPU)
Expand Down
6 changes: 6 additions & 0 deletions lua/entities/gmod_wire_spu/cl_spuvm.lua
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,12 @@ function VM:Reset()
self.INTR = 0 -- Handling an interrupt
self.BlockStart = 0 -- Start of the block
self.BlockSize = 0 -- Size of the block
self.QUOTIMER = 0
self.QUOCMP = 0
self.PreqOperand1 = 0
self.PreqOperand2 = 0
self.PreqHandled = 0
self.PreqReturn = 0

self.EntryPoint0 = 0
self.EntryPoint1 = 0
Expand Down
13 changes: 8 additions & 5 deletions lua/wire/cpulib.lua
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,7 @@ end
-- TR: trigonometric syntax operand
-- OL: old mnemonic for the instruction
-- BL: instruction supports block prefix
-- PR: privileged opcode but with custom handler(for example, if it needs to get a value to write to an operand)
-- Op1 - operand 1 name
-- Op2 - operand 2 name

Expand All @@ -824,7 +825,7 @@ end
-- INT: 48-bit signed integer

CPULib.InstructionTable = {}
local W1,R0,OB,UB,CB,TR,OL,BL = 1,2,4,8,16,32,64,128
local W1,R0,OB,UB,CB,TR,OL,BL,PR = 1,2,4,8,16,32,64,128,256

local function Bit(x,n) return (math.floor(x / n) % 2) == 1 end
local function Entry(Set,Opc,Mnemonic,Ops,Version,Flags,Op1,Op2,Reference)
Expand All @@ -846,6 +847,7 @@ local function Entry(Set,Opc,Mnemonic,Ops,Version,Flags,Op1,Op2,Reference)
Trigonometric = Bit(Flags,TR),
Old = Bit(Flags,OL),
BlockPrefix = Bit(Flags,BL),
PrivilegedRequester = Bit(Flags,PR)
})
end
local function CPU(...) Entry("CPU",...) end
Expand Down Expand Up @@ -880,7 +882,8 @@ CPULib.FlagLookup = {
["CB"] = CB,
["TR"] = TR,
["OL"] = OL,
["BL"] = BL
["BL"] = BL,
["PR"] = PR
}

-- Parses an array of flags into a single number from a lookup table by name
Expand Down Expand Up @@ -1099,7 +1102,7 @@ CPU(012, "MUL", 2, 1.00, 0, "X", "Y", "X = X * Y"
CPU(013, "DIV", 2, 1.00, 0, "X", "Y", "X = X / Y")
CPU(014, "MOV", 2, 1.00, 0, "X", "Y", "X = Y")
CPU(015, "CMP", 2, 1.00, 0, "X", "Y", "Compare X and Y. Use with conditional branching instructions")
CPU(016, "RD", 2, 1.00, R0+OB, "X", "PTR", "Read value from memory by pointer PTR")
CPU(016, "RD", 2, 1.00, PR+OB, "X", "PTR", "Read value from memory by pointer PTR")
CPU(017, "WD", 2, 1.00, R0+OB, "PTR", "Y", "Write value to memory by pointer PTR")
CPU(018, "MIN", 2, 1.00, 0, "X", "Y", "Set X to smaller value out of X and Y")
CPU(019, "MAX", 2, 1.00, 0, "X", "Y", "Set X to bigger value out of X and Y")
Expand Down Expand Up @@ -1235,7 +1238,7 @@ CPU(117, "LEAVE", 0, 10.00, 0, "", "", "Leave subr
CPU(118, "STM", 0, 10.00, R0, "", "", "Enable extended memory mode")
CPU(119, "CLM", 0, 10.00, R0, "", "", "Disable extended memory mode")
---- Dec 12 -------------------------------------------------------------------------------------------------------------------------------------
CPU(120, "CPUGET", 2, 5.00, R0, "X", "IDX", "Read internal processor register IDX")
CPU(120, "CPUGET", 2, 5.00, PR, "X", "IDX", "Read internal processor register IDX")
CPU(121, "CPUSET", 2, 5.00, R0, "IDX", "Y", "Write internal processor register IDX")
CPU(122, "SPP", 2, 5.00, R0+BL, "PAGE", "IDX", "Set page flag IDX")
CPU(123, "CPP", 2, 5.00, R0+BL, "PAGE", "IDX", "Clear page flag IDX")
Expand All @@ -1248,7 +1251,7 @@ CPU(129, "CMPOR", 2, 6.00, 0, "X", "Y", "Compare X
---- Dec 13 -------------------------------------------------------------------------------------------------------------------------------------
CPU(130, "MSHIFT", 2, 7.00, 0, "COUNT", "OFFSET","Shift (and rotate) data pointed by ESI by OFFSET bytes")
CPU(131, "SMAP", 2, 8.00, R0+BL, "PAGE1", "PAGE2", "Remap PAGE1 to physical page PAGE2")
CPU(132, "GMAP", 2, 8.00, R0, "X", "PAGE", "Read what physical page PAGE is mapped to")
CPU(132, "GMAP", 2, 8.00, PR, "X", "PAGE", "Read what physical page PAGE is mapped to")
CPU(133, "RSTACK", 2, 9.00, 0, "X", "IDX", "Read value from stack at offset IDX (from address SS+IDX)")
CPU(134, "SSTACK", 2, 9.00, 0, "IDX", "Y", "Write value to stack at offset IDX (to address SS+IDX)")
CPU(135, "ENTER", 1, 10.00, 0, "SIZE", "", "Enter stack frame and allocate SIZE bytes on stack for local variables")
Expand Down
40 changes: 37 additions & 3 deletions lua/wire/zvm/zvm_core.lua
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,31 @@ function ZVM:Dyn_EndQuotaInterrupt()
self:Dyn_Emit("end")
end

-- Allows you to set up code that runs if greater than this runlevel
function ZVM:Dyn_BeginUnprivilegedCode(Runlevel)
self:Emit("if (VM.PCAP == 1) and (VM.CurrentPage.RunLevel > %d) then",Runlevel)
end

-- For readability to signify the end of an unprivileged block.
function ZVM:Dyn_EndUnprivilegedCode()
self:Emit("end")
end

-- Set PreqOperands and an interrupt to return to just before this instruction, so it can be handled like a MEMRQ
function ZVM:Dyn_EmitUnprivilegedRequestInterrupt(Opcode)
self:Dyn_Emit("VM.PreqOperand1 = $1 or 0")
self:Dyn_Emit("VM.PreqOperand2 = $2 or 0")
self:Dyn_Emit("VM.PreqReturn = 0")
-- Default PreqHandled to -1 (meaning unhandled, don't take return value, just skip the instruction)
self:Dyn_Emit("VM.PreqHandled = -1")
-- Return to just before instruction, to allow the instruction to get the return value if handled
self:Dyn_EmitState()
self:Dyn_Emit("VM.IP = %d",self.PrecompileIP-self.PrecompileCurInstructionSize)
self:Dyn_Emit("VM.XEIP = %d",self.PrecompileXEIP-self.PrecompileCurInstructionSize)
self:Dyn_Emit("VM:Interrupt(13,%d)",Opcode)
self:Dyn_EmitBreak()
end

--------------------------------------------------------------------------------
function ZVM:Precompile_Initialize()
self.PrecompileXEIP = self.XEIP
Expand Down Expand Up @@ -515,9 +540,14 @@ function ZVM:Precompile_Step()

-- Check opcode runlevel
if self.OpcodeRunLevel[Opcode] then
self:Emit("if (VM.PCAP == 1) and (VM.CurrentPage.RunLevel > %d) then",self.OpcodeRunLevel[Opcode])
self:Dyn_EmitInterrupt("13",Opcode)
self:Emit("end")
self:Dyn_BeginUnprivilegedCode(self.OpcodeRunLevel[Opcode])
self:Dyn_Emit("if VM.PreqHandled == 0 then")
self:Dyn_EmitUnprivilegedRequestInterrupt(Opcode)
self:Dyn_Emit("end")
-- Skip running the privileged code if this was deemed "handled"
self:Dyn_Emit("VM.PreqHandled = 0")
self:Dyn_Emit("else")
-- Privileged code will get wrapped in this block
end

-- Calculate operand RM bytes
Expand Down Expand Up @@ -632,6 +662,10 @@ function ZVM:Precompile_Step()
if self.EmitNeedInterruptCheck then
self:Dyn_EmitInterruptCheck()
end
if self.OpcodeRunLevel[Opcode] then
-- Wrap the privileged block up here.
self:Dyn_EndUnprivilegedCode()
end
end

-- Do not repeat if opcode breaks the stream
Expand Down
5 changes: 5 additions & 0 deletions lua/wire/zvm/zvm_data.lua
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,13 @@ ZVM.InternalRegister[65] = "TimerRate"
ZVM.InternalRegister[66] = "TimerPrevTime"
ZVM.InternalRegister[67] = "TimerAddress"
ZVM.InternalRegister[68] = "TimerPrevMode"
----------------------------------
ZVM.InternalRegister[69] = "LASTQUO"
ZVM.InternalRegister[70] = "QUOFLAG"
ZVM.InternalRegister[71] = "PreqOperand1"
ZVM.InternalRegister[72] = "PreqOperand2"
ZVM.InternalRegister[73] = "PreqReturn"
ZVM.InternalRegister[74] = "PreqHandled"
----------------------------------
for reg=0,31 do ZVM.InternalRegister[96+reg] = "R"..reg end

Expand Down
6 changes: 6 additions & 0 deletions lua/wire/zvm/zvm_features.lua
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@ function ZVM:Reset()
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
Expand Down
63 changes: 53 additions & 10 deletions lua/wire/zvm/zvm_opcodes.lua
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,23 @@ end
ZVM.OpcodeTable[16] = function(self) --RD
self:Dyn_Emit("$L OP,ANS = $2,0")
self:Dyn_EmitOperand("ANS")
self:Dyn_Emit("if VM.Memory[OP] then")
self:Dyn_Emit("ANS = VM.Memory[OP]")
self:Dyn_Emit("end")
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)")
Expand Down Expand Up @@ -995,9 +1009,23 @@ ZVM.OpcodeTable[120] = function(self) --CPUGET
self:Dyn_Emit("$L OP = 0")
self:Dyn_EmitState()
self:Dyn_EmitOperand("OP")
self:Dyn_Emit("if VM.InternalRegister[REG] then")
self:Dyn_Emit("OP = VM[VM.InternalRegister[REG]]")
self:Dyn_Emit("end")
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")
Expand Down Expand Up @@ -1215,10 +1243,25 @@ ZVM.OpcodeTable[131] = function(self) --SMAP
self:Dyn_Emit("end")
end
ZVM.OpcodeTable[132] = function(self) --GMAP
self:Dyn_Emit("$L IDX = math.floor(ADDR / 128)")
self:Dyn_Emit("$L PAGE = VM:GetPageByIndex(IDX)")
self:Dyn_EmitInterruptCheck()
self:Dyn_EmitOperand("PAGE.MappedIndex")
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")
Expand Down

0 comments on commit 7adeecf

Please sign in to comment.