Skip to content

Commit

Permalink
CPULib Extension framework (#45)
Browse files Browse the repository at this point in the history
* basic extension support

* GPU extensions functioning

* Negative opcodes can take operands now

No segments yet

* Extension opcodes can take operands now + SPU extensions

* Extensions persist through duping

* Clean up debugging

* Disables example extensions by default

* Update zvm_core.lua

Opcode needs to have its modifiers stripped by code, can't keep a copy of the original

* Lua instructions, easier extension creation
  • Loading branch information
DerelictDrone authored Jan 25, 2024
1 parent 2ca85dc commit beb9fe3
Show file tree
Hide file tree
Showing 20 changed files with 666 additions and 44 deletions.
3 changes: 3 additions & 0 deletions lua/autorun/cpu_load.lua
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ end
AddCSLuaFile("wire/cpulib.lua")
include("wire/cpulib.lua")

-- AddCSLuaFile("wire/cpulib_example_extension.lua")
-- include("wire/cpulib_example_extension.lua")

AddCSLuaFile("wire/gpulib.lua")
include("wire/gpulib.lua")

Expand Down
15 changes: 15 additions & 0 deletions lua/entities/gmod_wire_cpu.lua
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ function ENT:Initialize()
self.VM.MemoryWriteCycles = 2
self.VM.ExternalReadCycles = 8
self.VM.ExternalWriteCycles = 8
if self.ZVMExtensions then
self.VM.Extensions = CPULib:FromExtensionString(self.ZVMExtensions,"CPU")
CPULib:LoadExtensions(self.VM,"CPU")
end
self.VM:Reset()

self:SetCPUName()
Expand Down Expand Up @@ -140,6 +144,14 @@ function ENT:SetMemoryModel(model)
self.VM.ROMSize = memoryModels[model][2] or 65536
end

function ENT:SetExtensionLoadOrder(extstr)
self.ZVMExtensions = extstr
if self.VM then
self.VM.Extensions = CPULib:FromExtensionString(self.ZVMExtensions,"CPU")
CPULib:LoadExtensions(self.VM,"CPU")
end
end

-- Execute ZCPU virtual machine
function ENT:Run()
-- Do not run if debugging is active
Expand Down Expand Up @@ -248,6 +260,7 @@ function ENT:BuildDupeInfo()
info.InternalRAMSize = self.VM.RAMSize
info.InternalROMSize = self.VM.ROMSize
info.CPUName = self.CPUName
info.ZVMExtensions = self.ZVMExtensions

if self.VM.ROMSize > 0 then
info.Memory = {}
Expand All @@ -264,6 +277,8 @@ function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID)
self.VM.RAMSize = info.InternalRAMSize or 65536
self.VM.ROMSize = info.InternalROMSize or 65536
self:SetCPUName(info.CPUName)
self:SetExtensionLoadOrder(info.ZVMExtensions)


