From 7adeecf7a871fe8c97fb59a8e817c53d142f9751 Mon Sep 17 00:00:00 2001 From: DerelictDrone <57756830+DerelictDrone@users.noreply.github.com> Date: Thu, 5 Sep 2024 23:01:01 -0500 Subject: [PATCH] Privileged instructions can now be proxied by interrupt 13 (#66) * Privileged instructions can now be proxied by int13 * Fix regular runlevel zero --- lua/entities/gmod_wire_gpu/cl_gpuvm.lua | 6 +++ lua/entities/gmod_wire_spu/cl_spuvm.lua | 6 +++ lua/wire/cpulib.lua | 13 +++-- lua/wire/zvm/zvm_core.lua | 40 ++++++++++++++-- lua/wire/zvm/zvm_data.lua | 5 ++ lua/wire/zvm/zvm_features.lua | 6 +++ lua/wire/zvm/zvm_opcodes.lua | 63 +++++++++++++++++++++---- 7 files changed, 121 insertions(+), 18 deletions(-) diff --git a/lua/entities/gmod_wire_gpu/cl_gpuvm.lua b/lua/entities/gmod_wire_gpu/cl_gpuvm.lua index a672fba..fda58bc 100644 --- a/lua/entities/gmod_wire_gpu/cl_gpuvm.lua +++ b/lua/entities/gmod_wire_gpu/cl_gpuvm.lua @@ -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) diff --git a/lua/entities/gmod_wire_spu/cl_spuvm.lua b/lua/entities/gmod_wire_spu/cl_spuvm.lua index 576c157..01994a9 100644 --- a/lua/entities/gmod_wire_spu/cl_spuvm.lua +++ b/lua/entities/gmod_wire_spu/cl_spuvm.lua @@ -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 diff --git a/lua/wire/cpulib.lua b/lua/wire/cpulib.lua index 322fe50..ff1c31c 100644 --- a/lua/wire/cpulib.lua +++ b/lua/wire/cpulib.lua @@ -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 @@ -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) @@ -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 @@ -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 @@ -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") @@ -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") @@ -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") diff --git a/lua/wire/zvm/zvm_core.lua b/lua/wire/zvm/zvm_core.lua index eab4d0a..0bbdca0 100644 --- a/lua/wire/zvm/zvm_core.lua +++ b/lua/wire/zvm/zvm_core.lua @@ -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 @@ -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 @@ -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 diff --git a/lua/wire/zvm/zvm_data.lua b/lua/wire/zvm/zvm_data.lua index 36a7171..2e96628 100644 --- a/lua/wire/zvm/zvm_data.lua +++ b/lua/wire/zvm/zvm_data.lua @@ -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 diff --git a/lua/wire/zvm/zvm_features.lua b/lua/wire/zvm/zvm_features.lua index b23da43..a18a4af 100644 --- a/lua/wire/zvm/zvm_features.lua +++ b/lua/wire/zvm/zvm_features.lua @@ -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 diff --git a/lua/wire/zvm/zvm_opcodes.lua b/lua/wire/zvm/zvm_opcodes.lua index 79a8754..feea9ff 100644 --- a/lua/wire/zvm/zvm_opcodes.lua +++ b/lua/wire/zvm/zvm_opcodes.lua @@ -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)") @@ -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") @@ -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")