if info.Memory then--and
--(((info.UseROM) and (info.UseROM == true)) or
Expand Down
5 changes: 5 additions & 0 deletions lua/entities/gmod_wire_gpu/cl_gpuvm.lua
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,11 @@ function ENT:OverrideVM()
self.VM.OperandCount[131] = nil --SMAP
self.VM.OperandCount[132] = nil --GMAP

if self.ZVMExtensions then
self.VM.Extensions = CPULib:FromExtensionString(self.ZVMExtensions,"GPU")
CPULib:LoadExtensions(self.VM,"GPU")
end

-- Add some extra lookups
self.VM.FontName = {}
self.VM.FontName[0] = "Lucida Console"
Expand Down
16 changes: 15 additions & 1 deletion lua/entities/gmod_wire_gpu/cl_init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,20 @@ local function GPU_MemoryModel(um)
end
usermessage.Hook("wire_gpu_memorymodel", GPU_MemoryModel)

local function GPU_SetExtensions(um)
local GPU = ents.GetByIndex(um:ReadLong())
if not GPU then return end
if not GPU:IsValid() then return end
local extstr = um:ReadString()
local extensions = CPULib:FromExtensionString(extstr,"GPU")
if GPU.VM then
GPU.VM.Extensions = extensions
CPULib:LoadExtensions(GPU.VM,"GPU")
end
GPU.ZVMExtensions = extstr
end
usermessage.Hook("wire_gpu_extensions", GPU_SetExtensions)

local wire_gpu_frameratio = CreateClientConVar("wire_gpu_frameratio",4)

function ENT:Initialize()
Expand All @@ -75,6 +89,7 @@ function ENT:Initialize()
self.VM.CPUVER = 1.0 -- Beta GPU by default
self.VM.CPUTYPE = 1 -- ZGPU
self.ChipType = 0
self.VM.Extensions = CPULib:FromExtensionString(self.ZVMExtensions,"GPU")

-- Hard-reset VM and override it
self:OverrideVM()
Expand Down Expand Up @@ -187,7 +202,6 @@ function ENT:Run(isAsync)
else
self.VM.SyncQuotaOverrun = self.VM.QuotaOverrunFunc
if self.VM.SyncQuotaOverrun then
print(self.VM.LateFrames)
self.VM.SyncQuotaIP = self.VM.IP
self.VM.LateFrames = self.VM.LateFrames + 1
else
Expand Down
14 changes: 14 additions & 0 deletions lua/entities/gmod_wire_gpu/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,18 @@ function ENT:SetMemoryModel(model,initial)
end
end

function ENT:SetExtensionLoadOrder(extstr)
self.ZVMExtensions = extstr
timer.Simple(0.1+math.random()*0.3,
function()
if not self:IsValid() then return end

umsg.Start("wire_gpu_extensions")
umsg.Long(self:EntIndex())
umsg.String(self.ZVMExtensions)
umsg.End()
end)
end

--------------------------------------------------------------------------------
-- Resend all GPU cache to newly spawned player
Expand Down Expand Up @@ -180,6 +192,7 @@ function ENT:BuildDupeInfo()
info.RAMSize = self.RAMSize
info.ChipType = self.ChipType
info.Memory = {}
info.ZVMExtensions = self.ZVMExtensions

for address = 0,self.RAMSize-1 do
if self.Memory[address] and (self.Memory[address] ~= 0) then info.Memory[address] = self.Memory[address] end
Expand All @@ -199,6 +212,7 @@ function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID)
self.RAMSize = math.Clamp(info.RAMSize or 65536, 0, 2097152)
self.ChipType = info.ChipType or 0
self.Memory = {}
self:SetExtensionLoadOrder(info.ZVMExtensions)

for address = 0,self.RAMSize-1 do
if info.Memory[address] then self.Memory[address] = tonumber(info.Memory[address]) or 0 end
Expand Down
14 changes: 14 additions & 0 deletions lua/entities/gmod_wire_spu/cl_init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,19 @@ local function SPU_MemoryModel(um)
end
usermessage.Hook("wire_spu_memorymodel", SPU_MemoryModel)

local function SPU_SetExtensions(um)
local SPU = ents.GetByIndex(um:ReadLong())
if not SPU then return end
if not SPU:IsValid() then return end
local extstr = um:ReadString()
local extensions = CPULib:FromExtensionString(extstr,"SPU")
if SPU.VM then
SPU.VM.Extensions = extensions
CPULib:LoadExtensions(SPU.VM,"SPU")
end
SPU.ZVMExtensions = extstr
end
usermessage.Hook("wire_spu_extensions", SPU_SetExtensions)



Expand All @@ -105,6 +118,7 @@ function ENT:Initialize()
self.VM.CPUVER = 1.0 -- Beta SPU by default
self.VM.CPUTYPE = 2 -- ZSPU
self.ChipType = 0
self.VM.Extensions = CPULib:FromExtensionString(self.ZVMExtensions,"SPU")

-- Create fake sound sources
self.SoundSources = {}
Expand Down
15 changes: 15 additions & 0 deletions lua/entities/gmod_wire_spu/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,18 @@ function ENT:SetMemoryModel(model,initial)
end
end

function ENT:SetExtensionLoadOrder(extstr)
self.ZVMExtensions = extstr
timer.Simple(0.1+math.random()*0.3,
function()
if not self:IsValid() then return end

umsg.Start("wire_spu_extensions")
umsg.Long(self:EntIndex())
umsg.String(self.ZVMExtensions)
umsg.End()
end)
end

--------------------------------------------------------------------------------
-- Resend all SPU cache to newly spawned player
Expand Down Expand Up @@ -184,6 +196,8 @@ function ENT:BuildDupeInfo()
info.RAMSize = self.RAMSize
info.ChipType = self.ChipType
info.Memory = {}
info.ZVMExtensions = self.ZVMExtensions
self:SetExtensionLoadOrder(self.ZVMExtensions)

for address = 0,self.RAMSize-1 do
if self.Memory[address] and (self.Memory[address] ~= 0) then info.Memory[address] = self.Memory[address] end
Expand All @@ -203,6 +217,7 @@ function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID)
self.RAMSize = math.Clamp(info.RAMSize or 65536, 0, 2097152)
self.ChipType = info.ChipType or 0
self.Memory = {}
self.ZVMExtensions = info.ZVMExtensions

for address = 0,self.RAMSize-1 do
if info.Memory[address] then self.Memory[address] = tonumber(info.Memory[address]) or 0 end
Expand Down
7 changes: 6 additions & 1 deletion lua/wire/client/hlzasm/hc_compiler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,12 @@ end

--------------------------------------------------------------------------------
-- Emit a code byte to the output stream
function HCOMP:WriteByte(byte,block)
function HCOMP:WriteByte(byte,block,negate)
-- hack to allow normal opcode calcs to work on negative opcodes
if negate then
byte = byte * -1
end

if self.WriteByteCallback then
self.WriteByteCallback(self.WriteByteCaller,self.WritePointer,byte)
end
Expand Down
71 changes: 58 additions & 13 deletions lua/wire/client/hlzasm/hc_opcodes.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,79 @@
--------------------------------------------------------------------------------
-- Initialize opcode count lookup table
HCOMP.OperandCount = {}
for _,instruction in pairs(CPULib.InstructionTable) do
HCOMP.OperandCount[instruction.Opcode] = instruction.OperandCount

local function buildMainLookup(instructions)
for _,instruction in pairs(instructions) do
HCOMP.OperandCount[instruction.Opcode] = instruction.OperandCount
end
end

-- Initialize table of single-operand instructions which write 1st operand
HCOMP.OpcodeWritesOperand = {}
for _,instruction in pairs(CPULib.InstructionTable) do
if instruction.WritesFirstOperand and (instruction.Mnemonic ~= "RESERVED") then
HCOMP.OpcodeWritesOperand[string.lower(instruction.Mnemonic)] = true

local function buildWritesFirstLookup(instructions)
for _,instruction in pairs(instructions) do
if instruction.WritesFirstOperand and (instruction.Mnemonic ~= "RESERVED") then
HCOMP.OpcodeWritesOperand[string.lower(instruction.Mnemonic)] = true
end
end
end

-- Initialize opcode number lookup table
HCOMP.OpcodeNumber = {}
for _,instruction in pairs(CPULib.InstructionTable) do
if instruction.Mnemonic ~= "RESERVED" then
HCOMP.OpcodeNumber[string.lower(instruction.Mnemonic)] = instruction.Opcode

local function buildOpLookupTable(instructions)
for _,instruction in pairs(instructions) do
if instruction.Mnemonic ~= "RESERVED" then
HCOMP.OpcodeNumber[string.lower(instruction.Mnemonic)] = instruction.Opcode
end
end
end

-- Initialize list of obsolete/old opcodes
HCOMP.OpcodeObsolete = {}
HCOMP.OpcodeOld = {}
for _,instruction in pairs(CPULib.InstructionTable) do
if instruction.Obsolete and (instruction.Mnemonic ~= "RESERVED") then
HCOMP.OpcodeObsolete[string.lower(instruction.Mnemonic)] = true

local function buildDeprecatedLookupTable(instructions)
for _,instruction in pairs(instructions) do
if instruction.Obsolete and (instruction.Mnemonic ~= "RESERVED") then
HCOMP.OpcodeObsolete[string.lower(instruction.Mnemonic)] = true
end
if instruction.Old and (instruction.Mnemonic ~= "RESERVED") then
HCOMP.OpcodeOld[string.lower(instruction.Mnemonic)] = string.lower(instruction.Reference)
end
end
end

buildMainLookup(CPULib.InstructionTable)
buildWritesFirstLookup(CPULib.InstructionTable)
buildOpLookupTable(CPULib.InstructionTable)
buildDeprecatedLookupTable(CPULib.InstructionTable)

local function RemoveInstructions(indexes)
for _, inst in ipairs(indexes) do
local instName = string.lower(CPULib.InstructionTable[inst].Mnemonic)
HCOMP.OperandCount[CPULib.InstructionTable[inst].Opcode] = nil
HCOMP.OpcodeWritesOperand[instName] = nil
HCOMP.OpcodeNumber[instName] = nil
HCOMP.OpcodeOld[instName] = nil
HCOMP.OpcodeObsolete[instName] = nil
end
if instruction.Old and (instruction.Mnemonic ~= "RESERVED") then
HCOMP.OpcodeOld[string.lower(instruction.Mnemonic)] = string.lower(instruction.Reference)
HCOMP:RemoveTokenizerOpcodes(indexes)
end

local function CreateInstructions(indexes)
-- build a small table mirroring instructiontable to reuse the above functions
local newInstructions = {}
for _,inst in ipairs(indexes) do
table.insert(newInstructions,CPULib.InstructionTable[inst])
end
buildMainLookup(newInstructions)
buildWritesFirstLookup(newInstructions)
buildOpLookupTable(newInstructions)
buildDeprecatedLookupTable(newInstructions)
HCOMP:RegenerateTokenizerOpcodes()
end

table.insert(CPULib.RemoveInstructionHooks,RemoveInstructions)
table.insert(CPULib.CreateInstructionHooks,CreateInstructions)
9 changes: 6 additions & 3 deletions lua/wire/client/hlzasm/hc_output.lua
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,10 @@ function HCOMP:WriteBlock(block)
return
end
local Opcode,RM = self.OpcodeNumber[block.Opcode],nil

local negativeOp = Opcode and Opcode < 0
if negativeOp then
Opcode = Opcode*-1
end
-- Generate RM if more than 1 operand
if #block.Operands > 0 then
RM = self:OperandRM(block.Operands[1],block)
Expand All @@ -617,7 +620,7 @@ function HCOMP:WriteBlock(block)

if not self.Settings.FixedSizeOutput then -- Variable-size instructions
-- Write opcode
self:WriteByte(Opcode,block)
self:WriteByte(Opcode,block,negativeOp)
-- Write RM
if RM then self:WriteByte(RM,block) end

Expand All @@ -630,7 +633,7 @@ function HCOMP:WriteBlock(block)
if (#block.Operands > 1) and (block.Operands[2].Value) then self:WriteByte(block.Operands[2].Value,block) end
else -- Fixed-size instructions
-- Write opcode
self:WriteByte(Opcode + 2000,block)
self:WriteByte(Opcode + 2000,block,negativeOp)

-- Write RM
self:WriteByte(RM or 0,block)
Expand Down
1 change: 0 additions & 1 deletion lua/wire/client/hlzasm/hc_syntax.lua
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ function HCOMP:Opcode() local TOKEN,TOKENSET = self.TOKEN,self.TOKENSET
local opcodeName = self.TokenData
local opcodeNo = self.OpcodeNumber[self.TokenData]
local operandCount = self.OperandCount[opcodeNo]

-- Check if opcode is obsolete or old
if self.OpcodeObsolete[opcodeName] then
self:Warning("Instruction \""..opcodeName.."\" is obsolete")
Expand Down
23 changes: 18 additions & 5 deletions lua/wire/client/hlzasm/hc_tokenizer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -212,16 +212,29 @@ for symID,symList in pairs(HCOMP.TOKEN_TEXT) do
end




function HCOMP:RemoveTokenizerOpcodes(indexes)
-- Remove opcodes from the lookup table
for _,languageName in pairs(self.TOKEN_TEXT["OPCODE"][1]) do
self.PARSER_LOOKUP[languageName] = self.PARSER_LOOKUP[languageName] or {}
for _,index in ipairs(indexes) do
self.PARSER_LOOKUP[languageName][string.upper(CPULib.InstructionTable[index].Mnemonic)] = nil
end
end
end

function HCOMP:RegenerateTokenizerOpcodes()
-- Add opcodes to the lookup table
for _,languageName in pairs(HCOMP.TOKEN_TEXT["OPCODE"][1]) do
HCOMP.PARSER_LOOKUP[languageName] = HCOMP.PARSER_LOOKUP[languageName] or {}
for opcodeName,opcodeNo in pairs(HCOMP.OpcodeNumber) do
HCOMP.PARSER_LOOKUP[languageName][string.upper(opcodeName)] = { opcodeName, HCOMP.TOKEN.OPCODE }
HCOMP.PARSER_LOOKUP[languageName] = HCOMP.PARSER_LOOKUP[languageName] or {}
for opcodeName,opcodeNo in pairs(HCOMP.OpcodeNumber) do
HCOMP.PARSER_LOOKUP[languageName][string.upper(opcodeName)] = { opcodeName, HCOMP.TOKEN.OPCODE }
end
end
end



HCOMP:RegenerateTokenizerOpcodes()

--------------------------------------------------------------------------------
-- Skip a single file in input
Expand Down
Loading

0 comments on commit beb9fe3

Please sign in to comment.