From 5c96feccafb205cdd31f0b09e3ef149022022129 Mon Sep 17 00:00:00 2001 From: Astralcircle <142503363+Astralcircle@users.noreply.github.com> Date: Fri, 13 Feb 2026 18:19:30 +0300 Subject: [PATCH 1/3] Merge some zcpu stuff with wire --- lua/autorun/cpu_load.lua | 38 --------------- lua/autorun/wire_load.lua | 21 +++++++++ lua/wire/cpu_default_data_decompressor.lua | 54 ---------------------- lua/wire/default_data_generator.lua | 5 +- 4 files changed, 25 insertions(+), 93 deletions(-) delete mode 100644 lua/autorun/cpu_load.lua delete mode 100644 lua/wire/cpu_default_data_decompressor.lua diff --git a/lua/autorun/cpu_load.lua b/lua/autorun/cpu_load.lua deleted file mode 100644 index d3cadd1b66..0000000000 --- a/lua/autorun/cpu_load.lua +++ /dev/null @@ -1,38 +0,0 @@ -AddCSLuaFile() - --- HL-ZASM -AddCSLuaFile("wire/client/hlzasm/hc_compiler.lua") -AddCSLuaFile("wire/client/hlzasm/hc_opcodes.lua") -AddCSLuaFile("wire/client/hlzasm/hc_expression.lua") -AddCSLuaFile("wire/client/hlzasm/hc_preprocess.lua") -AddCSLuaFile("wire/client/hlzasm/hc_syntax.lua") -AddCSLuaFile("wire/client/hlzasm/hc_codetree.lua") -AddCSLuaFile("wire/client/hlzasm/hc_optimize.lua") -AddCSLuaFile("wire/client/hlzasm/hc_output.lua") -AddCSLuaFile("wire/client/hlzasm/hc_tokenizer.lua") - --- ZVM -AddCSLuaFile("wire/zvm/zvm_core.lua") -AddCSLuaFile("wire/zvm/zvm_features.lua") -AddCSLuaFile("wire/zvm/zvm_opcodes.lua") -AddCSLuaFile("wire/zvm/zvm_data.lua") - -if SERVER then - include("wire/zvm/zvm_tests.lua") -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") - -AddCSLuaFile("wire/cpu_default_data_decompressor.lua") -include("wire/cpu_default_data_decompressor.lua") - -if CLIENT then - include("wire/client/hlzasm/hc_compiler.lua") -end \ No newline at end of file diff --git a/lua/autorun/wire_load.lua b/lua/autorun/wire_load.lua index 6cf4e42a25..404705a750 100644 --- a/lua/autorun/wire_load.lua +++ b/lua/autorun/wire_load.lua @@ -33,6 +33,7 @@ if SERVER then AddCSLuaFile("wire/fpgagates.lua") AddCSLuaFile("wire/cpugates.lua") AddCSLuaFile("wire/wiremonitors.lua") + AddCSLuaFile("wire/cpulib.lua") AddCSLuaFile("wire/gpulib.lua") AddCSLuaFile("wire/timedpairs.lua") AddCSLuaFile("wire/default_data_generator.lua") @@ -69,6 +70,23 @@ if SERVER then AddCSLuaFile("wire/client/node_editor/nodeeditor.lua") AddCSLuaFile("wire/client/node_editor/wire_fpga_editor.lua") + -- hl-zasm + AddCSLuaFile("wire/client/hlzasm/hc_compiler.lua") + AddCSLuaFile("wire/client/hlzasm/hc_opcodes.lua") + AddCSLuaFile("wire/client/hlzasm/hc_expression.lua") + AddCSLuaFile("wire/client/hlzasm/hc_preprocess.lua") + AddCSLuaFile("wire/client/hlzasm/hc_syntax.lua") + AddCSLuaFile("wire/client/hlzasm/hc_codetree.lua") + AddCSLuaFile("wire/client/hlzasm/hc_optimize.lua") + AddCSLuaFile("wire/client/hlzasm/hc_output.lua") + AddCSLuaFile("wire/client/hlzasm/hc_tokenizer.lua") + + -- zvm + AddCSLuaFile("wire/zvm/zvm_core.lua") + AddCSLuaFile("wire/zvm/zvm_features.lua") + AddCSLuaFile("wire/zvm/zvm_opcodes.lua") + AddCSLuaFile("wire/zvm/zvm_data.lua") + for _, filename in ipairs(file.Find("wire/client/text_editor/modes/*.lua","LUA")) do AddCSLuaFile("wire/client/text_editor/modes/" .. filename) end @@ -83,6 +101,7 @@ include("wire/wiregates.lua") include("wire/fpgagates.lua") include("wire/cpugates.lua") include("wire/wiremonitors.lua") +include("wire/cpulib.lua") include("wire/gpulib.lua") include("wire/timedpairs.lua") include("wire/default_data_generator.lua") @@ -95,6 +114,7 @@ if SERVER then include("wire/server/debuggerlib.lua") include("wire/server/sents_registry.lua") include("wire/server/wire_map_interface.lua") + include("wire/zvm/zvm_tests.lua") if CreateConVar("wire_force_workshop", "1", FCVAR_ARCHIVE, "Should Wire force all clients to download the Workshop edition of Wire, for models? (requires restart to disable)"):GetBool() then if select(2, WireLib.GetVersion()):find("Workshop", 1, true) then @@ -130,6 +150,7 @@ if CLIENT then include("wire/client/customspawnmenu.lua") include("wire/client/node_editor/nodeeditor.lua") include("wire/client/node_editor/wire_fpga_editor.lua") + include("wire/client/hlzasm/hc_compiler.lua") end if SERVER then print("Wiremod " .. select(2, WireLib.GetVersion()) .. " loaded") end diff --git a/lua/wire/cpu_default_data_decompressor.lua b/lua/wire/cpu_default_data_decompressor.lua deleted file mode 100644 index 2cf6cd4a2b..0000000000 --- a/lua/wire/cpu_default_data_decompressor.lua +++ /dev/null @@ -1,54 +0,0 @@ --- Garry has imposed a file extension whitelist for the Steam Workshop which does not permit the dangerous format .txt --- Therefore, we must store our .txt's in default_data_files.lua, and then extract them when first run - -local ignored_dirs = { - ["cpuchip/tests"] = true, - ["gpuchip/tests"] = true, - ["spuchip/tests"] = true -} - -local checked_dirs = { - "cpuchip", - "gpuchip", - "spuchip" -} - --- Compress all files in addons/wire/data recursively into 1 json string -local function ReadDir(root) - if ignored_dirs[root] then return nil end - local tab = {} - local files,dirs = file.Find("data_static/"..root.."*","GAME") - for _, f in pairs(files) do - f = root..f - tab[f] = file.Read("data_static/"..f, "GAME") - end - for _, f in pairs(dirs) do - f = root..f.."/" - tab[f] = ReadDir(f) - end - return tab -end - --- Uncomment and Rename this file to wire/lua/wire/default_data_files.lua to update it --- file.Write("cpu_default_data_files.txt", "--"..util.TableToJSON(ReadDir(""))) - --- Decompress the json string wire/lua/wire/default_data_files.lua into the corresponding 36+ default data files -local function WriteDir(tab) - for f, contents in pairs(tab) do - if isstring(contents) then - if not file.Exists(f,"DATA") then - file.Write(f, contents) - end - else - file.CreateDir(f) - WriteDir(contents) - end - end -end - --- Write any missing files to the folder -if CLIENT then - for _,dir in pairs(checked_dirs) do - WriteDir(ReadDir(dir..'/'), 3) - end -end \ No newline at end of file diff --git a/lua/wire/default_data_generator.lua b/lua/wire/default_data_generator.lua index 265015eb10..3e91928a68 100644 --- a/lua/wire/default_data_generator.lua +++ b/lua/wire/default_data_generator.lua @@ -18,11 +18,14 @@ function WireLib.GenerateDefaultData() -- When adding new folders that need to be generated, add them to this list RecursivelyGenerateFolder("data_static/expression2/") RecursivelyGenerateFolder("data_static/fpgachip/") + RecursivelyGenerateFolder("data_static/cpuchip/") + RecursivelyGenerateFolder("data_static/gpuchip/") + RecursivelyGenerateFolder("data_static/spuchip/") RecursivelyGenerateFolder("data_static/soundlists/") end -- Regenerate data files on every structure update -local DataVersion = 2 +local DataVersion = 3 if cookie.GetNumber("wire_data_version", 0) < DataVersion then cookie.Set("wire_data_version", tostring(DataVersion)) From a90a966f7db157e06a878db1ee3dfb8fee6e93a9 Mon Sep 17 00:00:00 2001 From: Astralcircle <142503363+Astralcircle@users.noreply.github.com> Date: Fri, 13 Feb 2026 18:31:45 +0300 Subject: [PATCH 2/3] Add icons for new tools --- lua/wire/stools/cpu.lua | 2 ++ lua/wire/stools/fpga.lua | 2 ++ lua/wire/stools/gpu.lua | 2 ++ lua/wire/stools/spu.lua | 2 ++ 4 files changed, 8 insertions(+) diff --git a/lua/wire/stools/cpu.lua b/lua/wire/stools/cpu.lua index 204620d2f8..58fe294e4b 100644 --- a/lua/wire/stools/cpu.lua +++ b/lua/wire/stools/cpu.lua @@ -11,6 +11,8 @@ if CLIENT then { name = "reload", text = "Attach debugger" }, { name = "reload_shift", text = "Shift+Reload: Clear" }, } + + WireToolSetup.setToolMenuIcon("icon16/server.png") end WireToolSetup.BaseLang() WireToolSetup.SetupMax( 7 ) diff --git a/lua/wire/stools/fpga.lua b/lua/wire/stools/fpga.lua index e70602ce1c..12661ba6b6 100644 --- a/lua/wire/stools/fpga.lua +++ b/lua/wire/stools/fpga.lua @@ -10,6 +10,8 @@ if CLIENT then { name = "right", text = "Open editor" }, { name = "reload", text = "Reset" } } + + WireToolSetup.setToolMenuIcon("icon16/calendar.png") end WireToolSetup.BaseLang() WireToolSetup.SetupMax(40) diff --git a/lua/wire/stools/gpu.lua b/lua/wire/stools/gpu.lua index f444992e69..323febbc53 100644 --- a/lua/wire/stools/gpu.lua +++ b/lua/wire/stools/gpu.lua @@ -10,6 +10,8 @@ if CLIENT then { name = "right", text = "open editor and/or attach debugger to the ZGPU" }, { name = "reload", text = "Wipe ROM/RAM and reset memory model" }, } + + WireToolSetup.setToolMenuIcon("icon16/monitor.png") end WireToolSetup.BaseLang() WireToolSetup.SetupMax( 7 ) diff --git a/lua/wire/stools/spu.lua b/lua/wire/stools/spu.lua index 962e7b5955..d9f344867b 100644 --- a/lua/wire/stools/spu.lua +++ b/lua/wire/stools/spu.lua @@ -9,6 +9,8 @@ if CLIENT then { name = "left", text = "Create/reflash " .. TOOL.Name }, { name = "right", text = "Open editor" }, } + + WireToolSetup.setToolMenuIcon("icon16/server_lightning.png") end WireToolSetup.BaseLang() WireToolSetup.SetupMax( 7 ) From 3f97bc6811fd6f2d56c6945536773b2ca9b373fe Mon Sep 17 00:00:00 2001 From: Astralcircle <142503363+Astralcircle@users.noreply.github.com> Date: Fri, 13 Feb 2026 18:36:09 +0300 Subject: [PATCH 3/3] Convert all zcpu files to tabs --- lua/entities/gmod_wire_gpu/cl_gpuvm.lua | 4262 ++++++++--------- lua/entities/gmod_wire_gpu/cl_init.lua | 606 +-- lua/entities/gmod_wire_gpu/init.lua | 572 +-- lua/entities/gmod_wire_spu/cl_init.lua | 262 +- lua/entities/gmod_wire_spu/cl_spuvm.lua | 788 +-- lua/entities/gmod_wire_spu/init.lua | 496 +- lua/wire/client/hlzasm/hc_codetree.lua | 1066 ++--- lua/wire/client/hlzasm/hc_compiler.lua | 1366 +++--- lua/wire/client/hlzasm/hc_expression.lua | 1952 ++++---- lua/wire/client/hlzasm/hc_opcodes.lua | 78 +- lua/wire/client/hlzasm/hc_optimize.lua | 212 +- lua/wire/client/hlzasm/hc_output.lua | 1128 ++--- lua/wire/client/hlzasm/hc_preprocess.lua | 464 +- lua/wire/client/hlzasm/hc_syntax.lua | 3090 ++++++------ lua/wire/client/hlzasm/hc_tokenizer.lua | 820 ++-- lua/wire/client/text_editor/modes/zcpu.lua | 618 +-- lua/wire/stools/cpu.lua | 954 ++-- lua/wire/stools/gpu.lua | 448 +- lua/wire/stools/spu.lua | 408 +- .../tests/benchmarks/benchmark_page_bios.lua | 4 +- .../benchmarks/benchmark_zasm_assembler.lua | 8 +- lua/wire/zvm/tests/ifdefs.lua | 10 +- lua/wire/zvm/tests/virtualiobus.lua | 2 +- lua/wire/zvm/zvm_core.lua | 1186 ++--- lua/wire/zvm/zvm_features.lua | 1662 +++---- lua/wire/zvm/zvm_opcodes.lua | 2822 +++++------ lua/wire/zvm/zvm_tests.lua | 4 +- 27 files changed, 12644 insertions(+), 12644 deletions(-) diff --git a/lua/entities/gmod_wire_gpu/cl_gpuvm.lua b/lua/entities/gmod_wire_gpu/cl_gpuvm.lua index 4670890bad..741a210105 100644 --- a/lua/entities/gmod_wire_gpu/cl_gpuvm.lua +++ b/lua/entities/gmod_wire_gpu/cl_gpuvm.lua @@ -12,293 +12,293 @@ surface.CreateFont( "WireGPU_ErrorFont",{ }) function ENT:OverrideVM() - -- Store VM calls that will be overriden - self.VM.BaseReset = self.VM.Reset - - -- Add additional VM functionality - for k,v in pairs(VM) do - if k == "OpcodeTable" then - for k2,v2 in pairs(v) do - self.VM.OpcodeTable[k2] = v2 - end - else - self.VM[k] = v - end - end - - self.VM.Env["surface"] = { - SetTexture = surface.SetTexture, - SetDrawColor = surface.SetDrawColor, - DrawRect = surface.DrawRect, - DrawTexturedRect = surface.DrawTexturedRect - } - self.VM.Env["GPULib"] = { - Material = GPULib.Material - } - self.VM.Env["render"] = { - CopyTexture = render.CopyTexture - } - self.VM.Env["string"] = { - sub = string.sub, - len = string.len - } - self.VM.Env["tostring"] = tostring - self.VM.Env["tonumber"] = tonumber - self.VM.Env["WireGPU_matBuffer"] = WireGPU_matBuffer - - self.VM.ErrorText = {} - self.VM.ErrorText[2] = "Program ended unexpectedly" - self.VM.ErrorText[3] = "Arithmetic division by zero" - self.VM.ErrorText[4] = "Unknown instruction detected" - self.VM.ErrorText[5] = "Internal GPU error" - self.VM.ErrorText[6] = "Stack violation error" - self.VM.ErrorText[7] = "Memory I/O fault" - self.VM.ErrorText[13] = "General fault" - self.VM.ErrorText[15] = "Address space violation" - self.VM.ErrorText[16] = "Pants integrity violation" - self.VM.ErrorText[17] = "Frame instruction limit" - self.VM.ErrorText[18] = "Frame delayed by quota overrun" - self.VM.ErrorText[23] = "Error reading string data" - self.VM.ErrorText[24] = "Nonexistent color mode" - - self.VM.Interrupt = function(self,interruptNo,interruptParameter,isExternal,cascadeInterrupt) - if self.ASYNC == 1 then - if self.EntryPoint5 > 0 then - self.IP = self.EntryPoint5 - self.LADD = interruptParameter or self.XEIP - self.LINT = interruptNo - else - -- Shutdown asynchronous thread - self.Memory[65528] = 0 - end - else - if self.EntryPoint3 > 0 then - self.IP = self.EntryPoint3 - self.LADD = interruptParameter or self.XEIP - self.LINT = interruptNo - else - if (interruptNo == 2) and (self.XEIP == 0) then self.INTR = 1 return end - - if self.RenderEnable == 1 then - surface.SetTexture(0) - surface.SetDrawColor(0,0,0,120) - surface.DrawRect(0,0,self.ScreenWidth,self.ScreenHeight) - - draw.DrawText("Error in the instruction stream","WireGPU_ErrorFont",48,16,Color(255,255,255,255)) - draw.DrawText((self.ErrorText[interruptNo] or "Unknown error").." (#"..interruptNo..")","WireGPU_ErrorFont",16,16+32*2,Color(255,255,255,255)) - draw.DrawText("Parameter: "..(interruptParameter or 0),"WireGPU_ErrorFont",16,16+32*3,Color(255,255,255,255)) - draw.DrawText("Address: "..self.XEIP,"WireGPU_ErrorFont",16,16+32*4,Color(255,255,255,255)) - - local errorPosition = CPULib.Debugger.PositionByPointer[self.XEIP] - if errorPosition then - local posText = HCOMP:formatPrefix(errorPosition.Line,errorPosition.Col,errorPosition.File) - - draw.DrawText("Debugging data present (may be invalid):","WireGPU_ErrorFont",16,16+32*6,Color(255,255,255,255)) - draw.DrawText("Error at "..posText,"WireGPU_ErrorFont",16,16+32*7,Color(255,255,255,255)) - draw.DrawText("Line: ","WireGPU_ErrorFont",16,16+32*9,Color(255,255,255,255)) - end - end - self.INTR = 1 - end - end - end - - -- Override ports - self.VM.WritePort = function(VM,Port,Value) - VM:WriteCell(63488+Port,Value) - end - self.VM.ReadPort = function(VM,Port) - return VM:ReadCell(63488+Port) - end - - -- Override writecell - self.VM.BaseWriteCell = self.VM.WriteCell - self.VM.WriteCell = function(VM,Address,Value) - VM:BaseWriteCell(Address,Value) - if (Address >= 65536) and (Address <= 131071) then - if VM.MemBusCount < 8 then - VM.MemBusCount = VM.MemBusCount + 1 - VM.MemBusBuffer[Address] = Value - end - elseif Address == 65534 then - VM:HardReset() - elseif Address == 65530 then - VM.ROM = {} - elseif Address == 65529 then - VM.AsyncState = {} - end - end - - self.VM.ExternalWrite = function(VM, Address, Value) - if (Address >= 65536) and (Address <= 131071) then - return true - elseif Address < 0 then - VM:WritePort(-Address - 1, Value) - return true - end - - VM:Interrupt(7, Address) - return false - end - - local RGBXXXCache = {} - local function RGBXXXSingleCellColor(VM,Address) - local c = VM:ReadCell(Address) - local cached = RGBXXXCache[c] - VM.TMR = VM.TMR + 1 - if cached then return cached,1 end - VM.TMR = VM.TMR + 7 - local crgb = math.floor(c / 1000) - local cgray = c - math.floor(c / 1000)*1000 - - local cb = cgray+28*math.fmod(crgb, 10) - local cg = cgray+28*math.fmod(math.floor(crgb / 10), 10) - local cr = cgray+28*math.fmod(math.floor(crgb / 100), 10) - local color = {x=cr,y=cg,z=cb,w=255} - RGBXXXCache[c] = color - return color,1 - end - - local B24Cache = {} - local function B24SingleCellColor(VM,Address) - local c = VM:ReadCell(Address) - local cached = B24Cache[c] - VM.TMR = VM.TMR + 1 - if cached then return cached,1 end - VM.TMR = VM.TMR + 9 - local R = bit.band(bit.rshift(c,16),255) - local G = bit.band(bit.rshift(c,8),255) - local B = bit.band(c,255) - local color = {x=R,y=G,z=B,w=255} - B24Cache[c] = color - return color,1 - end - - local ArithmeticRGBCache = {} - local function ArithmeticRGBSingleCellColor(VM,Address) - local c = VM:ReadCell(Address) - local cached = ArithmeticRGBCache[c] - VM.TMR = VM.TMR + 1 - if cached then return cached,1 end - VM.TMR = VM.TMR + 14 - local cb = math.fmod(c, 1000) - local cg = math.fmod(math.floor(c / 1e3), 1000) - local cr = math.fmod(math.floor(c / 1e6), 1000) - local color = {x=cr,y=cg,z=cb,w=255} - ArithmeticRGBCache[c] = color - return color,1 - end - - local function MonoSingleCellColor(VM,Address) - local c = VM:ReadCell(Address) - return {x=c,y=c,z=c,w=255},1 - end - - local function RGBThreeCellColor(VM,Address) - local v3f = VM:ReadVector3f(Address) - v3f.w = 255 - return v3f,3 - end - - self.VM.ColorConversionModes = { - [0]={Converter = RGBXXXSingleCellColor, PixelSize = 1}, - [1]={Converter = RGBThreeCellColor, PixelSize = 3}, - [2]={Converter = B24SingleCellColor, PixelSize = 1}, - [3]={Converter = ArithmeticRGBSingleCellColor, PixelSize = 1}, - [4]={Converter = MonoSingleCellColor, PixelSize = 1}, - } - - function self.VM:ResetColorCaches() - RGBXXXCache = {} - B24Cache = {} - ArithmeticRGBCache = {} - end - - - -- Add internal registers - self.VM.InternalRegister[128] = "EntryPoint0" - self.VM.InternalRegister[129] = "EntryPoint1" - self.VM.InternalRegister[130] = "EntryPoint2" - self.VM.InternalRegister[131] = "EntryPoint3" - self.VM.InternalRegister[132] = "EntryPoint4" - self.VM.InternalRegister[133] = "EntryPoint5" - self.VM.InternalRegister[134] = "EntryPoint6" - self.VM.InternalRegister[135] = "EntryPoint7" - self.VM.InternalRegister[136] = "CoordinatePipe" - self.VM.InternalRegister[137] = "VertexPipe" - self.VM.InternalRegister[138] = "VertexBufZSort" - self.VM.InternalRegister[139] = "VertexLighting" - self.VM.InternalRegister[140] = "VertexBufEnabled" - self.VM.InternalRegister[141] = "VertexCulling" - self.VM.InternalRegister[142] = "DistanceCulling" - self.VM.InternalRegister[143] = "Font" - self.VM.InternalRegister[144] = "FontSize" - self.VM.InternalRegister[145] = "WordWrapMode" - self.VM.InternalRegister[146] = "ASYNC" - self.VM.InternalRegister[147] = "INIT" - - -- Remove internal registers - self.VM.InternalRegister[24] = nil --IDTR - self.VM.InternalRegister[32] = nil --IF - self.VM.InternalRegister[33] = nil --PF - self.VM.InternalRegister[34] = nil --EF - self.VM.InternalRegister[45] = nil --BusLock - self.VM.InternalRegister[46] = nil --IDLE - self.VM.InternalRegister[47] = nil --INTR - self.VM.InternalRegister[52] = nil --NIDT - - -- Remove some instructions - self.VM.OperandCount[16] = nil --RD - self.VM.OperandCount[17] = nil --WD - self.VM.OperandCount[28] = nil --SPG - self.VM.OperandCount[29] = nil --CPG - self.VM.OperandCount[37] = nil --HALT - self.VM.OperandCount[41] = nil --IRET - self.VM.OperandCount[42] = nil --STI - self.VM.OperandCount[43] = nil --CLI - self.VM.OperandCount[44] = nil --STP - self.VM.OperandCount[45] = nil --CLP - self.VM.OperandCount[46] = nil --STD - self.VM.OperandCount[48] = nil --STEF - self.VM.OperandCount[49] = nil --CLEF - self.VM.OperandCount[70] = nil --EXTINT - self.VM.OperandCount[95] = nil --ERPG - self.VM.OperandCount[96] = nil --WRPG - self.VM.OperandCount[97] = nil --RDPG - self.VM.OperandCount[99] = nil --LIDTR - self.VM.OperandCount[100] = nil --STATESTORE - self.VM.OperandCount[109] = nil --STATERESTORE - self.VM.OperandCount[110] = nil --EXTRET - self.VM.OperandCount[113] = nil --RLADD - self.VM.OperandCount[116] = nil --STD2 - self.VM.OperandCount[118] = nil --STM - self.VM.OperandCount[119] = nil --CLM - self.VM.OperandCount[122] = nil --SPP - self.VM.OperandCount[123] = nil --CPP - self.VM.OperandCount[124] = nil --SRL - self.VM.OperandCount[125] = nil --GRL - 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" - self.VM.FontName[1] = "Courier New" - self.VM.FontName[2] = "Trebuchet" - self.VM.FontName[3] = "Arial" - self.VM.FontName[4] = "Times New Roman" - self.VM.FontName[5] = "Coolvetica" - self.VM.FontName[6] = "Akbar" - self.VM.FontName[7] = "csd" - - -- Add text layouter - self.VM.Layouter = MakeTextScreenLayouter() - self.VM.Entity = self + -- Store VM calls that will be overriden + self.VM.BaseReset = self.VM.Reset + + -- Add additional VM functionality + for k,v in pairs(VM) do + if k == "OpcodeTable" then + for k2,v2 in pairs(v) do + self.VM.OpcodeTable[k2] = v2 + end + else + self.VM[k] = v + end + end + + self.VM.Env["surface"] = { + SetTexture = surface.SetTexture, + SetDrawColor = surface.SetDrawColor, + DrawRect = surface.DrawRect, + DrawTexturedRect = surface.DrawTexturedRect + } + self.VM.Env["GPULib"] = { + Material = GPULib.Material + } + self.VM.Env["render"] = { + CopyTexture = render.CopyTexture + } + self.VM.Env["string"] = { + sub = string.sub, + len = string.len + } + self.VM.Env["tostring"] = tostring + self.VM.Env["tonumber"] = tonumber + self.VM.Env["WireGPU_matBuffer"] = WireGPU_matBuffer + + self.VM.ErrorText = {} + self.VM.ErrorText[2] = "Program ended unexpectedly" + self.VM.ErrorText[3] = "Arithmetic division by zero" + self.VM.ErrorText[4] = "Unknown instruction detected" + self.VM.ErrorText[5] = "Internal GPU error" + self.VM.ErrorText[6] = "Stack violation error" + self.VM.ErrorText[7] = "Memory I/O fault" + self.VM.ErrorText[13] = "General fault" + self.VM.ErrorText[15] = "Address space violation" + self.VM.ErrorText[16] = "Pants integrity violation" + self.VM.ErrorText[17] = "Frame instruction limit" + self.VM.ErrorText[18] = "Frame delayed by quota overrun" + self.VM.ErrorText[23] = "Error reading string data" + self.VM.ErrorText[24] = "Nonexistent color mode" + + self.VM.Interrupt = function(self,interruptNo,interruptParameter,isExternal,cascadeInterrupt) + if self.ASYNC == 1 then + if self.EntryPoint5 > 0 then + self.IP = self.EntryPoint5 + self.LADD = interruptParameter or self.XEIP + self.LINT = interruptNo + else + -- Shutdown asynchronous thread + self.Memory[65528] = 0 + end + else + if self.EntryPoint3 > 0 then + self.IP = self.EntryPoint3 + self.LADD = interruptParameter or self.XEIP + self.LINT = interruptNo + else + if (interruptNo == 2) and (self.XEIP == 0) then self.INTR = 1 return end + + if self.RenderEnable == 1 then + surface.SetTexture(0) + surface.SetDrawColor(0,0,0,120) + surface.DrawRect(0,0,self.ScreenWidth,self.ScreenHeight) + + draw.DrawText("Error in the instruction stream","WireGPU_ErrorFont",48,16,Color(255,255,255,255)) + draw.DrawText((self.ErrorText[interruptNo] or "Unknown error").." (#"..interruptNo..")","WireGPU_ErrorFont",16,16+32*2,Color(255,255,255,255)) + draw.DrawText("Parameter: "..(interruptParameter or 0),"WireGPU_ErrorFont",16,16+32*3,Color(255,255,255,255)) + draw.DrawText("Address: "..self.XEIP,"WireGPU_ErrorFont",16,16+32*4,Color(255,255,255,255)) + + local errorPosition = CPULib.Debugger.PositionByPointer[self.XEIP] + if errorPosition then + local posText = HCOMP:formatPrefix(errorPosition.Line,errorPosition.Col,errorPosition.File) + + draw.DrawText("Debugging data present (may be invalid):","WireGPU_ErrorFont",16,16+32*6,Color(255,255,255,255)) + draw.DrawText("Error at "..posText,"WireGPU_ErrorFont",16,16+32*7,Color(255,255,255,255)) + draw.DrawText("Line: ","WireGPU_ErrorFont",16,16+32*9,Color(255,255,255,255)) + end + end + self.INTR = 1 + end + end + end + + -- Override ports + self.VM.WritePort = function(VM,Port,Value) + VM:WriteCell(63488+Port,Value) + end + self.VM.ReadPort = function(VM,Port) + return VM:ReadCell(63488+Port) + end + + -- Override writecell + self.VM.BaseWriteCell = self.VM.WriteCell + self.VM.WriteCell = function(VM,Address,Value) + VM:BaseWriteCell(Address,Value) + if (Address >= 65536) and (Address <= 131071) then + if VM.MemBusCount < 8 then + VM.MemBusCount = VM.MemBusCount + 1 + VM.MemBusBuffer[Address] = Value + end + elseif Address == 65534 then + VM:HardReset() + elseif Address == 65530 then + VM.ROM = {} + elseif Address == 65529 then + VM.AsyncState = {} + end + end + + self.VM.ExternalWrite = function(VM, Address, Value) + if (Address >= 65536) and (Address <= 131071) then + return true + elseif Address < 0 then + VM:WritePort(-Address - 1, Value) + return true + end + + VM:Interrupt(7, Address) + return false + end + + local RGBXXXCache = {} + local function RGBXXXSingleCellColor(VM,Address) + local c = VM:ReadCell(Address) + local cached = RGBXXXCache[c] + VM.TMR = VM.TMR + 1 + if cached then return cached,1 end + VM.TMR = VM.TMR + 7 + local crgb = math.floor(c / 1000) + local cgray = c - math.floor(c / 1000)*1000 + + local cb = cgray+28*math.fmod(crgb, 10) + local cg = cgray+28*math.fmod(math.floor(crgb / 10), 10) + local cr = cgray+28*math.fmod(math.floor(crgb / 100), 10) + local color = {x=cr,y=cg,z=cb,w=255} + RGBXXXCache[c] = color + return color,1 + end + + local B24Cache = {} + local function B24SingleCellColor(VM,Address) + local c = VM:ReadCell(Address) + local cached = B24Cache[c] + VM.TMR = VM.TMR + 1 + if cached then return cached,1 end + VM.TMR = VM.TMR + 9 + local R = bit.band(bit.rshift(c,16),255) + local G = bit.band(bit.rshift(c,8),255) + local B = bit.band(c,255) + local color = {x=R,y=G,z=B,w=255} + B24Cache[c] = color + return color,1 + end + + local ArithmeticRGBCache = {} + local function ArithmeticRGBSingleCellColor(VM,Address) + local c = VM:ReadCell(Address) + local cached = ArithmeticRGBCache[c] + VM.TMR = VM.TMR + 1 + if cached then return cached,1 end + VM.TMR = VM.TMR + 14 + local cb = math.fmod(c, 1000) + local cg = math.fmod(math.floor(c / 1e3), 1000) + local cr = math.fmod(math.floor(c / 1e6), 1000) + local color = {x=cr,y=cg,z=cb,w=255} + ArithmeticRGBCache[c] = color + return color,1 + end + + local function MonoSingleCellColor(VM,Address) + local c = VM:ReadCell(Address) + return {x=c,y=c,z=c,w=255},1 + end + + local function RGBThreeCellColor(VM,Address) + local v3f = VM:ReadVector3f(Address) + v3f.w = 255 + return v3f,3 + end + + self.VM.ColorConversionModes = { + [0]={Converter = RGBXXXSingleCellColor, PixelSize = 1}, + [1]={Converter = RGBThreeCellColor, PixelSize = 3}, + [2]={Converter = B24SingleCellColor, PixelSize = 1}, + [3]={Converter = ArithmeticRGBSingleCellColor, PixelSize = 1}, + [4]={Converter = MonoSingleCellColor, PixelSize = 1}, + } + + function self.VM:ResetColorCaches() + RGBXXXCache = {} + B24Cache = {} + ArithmeticRGBCache = {} + end + + + -- Add internal registers + self.VM.InternalRegister[128] = "EntryPoint0" + self.VM.InternalRegister[129] = "EntryPoint1" + self.VM.InternalRegister[130] = "EntryPoint2" + self.VM.InternalRegister[131] = "EntryPoint3" + self.VM.InternalRegister[132] = "EntryPoint4" + self.VM.InternalRegister[133] = "EntryPoint5" + self.VM.InternalRegister[134] = "EntryPoint6" + self.VM.InternalRegister[135] = "EntryPoint7" + self.VM.InternalRegister[136] = "CoordinatePipe" + self.VM.InternalRegister[137] = "VertexPipe" + self.VM.InternalRegister[138] = "VertexBufZSort" + self.VM.InternalRegister[139] = "VertexLighting" + self.VM.InternalRegister[140] = "VertexBufEnabled" + self.VM.InternalRegister[141] = "VertexCulling" + self.VM.InternalRegister[142] = "DistanceCulling" + self.VM.InternalRegister[143] = "Font" + self.VM.InternalRegister[144] = "FontSize" + self.VM.InternalRegister[145] = "WordWrapMode" + self.VM.InternalRegister[146] = "ASYNC" + self.VM.InternalRegister[147] = "INIT" + + -- Remove internal registers + self.VM.InternalRegister[24] = nil --IDTR + self.VM.InternalRegister[32] = nil --IF + self.VM.InternalRegister[33] = nil --PF + self.VM.InternalRegister[34] = nil --EF + self.VM.InternalRegister[45] = nil --BusLock + self.VM.InternalRegister[46] = nil --IDLE + self.VM.InternalRegister[47] = nil --INTR + self.VM.InternalRegister[52] = nil --NIDT + + -- Remove some instructions + self.VM.OperandCount[16] = nil --RD + self.VM.OperandCount[17] = nil --WD + self.VM.OperandCount[28] = nil --SPG + self.VM.OperandCount[29] = nil --CPG + self.VM.OperandCount[37] = nil --HALT + self.VM.OperandCount[41] = nil --IRET + self.VM.OperandCount[42] = nil --STI + self.VM.OperandCount[43] = nil --CLI + self.VM.OperandCount[44] = nil --STP + self.VM.OperandCount[45] = nil --CLP + self.VM.OperandCount[46] = nil --STD + self.VM.OperandCount[48] = nil --STEF + self.VM.OperandCount[49] = nil --CLEF + self.VM.OperandCount[70] = nil --EXTINT + self.VM.OperandCount[95] = nil --ERPG + self.VM.OperandCount[96] = nil --WRPG + self.VM.OperandCount[97] = nil --RDPG + self.VM.OperandCount[99] = nil --LIDTR + self.VM.OperandCount[100] = nil --STATESTORE + self.VM.OperandCount[109] = nil --STATERESTORE + self.VM.OperandCount[110] = nil --EXTRET + self.VM.OperandCount[113] = nil --RLADD + self.VM.OperandCount[116] = nil --STD2 + self.VM.OperandCount[118] = nil --STM + self.VM.OperandCount[119] = nil --CLM + self.VM.OperandCount[122] = nil --SPP + self.VM.OperandCount[123] = nil --CPP + self.VM.OperandCount[124] = nil --SRL + self.VM.OperandCount[125] = nil --GRL + 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" + self.VM.FontName[1] = "Courier New" + self.VM.FontName[2] = "Trebuchet" + self.VM.FontName[3] = "Arial" + self.VM.FontName[4] = "Times New Roman" + self.VM.FontName[5] = "Coolvetica" + self.VM.FontName[6] = "Akbar" + self.VM.FontName[7] = "csd" + + -- Add text layouter + self.VM.Layouter = MakeTextScreenLayouter() + self.VM.Entity = self end @@ -332,135 +332,135 @@ end -- Reset state each GPU frame -------------------------------------------------------------------------------- function VM:Reset() - -- Reset VM - 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 = 32767 - 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 - - self.ESZ = 32768 -- Stack size register - 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.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.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) - -- [131072]..[1048576] - Extended GPU memory (1MB GPU) - -- [131072]..[524287] - Extended GPU memory (512K GPU) - -- [131072]..[262143] - Extended GPU memory (256K GPU) - -- No extended memory (128K GPU) - -- [65536]..[131071] - MemBus mapped memory (read/write) - -- No extra memory beyond 65536 (64K GPU) - -- - -- Hardware control registers: - -- [65535] - CLK - -- [65534] - RESET - -- [65533] - HARDWARE CLEAR - -- [65532] - Vertex mode (render vertex instead of RT) - -- [65531] - HALT - -- [65530] - RAM_RESET - -- [65529] - Async thread reset - -- [65528] - Async thread clk - -- [65527] - Async thread frequency - -- [65526] - Player index (0 to 31) - -- - -- Image control: - -- [65525] - Horizontal image scale - -- [65524] - Vertical image scale - -- [65523] - Hardware scale - -- [65522] - Rotation (0 - 0*, 1 - 90*, 2 - 180*, 3 - 270*) - -- [65521] - Sprite/texture size - -- [65520] - Pointer to texture data - -- [65519] - Size of texture data - -- [65518] - Raster quality - -- [65517] - Texture buffer (1: sprite buffer, 0: front buffer) - -- - -- Vertex pipe controls: - -- [65515] - Image width (800) - -- [65514] - Image height (600) - -- [65513] - Real screen ratio - -- [65512] - Parameter list address (for dwritefmt) - -- - -- Cursor control: - -- [65505] - Cursor X (0..1) - -- [65504] - Cursor Y (0..1) - -- [65503] - Cursor visible - -- [65502] - Cursor buttons (bits) - -- - -- Brightness control: - -- [65495] - Brightness W - -- [65494] - Brightness R - -- [65493] - Brightness G - -- [65492] - Brightness B - -- [65491] - Contrast W - -- [65490] - Contrast R - -- [65489] - Contrast G - -- [65488] - Contrast B - -- - -- Rendering settings - -- [65485] - Circle quality (3..128) - -- [65484] - Offset Point X - -- [65483] - Offset Point Y - -- [65482] - Rotation (rad) - -- [65481] - Scale - -- [65480] - Center point X - -- [65479] - Center point Y - -- [65478] - Circle start (rad) - -- [65477] - Circle end (rad) - -- [65476] - Line width (1) - -- [65475] - Scale X - -- [65474] - Scale Y - -- [65473] - Font horizontal align - -- [65472] - ZOffset - -- [65471] - Font vertical align - -- [65470] - Culling distance - -- [65469] - Culling mode (0: front, 1: back) - -- [65468] - Single-side lighting (1: front, -1: back) - -- [65467] - Memory offset of vertex data (non-zero means poly ops take indexes into this array) - -- [65466] - Texture rotation (rad) - -- [65465] - Texture scale - -- [65464] - Texture center point U - -- [65463] - Texture center point V - -- [65462] - Texture offset U - -- [65461] - Texture offset V - -- - -- Misc: - -- [64512] - Last register - -- [63488]..[64511] - External ports - - self.Memory[65535] = 1 - self.Memory[65534] = 0 + -- Reset VM + 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 = 32767 + 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 + + self.ESZ = 32768 -- Stack size register + 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.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.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) + -- [131072]..[1048576] - Extended GPU memory (1MB GPU) + -- [131072]..[524287] - Extended GPU memory (512K GPU) + -- [131072]..[262143] - Extended GPU memory (256K GPU) + -- No extended memory (128K GPU) + -- [65536]..[131071] - MemBus mapped memory (read/write) + -- No extra memory beyond 65536 (64K GPU) + -- + -- Hardware control registers: + -- [65535] - CLK + -- [65534] - RESET + -- [65533] - HARDWARE CLEAR + -- [65532] - Vertex mode (render vertex instead of RT) + -- [65531] - HALT + -- [65530] - RAM_RESET + -- [65529] - Async thread reset + -- [65528] - Async thread clk + -- [65527] - Async thread frequency + -- [65526] - Player index (0 to 31) + -- + -- Image control: + -- [65525] - Horizontal image scale + -- [65524] - Vertical image scale + -- [65523] - Hardware scale + -- [65522] - Rotation (0 - 0*, 1 - 90*, 2 - 180*, 3 - 270*) + -- [65521] - Sprite/texture size + -- [65520] - Pointer to texture data + -- [65519] - Size of texture data + -- [65518] - Raster quality + -- [65517] - Texture buffer (1: sprite buffer, 0: front buffer) + -- + -- Vertex pipe controls: + -- [65515] - Image width (800) + -- [65514] - Image height (600) + -- [65513] - Real screen ratio + -- [65512] - Parameter list address (for dwritefmt) + -- + -- Cursor control: + -- [65505] - Cursor X (0..1) + -- [65504] - Cursor Y (0..1) + -- [65503] - Cursor visible + -- [65502] - Cursor buttons (bits) + -- + -- Brightness control: + -- [65495] - Brightness W + -- [65494] - Brightness R + -- [65493] - Brightness G + -- [65492] - Brightness B + -- [65491] - Contrast W + -- [65490] - Contrast R + -- [65489] - Contrast G + -- [65488] - Contrast B + -- + -- Rendering settings + -- [65485] - Circle quality (3..128) + -- [65484] - Offset Point X + -- [65483] - Offset Point Y + -- [65482] - Rotation (rad) + -- [65481] - Scale + -- [65480] - Center point X + -- [65479] - Center point Y + -- [65478] - Circle start (rad) + -- [65477] - Circle end (rad) + -- [65476] - Line width (1) + -- [65475] - Scale X + -- [65474] - Scale Y + -- [65473] - Font horizontal align + -- [65472] - ZOffset + -- [65471] - Font vertical align + -- [65470] - Culling distance + -- [65469] - Culling mode (0: front, 1: back) + -- [65468] - Single-side lighting (1: front, -1: back) + -- [65467] - Memory offset of vertex data (non-zero means poly ops take indexes into this array) + -- [65466] - Texture rotation (rad) + -- [65465] - Texture scale + -- [65464] - Texture center point U + -- [65463] - Texture center point V + -- [65462] - Texture offset U + -- [65461] - Texture offset V + -- + -- Misc: + -- [64512] - Last register + -- [63488]..[64511] - External ports + + self.Memory[65535] = 1 + self.Memory[65534] = 0 --self.Memory[65533] = 1 (value persists over reset) --self.Memory[65532] = 0 --self.Memory[65531] = 0 (value persists over reset) @@ -468,122 +468,122 @@ function VM:Reset() --self.Memory[65529] = 0 --self.Memory[65528] = 0 --self.Memory[65527] = 0 - self.Memory[65526] = (LocalPlayer():UserID() % 32) - ---------------------- - self.Memory[65525] = 1 - self.Memory[65524] = 1 - self.Memory[65523] = 0 - self.Memory[65522] = 0 - self.Memory[65521] = 512 - self.Memory[65520] = 0 - self.Memory[65519] = 0 - self.Memory[65518] = 0 - self.Memory[65517] = 1 - ---------------------- - self.Memory[65515] = 800 - self.Memory[65514] = 600 + self.Memory[65526] = (LocalPlayer():UserID() % 32) + ---------------------- + self.Memory[65525] = 1 + self.Memory[65524] = 1 + self.Memory[65523] = 0 + self.Memory[65522] = 0 + self.Memory[65521] = 512 + self.Memory[65520] = 0 + self.Memory[65519] = 0 + self.Memory[65518] = 0 + self.Memory[65517] = 1 + ---------------------- + self.Memory[65515] = 800 + self.Memory[65514] = 600 --self.Memory[65513] = 0 (set elsewhere) - self.Memory[65512] = 0 - ---------------------- + self.Memory[65512] = 0 + ---------------------- --self.Memory[65505] = 0 (set elsewhere) --self.Memory[65504] = 0 (set elsewhere) - self.Memory[65503] = 0 + self.Memory[65503] = 0 --self.Memory[65502] = 0 - ---------------------- - self.Memory[65495] = 1 - self.Memory[65494] = 1 - self.Memory[65493] = 1 - self.Memory[65492] = 1 - self.Memory[65491] = 0 - self.Memory[65490] = 0 - self.Memory[65489] = 0 - self.Memory[65488] = 0 - ---------------------- - self.Memory[65485] = 32 - self.Memory[65484] = 0 - self.Memory[65483] = 0 - self.Memory[65482] = 0 - self.Memory[65481] = 1 - self.Memory[65480] = 0 - self.Memory[65479] = 0 - self.Memory[65478] = 0 - self.Memory[65477] = 6.28318530717 - self.Memory[65476] = 1 - self.Memory[65475] = 1 - self.Memory[65474] = 1 - self.Memory[65473] = 0 - self.Memory[65472] = 0 - self.Memory[65471] = 0 - self.Memory[65470] = 0 - self.Memory[65469] = 0 - self.Memory[65468] = 0 - self.Memory[65467] = 0 - self.Memory[65466] = 0 - self.Memory[65465] = 1 - self.Memory[65464] = 0.5 - self.Memory[65463] = 0.5 - self.Memory[65462] = 0 - self.Memory[65461] = 0 - - -- Coordinate pipe - -- 0 - direct (0..512 or 0..1024 range) - -- 1 - mapped to screen - -- 2 - mapped to 0..1 range - -- 3 - mapped to -1..1 range - self.CoordinatePipe = 0 - - -- Vertex pipes: - -- 0 - XY mapping - -- 1 - YZ mapping - -- 2 - XZ mapping - -- 3 - XYZ projective mapping - -- 4 - XY mapping + matrix - -- 5 - XYZ projective mapping + matrix - self.VertexPipe = 0 - - -- Flags that can be ddisable/ddenable-d - -- 0 VERTEX_ZSORT Enable or disable ZSorting in vertex buffer (sorted on flush) - self.VertexBufZSort = 0 - -- 1 VERTEX_LIGHTING Enable or disable vertex lighting - self.VertexLighting = 0 - -- 2 VERTEX_BUFFER Enable or disable vertex buffer - self.VertexBufEnabled = 0 - -- 3 VERTEX_CULLING Enable or disable culling on faces - self.VertexCulling = 0 - -- 4 VERTEX_DCULLING Enable or disable distance culling - self.DistanceCulling = 0 - -- 5 VERTEX_TEXTURING Enable texturing from sprite buffer - self.VertexTexturing = 0 - - -- Font layouter related - self.Font = 0 - self.FontSize = 12 - self.TextBox = { x = 512, y = 512, z = 0, w = 0 } - self.WordWrapMode = 0 - - -- Current color - self.Color = {x = 0, y = 0, z = 0, w = 255} - self.Material = nil - self.Texture = 0 - - -- Model transform matrix - self.ModelMatrix = self.ModelMatrix or {} - self.ModelMatrix[0] = 1 self.ModelMatrix[1] = 0 self.ModelMatrix[2] = 0 self.ModelMatrix[3] = 0 - self.ModelMatrix[4] = 0 self.ModelMatrix[5] = 1 self.ModelMatrix[6] = 0 self.ModelMatrix[7] = 0 - self.ModelMatrix[8] = 0 self.ModelMatrix[9] = 0 self.ModelMatrix[10] = 1 self.ModelMatrix[11] = 0 - self.ModelMatrix[12] = 0 self.ModelMatrix[13] = 0 self.ModelMatrix[14] = 0 self.ModelMatrix[15] = 1 - - --View transform matrix - self.ProjectionMatrix = self.ProjectionMatrix or {} - self.ProjectionMatrix[0] = 1 self.ProjectionMatrix[1] = 0 self.ProjectionMatrix[2] = 0 self.ProjectionMatrix[3] = 0 - self.ProjectionMatrix[4] = 0 self.ProjectionMatrix[5] = 1 self.ProjectionMatrix[6] = 0 self.ProjectionMatrix[7] = 0 - self.ProjectionMatrix[8] = 0 self.ProjectionMatrix[9] = 0 self.ProjectionMatrix[10] = 1 self.ProjectionMatrix[11] = 0 - self.ProjectionMatrix[12] = 0 self.ProjectionMatrix[13] = 0 self.ProjectionMatrix[14] = 0 self.ProjectionMatrix[15] = 1 - - -- Reset buffers: - self.StringCache = {} - self.VertexBuffer = {} - self.Lights = {} + ---------------------- + self.Memory[65495] = 1 + self.Memory[65494] = 1 + self.Memory[65493] = 1 + self.Memory[65492] = 1 + self.Memory[65491] = 0 + self.Memory[65490] = 0 + self.Memory[65489] = 0 + self.Memory[65488] = 0 + ---------------------- + self.Memory[65485] = 32 + self.Memory[65484] = 0 + self.Memory[65483] = 0 + self.Memory[65482] = 0 + self.Memory[65481] = 1 + self.Memory[65480] = 0 + self.Memory[65479] = 0 + self.Memory[65478] = 0 + self.Memory[65477] = 6.28318530717 + self.Memory[65476] = 1 + self.Memory[65475] = 1 + self.Memory[65474] = 1 + self.Memory[65473] = 0 + self.Memory[65472] = 0 + self.Memory[65471] = 0 + self.Memory[65470] = 0 + self.Memory[65469] = 0 + self.Memory[65468] = 0 + self.Memory[65467] = 0 + self.Memory[65466] = 0 + self.Memory[65465] = 1 + self.Memory[65464] = 0.5 + self.Memory[65463] = 0.5 + self.Memory[65462] = 0 + self.Memory[65461] = 0 + + -- Coordinate pipe + -- 0 - direct (0..512 or 0..1024 range) + -- 1 - mapped to screen + -- 2 - mapped to 0..1 range + -- 3 - mapped to -1..1 range + self.CoordinatePipe = 0 + + -- Vertex pipes: + -- 0 - XY mapping + -- 1 - YZ mapping + -- 2 - XZ mapping + -- 3 - XYZ projective mapping + -- 4 - XY mapping + matrix + -- 5 - XYZ projective mapping + matrix + self.VertexPipe = 0 + + -- Flags that can be ddisable/ddenable-d + -- 0 VERTEX_ZSORT Enable or disable ZSorting in vertex buffer (sorted on flush) + self.VertexBufZSort = 0 + -- 1 VERTEX_LIGHTING Enable or disable vertex lighting + self.VertexLighting = 0 + -- 2 VERTEX_BUFFER Enable or disable vertex buffer + self.VertexBufEnabled = 0 + -- 3 VERTEX_CULLING Enable or disable culling on faces + self.VertexCulling = 0 + -- 4 VERTEX_DCULLING Enable or disable distance culling + self.DistanceCulling = 0 + -- 5 VERTEX_TEXTURING Enable texturing from sprite buffer + self.VertexTexturing = 0 + + -- Font layouter related + self.Font = 0 + self.FontSize = 12 + self.TextBox = { x = 512, y = 512, z = 0, w = 0 } + self.WordWrapMode = 0 + + -- Current color + self.Color = {x = 0, y = 0, z = 0, w = 255} + self.Material = nil + self.Texture = 0 + + -- Model transform matrix + self.ModelMatrix = self.ModelMatrix or {} + self.ModelMatrix[0] = 1 self.ModelMatrix[1] = 0 self.ModelMatrix[2] = 0 self.ModelMatrix[3] = 0 + self.ModelMatrix[4] = 0 self.ModelMatrix[5] = 1 self.ModelMatrix[6] = 0 self.ModelMatrix[7] = 0 + self.ModelMatrix[8] = 0 self.ModelMatrix[9] = 0 self.ModelMatrix[10] = 1 self.ModelMatrix[11] = 0 + self.ModelMatrix[12] = 0 self.ModelMatrix[13] = 0 self.ModelMatrix[14] = 0 self.ModelMatrix[15] = 1 + + --View transform matrix + self.ProjectionMatrix = self.ProjectionMatrix or {} + self.ProjectionMatrix[0] = 1 self.ProjectionMatrix[1] = 0 self.ProjectionMatrix[2] = 0 self.ProjectionMatrix[3] = 0 + self.ProjectionMatrix[4] = 0 self.ProjectionMatrix[5] = 1 self.ProjectionMatrix[6] = 0 self.ProjectionMatrix[7] = 0 + self.ProjectionMatrix[8] = 0 self.ProjectionMatrix[9] = 0 self.ProjectionMatrix[10] = 1 self.ProjectionMatrix[11] = 0 + self.ProjectionMatrix[12] = 0 self.ProjectionMatrix[13] = 0 self.ProjectionMatrix[14] = 0 self.ProjectionMatrix[15] = 1 + + -- Reset buffers: + self.StringCache = {} + self.VertexBuffer = {} + self.Lights = {} end @@ -592,46 +592,46 @@ end -- Save asynchronous thread state -------------------------------------------------------------------------------- local asyncPreservedVariables = { - "IP","EAX","EBX","ECX","EDX","ESI","EDI","ESP","EBP","CS","SS","DS","ES","GS", - "FS","KS","LS","ESZ","CMPR","XEIP","LADD","LINT","BPREC","IPREC","VMODE","INTR", - "BlockStart","BlockSize","CoordinatePipe","VertexPipe","VertexBufZSort","VertexLighting", - "VertexBufEnabled","VertexCulling","DistanceCulling","VertexTexturing","Font", - "FontSize","WordWrapMode","Material","Texture","VertexBuffer","Lights", + "IP","EAX","EBX","ECX","EDX","ESI","EDI","ESP","EBP","CS","SS","DS","ES","GS", + "FS","KS","LS","ESZ","CMPR","XEIP","LADD","LINT","BPREC","IPREC","VMODE","INTR", + "BlockStart","BlockSize","CoordinatePipe","VertexPipe","VertexBufZSort","VertexLighting", + "VertexBufEnabled","VertexCulling","DistanceCulling","VertexTexturing","Font", + "FontSize","WordWrapMode","Material","Texture","VertexBuffer","Lights", } for reg=0,31 do table.insert(asyncPreservedVariables,"R"..reg) end local asyncPreservedMemory = { - 65525,65524,65523,65522,65521,65520,65519,65518,65517, - 65515,65514,65512,65503,65495,65494,65493,65492,65491, - 65490,65489,65488,65485,65484,65483,65482,65481,65480, - 65479,65478,65477,65476,65475,65474,65473,65472,65471, - 65470,65469,65468,65467,65466,65465,65464,65463,65462, - 65461 + 65525,65524,65523,65522,65521,65520,65519,65518,65517, + 65515,65514,65512,65503,65495,65494,65493,65492,65491, + 65490,65489,65488,65485,65484,65483,65482,65481,65480, + 65479,65478,65477,65476,65475,65474,65473,65472,65471, + 65470,65469,65468,65467,65466,65465,65464,65463,65462, + 65461 } function VM:SaveAsyncThread_Util() - for _,var in pairs(asyncPreservedVariables) do self.AsyncState[var] = self[var] end - for _,mem in pairs(asyncPreservedMemory) do self.AsyncState[mem] = self.Memory[mem] end + for _,var in pairs(asyncPreservedVariables) do self.AsyncState[var] = self[var] end + for _,mem in pairs(asyncPreservedMemory) do self.AsyncState[mem] = self.Memory[mem] end - self.AsyncState.TextBox = { x = self.TextBox.x, y = self.TextBox.y, z = self.TextBox.z, w = self.TextBox.w } - self.AsyncState.Color = { x = self.Color.x, y = self.Color.y, z = self.Color.z, w = self.Color.w } - self.AsyncState.ModelMatrix = {} - self.AsyncState.ProjectionMatrix = {} - for k,v in pairs(self.ModelMatrix) do self.AsyncState.ModelMatrix[k] = v end - for k,v in pairs(self.ProjectionMatrix) do self.AsyncState.ProjectionMatrix[k] = v end + self.AsyncState.TextBox = { x = self.TextBox.x, y = self.TextBox.y, z = self.TextBox.z, w = self.TextBox.w } + self.AsyncState.Color = { x = self.Color.x, y = self.Color.y, z = self.Color.z, w = self.Color.w } + self.AsyncState.ModelMatrix = {} + self.AsyncState.ProjectionMatrix = {} + for k,v in pairs(self.ModelMatrix) do self.AsyncState.ModelMatrix[k] = v end + for k,v in pairs(self.ProjectionMatrix) do self.AsyncState.ProjectionMatrix[k] = v end end function VM:SaveAsyncThread() - if not self.AsyncState then - self.AsyncState = {} - self:Reset() + if not self.AsyncState then + self.AsyncState = {} + self:Reset() - self:SaveAsyncThread_Util() - self.AsyncState.IP = self.EntryPoint4 - return - end + self:SaveAsyncThread_Util() + self.AsyncState.IP = self.EntryPoint4 + return + end - self:SaveAsyncThread_Util() + self:SaveAsyncThread_Util() end @@ -640,27 +640,27 @@ end -- Restore asynchronous thread state -------------------------------------------------------------------------------- function VM:RestoreAsyncThread_Util() - for _,var in pairs(asyncPreservedVariables) do self[var] = self.AsyncState[var] end - for _,mem in pairs(asyncPreservedMemory) do self.Memory[mem] = self.AsyncState[mem] end + for _,var in pairs(asyncPreservedVariables) do self[var] = self.AsyncState[var] end + for _,mem in pairs(asyncPreservedMemory) do self.Memory[mem] = self.AsyncState[mem] end - self.TextBox = { x = self.AsyncState.TextBox.x, y = self.AsyncState.TextBox.y, z = self.AsyncState.TextBox.z, w = self.AsyncState.TextBox.w } - self.Color = { x = self.AsyncState.Color.x, y = self.AsyncState.Color.y, z = self.AsyncState.Color.z, w = self.AsyncState.Color.w } - self.ModelMatrix = {} - self.ProjectionMatrix = {} - for k,v in pairs(self.AsyncState.ModelMatrix) do self.ModelMatrix[k] = v end - for k,v in pairs(self.AsyncState.ProjectionMatrix) do self.ProjectionMatrix[k] = v end + self.TextBox = { x = self.AsyncState.TextBox.x, y = self.AsyncState.TextBox.y, z = self.AsyncState.TextBox.z, w = self.AsyncState.TextBox.w } + self.Color = { x = self.AsyncState.Color.x, y = self.AsyncState.Color.y, z = self.AsyncState.Color.z, w = self.AsyncState.Color.w } + self.ModelMatrix = {} + self.ProjectionMatrix = {} + for k,v in pairs(self.AsyncState.ModelMatrix) do self.ModelMatrix[k] = v end + for k,v in pairs(self.AsyncState.ProjectionMatrix) do self.ProjectionMatrix[k] = v end end function VM:RestoreAsyncThread() - if not self.AsyncState then - self.AsyncState = {} - self:Reset() + if not self.AsyncState then + self.AsyncState = {} + self:Reset() - self:SaveAsyncThread_Util() - self.AsyncState.IP = self.EntryPoint4 - end + self:SaveAsyncThread_Util() + self.AsyncState.IP = self.EntryPoint4 + end - self:RestoreAsyncThread_Util() + self:RestoreAsyncThread_Util() end @@ -669,43 +669,43 @@ end -- Reset GPU state and clear all persisting registers -------------------------------------------------------------------------------- function VM:HardReset() - self:Reset() + self:Reset() - -- Reset registers that usually persist over normal reset - self.Memory[65533] = 1 - self.Memory[65532] = 0 - self.Memory[65531] = 0 - self.Memory[65535] = 1 + -- Reset registers that usually persist over normal reset + self.Memory[65533] = 1 + self.Memory[65532] = 0 + self.Memory[65531] = 0 + self.Memory[65535] = 1 - self.Memory[65529] = 0 - self.Memory[65528] = 0 - self.Memory[65527] = 60000 + self.Memory[65529] = 0 + self.Memory[65528] = 0 + self.Memory[65527] = 60000 - self.Memory[65502] = 0 + self.Memory[65502] = 0 - -- Entrypoints to special calls - -- 0 DRAW Called when screen is being drawn - -- 1 INIT Called when screen is hard reset - -- 2 USE Called when screen is used - -- 3 ERROR Called when GPU error has occured - -- 4 ASYNC Asynchronous thread entrypoint - self.EntryPoint0 = 0 - self.EntryPoint1 = self.EntryPoint1 or 0 - self.EntryPoint2 = 0 - self.EntryPoint3 = 0 - self.EntryPoint4 = 0 - self.EntryPoint5 = 0 - self.EntryPoint6 = 0 - self.EntryPoint7 = 0 - self:ResetColorCaches() - -- Is running asynchronous thread - self.ASYNC = 0 + -- Entrypoints to special calls + -- 0 DRAW Called when screen is being drawn + -- 1 INIT Called when screen is hard reset + -- 2 USE Called when screen is used + -- 3 ERROR Called when GPU error has occured + -- 4 ASYNC Asynchronous thread entrypoint + self.EntryPoint0 = 0 + self.EntryPoint1 = self.EntryPoint1 or 0 + self.EntryPoint2 = 0 + self.EntryPoint3 = 0 + self.EntryPoint4 = 0 + self.EntryPoint5 = 0 + self.EntryPoint6 = 0 + self.EntryPoint7 = 0 + self:ResetColorCaches() + -- Is running asynchronous thread + self.ASYNC = 0 - -- Has initialized already - self.INIT = 0 + -- Has initialized already + self.INIT = 0 - -- Reset async thread - self.AsyncState = nil + -- Reset async thread + self.AsyncState = nil end @@ -714,23 +714,23 @@ end -- Compute UV -------------------------------------------------------------------------------- function VM:ComputeTextureUV(vertex,u,v) - local texturesOnSide = math.floor(512/self.Memory[65521]) - local textureX = (1/texturesOnSide) * (self.Texture % texturesOnSide) - local textureY = (1/texturesOnSide) * math.floor(self.Texture / texturesOnSide) - - local uvStep = (1/512) - local du,dv = u,v - - if (self.Memory[65466] ~= 0) or (self.Memory[65465] ~= 1) then - local cu,cv = self.Memory[65464],self.Memory[65463] - local tu,tv = u-cu,v-cv - local angle,scale = self.Memory[65466],self.Memory[65465] - du = scale*(tu*math.cos(angle) - tv*math.sin(angle)) + cu + self.Memory[65462] - dv = scale*(tv*math.cos(angle) + tu*math.sin(angle)) + cv + self.Memory[65461] - end + local texturesOnSide = math.floor(512/self.Memory[65521]) + local textureX = (1/texturesOnSide) * (self.Texture % texturesOnSide) + local textureY = (1/texturesOnSide) * math.floor(self.Texture / texturesOnSide) + + local uvStep = (1/512) + local du,dv = u,v + + if (self.Memory[65466] ~= 0) or (self.Memory[65465] ~= 1) then + local cu,cv = self.Memory[65464],self.Memory[65463] + local tu,tv = u-cu,v-cv + local angle,scale = self.Memory[65466],self.Memory[65465] + du = scale*(tu*math.cos(angle) - tv*math.sin(angle)) + cu + self.Memory[65462] + dv = scale*(tv*math.cos(angle) + tu*math.sin(angle)) + cv + self.Memory[65461] + end - vertex.u = textureX+(1/texturesOnSide)*du*(1-2*uvStep)+uvStep - vertex.v = textureY+(1/texturesOnSide)*dv*(1-2*uvStep)+uvStep + vertex.u = textureX+(1/texturesOnSide)*du*(1-2*uvStep)+uvStep + vertex.v = textureY+(1/texturesOnSide)*dv*(1-2*uvStep)+uvStep end @@ -739,67 +739,67 @@ end -- Transform coordinates through coordinate pipe -------------------------------------------------------------------------------- function VM:CoordinateTransform(x,y) - -- Transformed coordinates - local tX = x - local tY = y - - -- Is rotation/scale register set - if (self.Memory[65482] ~= 0) or (self.Memory[65481] ~= 1) then - -- Centerpoint of rotation - local cX = self.Memory[65480] - local cY = self.Memory[65479] - - -- Calculate normalized direction to rotated point - local vD = math.sqrt((x-cX)^2+(y-cY)^2) + 1e-7 - local vX = x / vD - local vY = y / vD - - -- Calculate angle of rotation for the point - local A - if self.RAMSize == 65536 then A = math.atan2(vX,vY) -- Old GPU - else A = math.atan2(vY,vX) - end - - -- Rotate point by a certain angle - A = A + self.Memory[65482] - - -- Generate new coordinates - tX = cX + math.cos(A) * vD * self.Memory[65481] * self.Memory[65475] - tY = cY + math.sin(A) * vD * self.Memory[65481] * self.Memory[65474] - end - - -- Apply DMOVE offset - tX = tX + self.Memory[65484] - tY = tY + self.Memory[65483] - - if self.CoordinatePipe == 0 then - tX = self.ScreenWidth*(tX/512) - tY = self.ScreenHeight*(tY/512) - elseif self.CoordinatePipe == 1 then - tX = self.ScreenWidth*tX/self.Memory[65515] - tY = self.ScreenHeight*tY/self.Memory[65514] - elseif self.CoordinatePipe == 2 then - tX = tX*self.ScreenWidth - tY = tY*self.ScreenHeight - elseif self.CoordinatePipe == 3 then - tX = 0.5*self.ScreenWidth*(1+tX) - tY = 0.5*self.ScreenHeight*(1+tY) - elseif self.CoordinatePipe == 4 then - tX = 0.5*self.ScreenWidth+tX - tY = 0.5*self.ScreenHeight+tY - end - - -- Apply raster quality transform - local transformedCoordinate = { x = tX, y = tY} - local rasterQuality = self.Memory[65518] - - if rasterQuality > 0 then - local W,H = self.ScreenWidth/2,self.ScreenHeight/2 - transformedCoordinate.x = (tX-W)*(1-(rasterQuality/W))+W - transformedCoordinate.y = (tY-H)*(1-(rasterQuality/H))+H - end - - return transformedCoordinate + -- Transformed coordinates + local tX = x + local tY = y + + -- Is rotation/scale register set + if (self.Memory[65482] ~= 0) or (self.Memory[65481] ~= 1) then + -- Centerpoint of rotation + local cX = self.Memory[65480] + local cY = self.Memory[65479] + + -- Calculate normalized direction to rotated point + local vD = math.sqrt((x-cX)^2+(y-cY)^2) + 1e-7 + local vX = x / vD + local vY = y / vD + + -- Calculate angle of rotation for the point + local A + if self.RAMSize == 65536 then A = math.atan2(vX,vY) -- Old GPU + else A = math.atan2(vY,vX) + end + + -- Rotate point by a certain angle + A = A + self.Memory[65482] + + -- Generate new coordinates + tX = cX + math.cos(A) * vD * self.Memory[65481] * self.Memory[65475] + tY = cY + math.sin(A) * vD * self.Memory[65481] * self.Memory[65474] + end + + -- Apply DMOVE offset + tX = tX + self.Memory[65484] + tY = tY + self.Memory[65483] + + if self.CoordinatePipe == 0 then + tX = self.ScreenWidth*(tX/512) + tY = self.ScreenHeight*(tY/512) + elseif self.CoordinatePipe == 1 then + tX = self.ScreenWidth*tX/self.Memory[65515] + tY = self.ScreenHeight*tY/self.Memory[65514] + elseif self.CoordinatePipe == 2 then + tX = tX*self.ScreenWidth + tY = tY*self.ScreenHeight + elseif self.CoordinatePipe == 3 then + tX = 0.5*self.ScreenWidth*(1+tX) + tY = 0.5*self.ScreenHeight*(1+tY) + elseif self.CoordinatePipe == 4 then + tX = 0.5*self.ScreenWidth+tX + tY = 0.5*self.ScreenHeight+tY + end + + -- Apply raster quality transform + local transformedCoordinate = { x = tX, y = tY} + local rasterQuality = self.Memory[65518] + + if rasterQuality > 0 then + local W,H = self.ScreenWidth/2,self.ScreenHeight/2 + transformedCoordinate.x = (tX-W)*(1-(rasterQuality/W))+W + transformedCoordinate.y = (tY-H)*(1-(rasterQuality/H))+H + end + + return transformedCoordinate end @@ -809,119 +809,119 @@ end -- Transform coordinate via vertex pipe -------------------------------------------------------------------------------- function VM:VertexTransform(inVertex,toScreen) - -- Make sure the coordinate is complete - local vertex = inVertex or {} - vertex.x = vertex.x or 0 - vertex.y = vertex.y or 0 - vertex.z = vertex.z or 0 - vertex.w = vertex.w or 1 - vertex.u = vertex.u or 0 - vertex.v = vertex.v or 0 - - -- Create the resulting coordinate - local resultVertex = { - x = vertex.x, - y = vertex.y, - z = vertex.z, - w = vertex.w, - u = vertex.u, - v = vertex.v } - - -- Transformed world coordinates - local worldVertex = { - x = 0, - y = 0, - z = 0, - w = 1, - u = vertex.u, - v = vertex.v } - - -- Add Z offset to input coordinate - vertex.z = vertex.z + self.Memory[65472] - - -- Do the transformation - if self.VertexPipe == 0 then -- XY plane - local resultCoordinate = self:CoordinateTransform(vertex.x,vertex.y) - resultVertex.x = resultCoordinate.x - resultVertex.y = resultCoordinate.y - elseif self.VertexPipe == 1 then -- YZ plane - local resultCoordinate = self:CoordinateTransform(vertex.y,vertex.z) - resultVertex.x = resultCoordinate.x - resultVertex.y = resultCoordinate.y - elseif self.VertexPipe == 2 then -- XZ plane - local resultCoordinate = self:CoordinateTransform(vertex.x,vertex.z) - resultVertex.x = resultCoordinate.x - resultVertex.y = resultCoordinate.y - elseif self.VertexPipe == 3 then -- Perspective transform - local tX = (vertex.x + self.Memory[65512])/(vertex.z + self.Memory[65512]) - local tY = (vertex.y + self.Memory[65512])/(vertex.z + self.Memory[65512]) - - local resultCoordinate = self:CoordinateTransform(tX,tY) - resultVertex.x = resultCoordinate.x - resultVertex.y = resultCoordinate.y - elseif self.VertexPipe == 4 then -- 2D matrix - local tX = self.ModelMatrix[0*4+0] * vertex.x + - self.ModelMatrix[0*4+1] * vertex.y + - self.ModelMatrix[0*4+2] * 0 + - self.ModelMatrix[0*4+3] * 1 - - local tY = self.ModelMatrix[1*4+0] * vertex.x + - self.ModelMatrix[1*4+1] * vertex.y + - self.ModelMatrix[1*4+2] * 0 + - self.ModelMatrix[1*4+3] * 1 - - local resultCoordinate = self:CoordinateTransform(tX,tY) - resultVertex.x = resultCoordinate.x - resultVertex.y = resultCoordinate.y - elseif self.VertexPipe == 5 then -- 3D matrix - local world - if not toScreen then - -- Transform into world coordinates - world = {} - - for i=0,3 do - world[i] = self.ModelMatrix[i*4+0] * vertex.x + - self.ModelMatrix[i*4+1] * vertex.y + - self.ModelMatrix[i*4+2] * vertex.z + - self.ModelMatrix[i*4+3] * vertex.w - end - - worldVertex.x = world[0] - worldVertex.y = world[1] - worldVertex.z = world[2] - worldVertex.w = world[3] - else - worldVertex = vertex - world = {} - world[0] = vertex.x - world[1] = vertex.y - world[2] = vertex.z - world[3] = vertex.w - end - - -- Transform into screen coordinates - local screen = {} - - for i=0,3 do - screen[i] = self.ProjectionMatrix[i*4+0] * world[0] + - self.ProjectionMatrix[i*4+1] * world[1] + - self.ProjectionMatrix[i*4+2] * world[2] + - self.ProjectionMatrix[i*4+3] * world[3] - end - - -- Project to screen - if screen[3] == 0 then screen[3] = 1 end - for i=0,3 do screen[i] = screen[i] / screen[3] end - - -- Transform coordinates - local resultCoordinate = self:CoordinateTransform(screen[0],screen[1]) - resultVertex.x = resultCoordinate.x - resultVertex.y = resultCoordinate.y - resultVertex.z = screen[2] - resultVertex.w = screen[3] - end - - return resultVertex,worldVertex + -- Make sure the coordinate is complete + local vertex = inVertex or {} + vertex.x = vertex.x or 0 + vertex.y = vertex.y or 0 + vertex.z = vertex.z or 0 + vertex.w = vertex.w or 1 + vertex.u = vertex.u or 0 + vertex.v = vertex.v or 0 + + -- Create the resulting coordinate + local resultVertex = { + x = vertex.x, + y = vertex.y, + z = vertex.z, + w = vertex.w, + u = vertex.u, + v = vertex.v } + + -- Transformed world coordinates + local worldVertex = { + x = 0, + y = 0, + z = 0, + w = 1, + u = vertex.u, + v = vertex.v } + + -- Add Z offset to input coordinate + vertex.z = vertex.z + self.Memory[65472] + + -- Do the transformation + if self.VertexPipe == 0 then -- XY plane + local resultCoordinate = self:CoordinateTransform(vertex.x,vertex.y) + resultVertex.x = resultCoordinate.x + resultVertex.y = resultCoordinate.y + elseif self.VertexPipe == 1 then -- YZ plane + local resultCoordinate = self:CoordinateTransform(vertex.y,vertex.z) + resultVertex.x = resultCoordinate.x + resultVertex.y = resultCoordinate.y + elseif self.VertexPipe == 2 then -- XZ plane + local resultCoordinate = self:CoordinateTransform(vertex.x,vertex.z) + resultVertex.x = resultCoordinate.x + resultVertex.y = resultCoordinate.y + elseif self.VertexPipe == 3 then -- Perspective transform + local tX = (vertex.x + self.Memory[65512])/(vertex.z + self.Memory[65512]) + local tY = (vertex.y + self.Memory[65512])/(vertex.z + self.Memory[65512]) + + local resultCoordinate = self:CoordinateTransform(tX,tY) + resultVertex.x = resultCoordinate.x + resultVertex.y = resultCoordinate.y + elseif self.VertexPipe == 4 then -- 2D matrix + local tX = self.ModelMatrix[0*4+0] * vertex.x + + self.ModelMatrix[0*4+1] * vertex.y + + self.ModelMatrix[0*4+2] * 0 + + self.ModelMatrix[0*4+3] * 1 + + local tY = self.ModelMatrix[1*4+0] * vertex.x + + self.ModelMatrix[1*4+1] * vertex.y + + self.ModelMatrix[1*4+2] * 0 + + self.ModelMatrix[1*4+3] * 1 + + local resultCoordinate = self:CoordinateTransform(tX,tY) + resultVertex.x = resultCoordinate.x + resultVertex.y = resultCoordinate.y + elseif self.VertexPipe == 5 then -- 3D matrix + local world + if not toScreen then + -- Transform into world coordinates + world = {} + + for i=0,3 do + world[i] = self.ModelMatrix[i*4+0] * vertex.x + + self.ModelMatrix[i*4+1] * vertex.y + + self.ModelMatrix[i*4+2] * vertex.z + + self.ModelMatrix[i*4+3] * vertex.w + end + + worldVertex.x = world[0] + worldVertex.y = world[1] + worldVertex.z = world[2] + worldVertex.w = world[3] + else + worldVertex = vertex + world = {} + world[0] = vertex.x + world[1] = vertex.y + world[2] = vertex.z + world[3] = vertex.w + end + + -- Transform into screen coordinates + local screen = {} + + for i=0,3 do + screen[i] = self.ProjectionMatrix[i*4+0] * world[0] + + self.ProjectionMatrix[i*4+1] * world[1] + + self.ProjectionMatrix[i*4+2] * world[2] + + self.ProjectionMatrix[i*4+3] * world[3] + end + + -- Project to screen + if screen[3] == 0 then screen[3] = 1 end + for i=0,3 do screen[i] = screen[i] / screen[3] end + + -- Transform coordinates + local resultCoordinate = self:CoordinateTransform(screen[0],screen[1]) + resultVertex.x = resultCoordinate.x + resultVertex.y = resultCoordinate.y + resultVertex.z = screen[2] + resultVertex.w = screen[3] + end + + return resultVertex,worldVertex end @@ -931,10 +931,10 @@ end -- Transform color -------------------------------------------------------------------------------- function VM:ColorTransform(color) - color.x = color.x * self.Memory[65495] * self.Memory[65494] - color.y = color.y * self.Memory[65495] * self.Memory[65493] - color.z = color.z * self.Memory[65495] * self.Memory[65492] - return color + color.x = color.x * self.Memory[65495] * self.Memory[65494] + color.y = color.y * self.Memory[65495] * self.Memory[65493] + color.z = color.z * self.Memory[65495] * self.Memory[65492] + return color end @@ -944,29 +944,29 @@ end -- Read a string by offset -------------------------------------------------------------------------------- function VM:ReadString(address) - local charString = "" - local charCount = 0 - local currentChar = 255 - - while currentChar ~= 0 do - currentChar = self:ReadCell(address + charCount) - -- Reading failed - if not currentChar then - return - elseif currentChar > 0 and currentChar < 255 then - charString = charString .. string.char(currentChar) - elseif currentChar ~= 0 then - self:Interrupt(23,currentChar) - return "" - end - - charCount = charCount + 1 - if charCount > 8192 then - self:Interrupt(23,0) - return "" - end - end - return charString + local charString = "" + local charCount = 0 + local currentChar = 255 + + while currentChar ~= 0 do + currentChar = self:ReadCell(address + charCount) + -- Reading failed + if not currentChar then + return + elseif currentChar > 0 and currentChar < 255 then + charString = charString .. string.char(currentChar) + elseif currentChar ~= 0 then + self:Interrupt(23,currentChar) + return "" + end + + charCount = charCount + 1 + if charCount > 8192 then + self:Interrupt(23,0) + return "" + end + end + return charString end @@ -976,13 +976,13 @@ end -- Get text size (by sk89q) -------------------------------------------------------------------------------- function VM:TextSize(text) - self:SetFont() + self:SetFont() - if self.WordWrapMode == 1 then - return self.Layouter:GetTextSize(text, self.TextBox.x, self.TextBox.y) - else - return surface.GetTextSize(text) - end + if self.WordWrapMode == 1 then + return self.Layouter:GetTextSize(text, self.TextBox.x, self.TextBox.y) + else + return surface.GetTextSize(text) + end end @@ -992,26 +992,26 @@ end -- Output font to screen (word wrap update by sk89q) -------------------------------------------------------------------------------- function VM:FontWrite(posaddr,text) - -- Read position - local vertex = {} - vertex.x = self:ReadCell(posaddr+0) - vertex.y = self:ReadCell(posaddr+1) - vertex = self:VertexTransform(vertex) - - self:SetFont() - - -- Draw text - if self.RenderEnable == 1 then - if self.WordWrapMode == 1 then - surface.SetTextColor(self.Color.x,self.Color.y,self.Color.z,self.Color.w) - self.Layouter:DrawText(tostring(text), vertex.x, vertex.y, self.TextBox.x, - self.TextBox.y, self.Memory[65473], self.Memory[65471]) - else - draw.DrawText(text,"WireGPU_"..self.FontName[self.Font]..self.FontSize, - vertex.x,vertex.y,Color(self.Color.x,self.Color.y,self.Color.z,self.Color.w), - self.Memory[65473]) - end - end + -- Read position + local vertex = {} + vertex.x = self:ReadCell(posaddr+0) + vertex.y = self:ReadCell(posaddr+1) + vertex = self:VertexTransform(vertex) + + self:SetFont() + + -- Draw text + if self.RenderEnable == 1 then + if self.WordWrapMode == 1 then + surface.SetTextColor(self.Color.x,self.Color.y,self.Color.z,self.Color.w) + self.Layouter:DrawText(tostring(text), vertex.x, vertex.y, self.TextBox.x, + self.TextBox.y, self.Memory[65473], self.Memory[65471]) + else + draw.DrawText(text,"WireGPU_"..self.FontName[self.Font]..self.FontSize, + vertex.x,vertex.y,Color(self.Color.x,self.Color.y,self.Color.z,self.Color.w), + self.Memory[65473]) + end + end end @@ -1021,49 +1021,49 @@ end -- Draw line between two points -------------------------------------------------------------------------------- function VM:DrawLine(point1,point2,drawNow) - -- Line centerpoint - local cX = (point1.x + point2.x) / 2 - local cY = (point1.y + point2.y) / 2 - - -- Line width - local W = self.Memory[65476] - - -- Line length and angle - local L = math.sqrt((point1.x-point2.x)^2+(point1.y-point2.y)^2) + 1e-7 - local dX = (point2.x-point1.x) / L - local dY = (point2.y-point1.y) / L - local A = math.atan2(dY,dX) - local dA = math.atan2(W,L/2) - - -- Generate vertexes - local vertexBuffer = { {}, {}, {}, {} } - - vertexBuffer[1].x = cX - 0.5 * L * math.cos(A-dA) - vertexBuffer[1].y = cY - 0.5 * L * math.sin(A-dA) - vertexBuffer[1].u = 0 - vertexBuffer[1].v = 0 - - vertexBuffer[2].x = cX + 0.5 * L * math.cos(A+dA) - vertexBuffer[2].y = cY + 0.5 * L * math.sin(A+dA) - vertexBuffer[2].u = 1 - vertexBuffer[2].v = 1 - - vertexBuffer[3].x = cX + 0.5 * L * math.cos(A-dA) - vertexBuffer[3].y = cY + 0.5 * L * math.sin(A-dA) - vertexBuffer[3].u = 0 - vertexBuffer[3].v = 1 - - vertexBuffer[4].x = cX - 0.5 * L * math.cos(A+dA) - vertexBuffer[4].y = cY - 0.5 * L * math.sin(A+dA) - vertexBuffer[4].u = 1 - vertexBuffer[4].v = 0 - - -- Draw vertexes - if drawNow then - surface.DrawPoly(vertexBuffer) - else - self:DrawToBuffer(vertexBuffer) - end + -- Line centerpoint + local cX = (point1.x + point2.x) / 2 + local cY = (point1.y + point2.y) / 2 + + -- Line width + local W = self.Memory[65476] + + -- Line length and angle + local L = math.sqrt((point1.x-point2.x)^2+(point1.y-point2.y)^2) + 1e-7 + local dX = (point2.x-point1.x) / L + local dY = (point2.y-point1.y) / L + local A = math.atan2(dY,dX) + local dA = math.atan2(W,L/2) + + -- Generate vertexes + local vertexBuffer = { {}, {}, {}, {} } + + vertexBuffer[1].x = cX - 0.5 * L * math.cos(A-dA) + vertexBuffer[1].y = cY - 0.5 * L * math.sin(A-dA) + vertexBuffer[1].u = 0 + vertexBuffer[1].v = 0 + + vertexBuffer[2].x = cX + 0.5 * L * math.cos(A+dA) + vertexBuffer[2].y = cY + 0.5 * L * math.sin(A+dA) + vertexBuffer[2].u = 1 + vertexBuffer[2].v = 1 + + vertexBuffer[3].x = cX + 0.5 * L * math.cos(A-dA) + vertexBuffer[3].y = cY + 0.5 * L * math.sin(A-dA) + vertexBuffer[3].u = 0 + vertexBuffer[3].v = 1 + + vertexBuffer[4].x = cX - 0.5 * L * math.cos(A+dA) + vertexBuffer[4].y = cY - 0.5 * L * math.sin(A+dA) + vertexBuffer[4].u = 1 + vertexBuffer[4].v = 0 + + -- Draw vertexes + if drawNow then + surface.DrawPoly(vertexBuffer) + else + self:DrawToBuffer(vertexBuffer) + end end @@ -1073,339 +1073,339 @@ end -- Flush vertex buffer. Based on code by Nick -------------------------------------------------------------------------------- local function triangleSortFunction(triA,triB) - local z1 = (triA.vertex[1].z + triA.vertex[2].z + triA.vertex[3].z) / 3 - local z2 = (triB.vertex[1].z + triB.vertex[2].z + triB.vertex[3].z) / 3 + local z1 = (triA.vertex[1].z + triA.vertex[2].z + triA.vertex[3].z) / 3 + local z2 = (triB.vertex[1].z + triB.vertex[2].z + triB.vertex[3].z) / 3 - return z1 < z2 + return z1 < z2 end function VM:FlushBuffer() - -- Projected vertex data: - -- vertexData.transformedVertex [SCREEN SPACE] - -- Vertex data in world space: - -- vertexData.vertex [WORLD SPACE] - -- Triangle color: - -- vertexData.color - -- - -- Light positions: [WORLD SPACE] - - if self.VertexBufEnabled == 1 then - -- Do not flush color-only buffer - if (#self.VertexBuffer == 1) and (not self.VertexBuffer[1].vertex) then - self.VertexBuffer = {} - return - end - - -- Sort triangles by distance - if self.VertexBufZSort == 1 then - table.sort(self.VertexBuffer,triangleSortFunction) - end - - -- Render each triangle - for vertexID,vertexData in ipairs(self.VertexBuffer) do - -- Should this polygon be culled - local cullVertex = false - - -- Generate output - local resultTriangle - local resultTriangle2 - local resultColor = { - x = vertexData.color.x, - y = vertexData.color.y, - z = vertexData.color.z, - w = vertexData.color.w, - } - local resultMaterial = vertexData.material - if vertexData.rt then - WireGPU_matBuffer:SetTexture("$basetexture", vertexData.rt) - resultMaterial = WireGPU_matBuffer - end - - - if vertexData.vertex then - resultTriangle = {} - - resultTriangle[1] = {} - resultTriangle[1].x = vertexData.transformedVertex[1].x - resultTriangle[1].y = vertexData.transformedVertex[1].y - resultTriangle[1].u = vertexData.transformedVertex[1].u - resultTriangle[1].v = vertexData.transformedVertex[1].v - - resultTriangle[2] = {} - resultTriangle[2].x = vertexData.transformedVertex[2].x - resultTriangle[2].y = vertexData.transformedVertex[2].y - resultTriangle[2].u = vertexData.transformedVertex[2].u - resultTriangle[2].v = vertexData.transformedVertex[2].v - - resultTriangle[3] = {} - resultTriangle[3].x = vertexData.transformedVertex[3].x - resultTriangle[3].y = vertexData.transformedVertex[3].y - resultTriangle[3].u = vertexData.transformedVertex[3].u - resultTriangle[3].v = vertexData.transformedVertex[3].v - - -- Additional processing - if (self.VertexCulling == 1) or (self.VertexLighting == 1) then - -- Get vertices (world space) - local v1 = vertexData.vertex[1] - local v2 = vertexData.vertex[2] - local v3 = vertexData.vertex[3] - - -- Compute barycenter (world space) - local vpos = { - x = (v1.x+v2.x) * 1/3, - y = (v1.y+v2.y) * 1/3, - z = (v1.z+v2.z) * 1/3 - } - - -- Compute normal (world space) - local x1 = v2.x - v1.x - local y1 = v2.y - v1.y - local z1 = v2.z - v1.z - - local x2 = v3.x - v1.x - local y2 = v3.y - v1.y - local z2 = v3.z - v1.z - - local normal = { - x = y1*z2-y2*z1, - y = z1*x2-z2*x1, - z = x1*y2-x2*y1 - } - - -- Normalize it - local d = (normal.x^2 + normal.y^2 + normal.z^2)^(1/2)+1e-7 - normal.x = normal.x / d - normal.y = normal.y / d - normal.z = normal.z / d - - -- Perform culling - if self.VertexCulling == 1 then - if self.Memory[65469] == 0 then - cullVertex = (normal.x*v1.x + normal.y*v1.y + normal.z*v1.z) <= 0 - else - cullVertex = (normal.x*v1.x + normal.y*v1.y + normal.z*v1.z) >= 0 - end - end - - -- Perform vertex lighting - if (self.VertexLighting == 1) and (not cullVertex) then - -- Extra color generated by lights - local lightColor = { x = 0, y = 0, z = 0, w = 255} - - -- Apply all lights (world space calculations) - for i=0,7 do - if self.Lights[i] then - local lightPosition = { - x = self.Lights[i].Position.x, - y = self.Lights[i].Position.y, - z = self.Lights[i].Position.z - } - local lightLength = (lightPosition.x^2+lightPosition.y^2+lightPosition.z^2)^(1/2)+1e-7 - lightPosition.x = lightPosition.x / lightLength - lightPosition.y = lightPosition.y / lightLength - lightPosition.z = lightPosition.z / lightLength - - local lightDot - if self.Memory[65468] == 0 then - lightDot = math.abs(lightPosition.x*normal.x + - lightPosition.y*normal.y + - lightPosition.z*normal.z)*self.Lights[i].Color.w - else - lightDot = math.max(0,self.Memory[65468]*(lightPosition.x*normal.x + - lightPosition.y*normal.y + - lightPosition.z*normal.z))*self.Lights[i].Color.w - - end - - lightColor.x = math.min(lightColor.x + self.Lights[i].Color.x * lightDot,255) - lightColor.y = math.min(lightColor.y + self.Lights[i].Color.y * lightDot,255) - lightColor.z = math.min(lightColor.z + self.Lights[i].Color.z * lightDot,255) - end - end - - -- Modulate object color with light color - resultColor.x = (1/255) * resultColor.x * lightColor.x - resultColor.y = (1/255) * resultColor.y * lightColor.y - resultColor.z = (1/255) * resultColor.z * lightColor.z - end - - -- Perform distance culling - if (self.DistanceCulling == 1) and (not cullVertex) then - local Infront = {} - local Behind = {} - - local frontCullDistance = self.Memory[65470] - local K = -frontCullDistance - - -- Generate list of vertices which go behind the camera - if v1.z - K >= 0 - then Behind [#Behind + 1] = v1 - else Infront[#Infront + 1] = v1 - end - - if v2.z - K >= 0 - then Behind [#Behind + 1] = v2 - else Infront[#Infront + 1] = v2 - end - - if v3.z - K >= 0 - then Behind [#Behind + 1] = v3 - else Infront[#Infront + 1] = v3 - end - - if #Behind == 1 then - local Point1 = Infront[1] - local Point2 = Infront[2] - local Point3 = Behind[1] - local Point4 = {} - - local D1 = { - x = Point3.x - Point1.x, - y = Point3.y - Point1.y, - z = Point3.z - Point1.z, - } - local D2 = { - x = Point3.x - Point2.x, - y = Point3.y - Point2.y, - z = Point3.z - Point2.z, - } - - local T1 = D1.z - local T2 = D2.z - - if (T1 ~= 0) and (T2 ~= 0) then - local S1 = (K - Point1.z)/T1 - local S2 = (K - Point2.z)/T2 - - -- Calculate the new UV values - Point4.u = Point2.u + S2 * (Point3.u - Point2.u) - Point4.v = Point2.v + S2 * (Point3.v - Point2.v) - - Point3.u = Point1.u + S1 * (Point3.u - Point1.u) - Point3.v = Point1.v + S1 * (Point3.v - Point1.v) - - -- Calculate new coordinates - Point3.x = Point1.x + S1 * D1.x - Point3.y = Point1.y + S1 * D1.y - Point3.z = Point1.z + S1 * D1.z - - Point4.x = Point2.x + S2 * D2.x - Point4.y = Point2.y + S2 * D2.y - Point4.z = Point2.z + S2 * D2.z - - -- Transform the points (from world space to screen space) - local P1t = self:VertexTransform(Point1,true) - local P2t = self:VertexTransform(Point2,true) - local P3t = self:VertexTransform(Point3,true) - local P4t = self:VertexTransform(Point4,true) - - resultTriangle[1] = P1t - resultTriangle[2] = P2t - resultTriangle[3] = P3t - - resultTriangle2 = {} - resultTriangle2[1] = P2t - resultTriangle2[2] = P3t - resultTriangle2[3] = P4t - end - elseif #Behind == 2 then - local Point1 = Infront[1] - local Point2 = Behind[1] - local Point3 = Behind[2] - - local D1 = { - x = Point2.x - Point1.x, - y = Point2.y - Point1.y, - z = Point2.z - Point1.z, - } - local D2 = { - x = Point3.x - Point1.x, - y = Point3.y - Point1.y, - z = Point3.z - Point1.z, - } - - local T1 = D1.z - local T2 = D2.z - - if (T1 ~= 0) and (T2 ~= 0) then - local S1 = (K - Point1.z)/T1 - local S2 = (K - Point1.z)/T2 - - --Calculate the new UV values - Point2.u = Point1.u + S1 * (Point2.u - Point1.u) - Point2.v = Point1.v + S1 * (Point2.v - Point1.v) - - Point3.u = Point1.u + S2 * (Point3.u - Point1.u) - Point3.v = Point1.v + S2 * (Point3.v - Point1.v) - - -- Calculate new coordinates - Point2.x = Point1.x + S1 * D1.x - Point2.y = Point1.y + S1 * D1.y - Point2.z = Point1.z + S1 * D1.z - - Point3.x = Point1.x + S2 * D2.x - Point3.y = Point1.y + S2 * D2.y - Point3.z = Point1.z + S2 * D2.z - - -- Transform the points (from world space to screen space) - local P1t = self:VertexTransform(Point1,true) - local P2t = self:VertexTransform(Point2,true) - local P3t = self:VertexTransform(Point3,true) - - resultTriangle[1] = P1t - resultTriangle[2] = P2t - resultTriangle[3] = P3t - end - elseif #Behind == 3 then - cullVertex = true - end - end - end -- End additional processing - end - - - if not cullVertex then - -- self:FixDrawDirection(DrawInfo) - if self.RenderEnable == 1 then - if resultMaterial then - surface.SetMaterial(resultMaterial) - resultTriangle = { - [1] = resultTriangle[3], - [2] = resultTriangle[2], - [3] = resultTriangle[1], - } - else - surface.SetTexture(0) - end - surface.SetDrawColor(resultColor.x,resultColor.y,resultColor.z,resultColor.w) - if vertexData.wireframe then - if resultTriangle then - for i=1,#resultTriangle do - local point1 = resultTriangle[i] - local point2 = resultTriangle[i+1] - if not point2 then point2 = resultTriangle[1] end - self:DrawLine(point1,point2,true) - end - end - - if resultTriangle2 then - for i=1,#resultTriangle2 do - local point1 = resultTriangle2[i] - local point2 = resultTriangle2[i+1] - if not point2 then point2 = resultTriangle2[1] end - self:DrawLine(point1,point2,true) - end - end - else - if resultTriangle then surface.DrawPoly(resultTriangle) end - if resultTriangle2 then surface.DrawPoly(resultTriangle2) end - end - end - end - end - - self.VertexBuffer = {} - end + -- Projected vertex data: + -- vertexData.transformedVertex [SCREEN SPACE] + -- Vertex data in world space: + -- vertexData.vertex [WORLD SPACE] + -- Triangle color: + -- vertexData.color + -- + -- Light positions: [WORLD SPACE] + + if self.VertexBufEnabled == 1 then + -- Do not flush color-only buffer + if (#self.VertexBuffer == 1) and (not self.VertexBuffer[1].vertex) then + self.VertexBuffer = {} + return + end + + -- Sort triangles by distance + if self.VertexBufZSort == 1 then + table.sort(self.VertexBuffer,triangleSortFunction) + end + + -- Render each triangle + for vertexID,vertexData in ipairs(self.VertexBuffer) do + -- Should this polygon be culled + local cullVertex = false + + -- Generate output + local resultTriangle + local resultTriangle2 + local resultColor = { + x = vertexData.color.x, + y = vertexData.color.y, + z = vertexData.color.z, + w = vertexData.color.w, + } + local resultMaterial = vertexData.material + if vertexData.rt then + WireGPU_matBuffer:SetTexture("$basetexture", vertexData.rt) + resultMaterial = WireGPU_matBuffer + end + + + if vertexData.vertex then + resultTriangle = {} + + resultTriangle[1] = {} + resultTriangle[1].x = vertexData.transformedVertex[1].x + resultTriangle[1].y = vertexData.transformedVertex[1].y + resultTriangle[1].u = vertexData.transformedVertex[1].u + resultTriangle[1].v = vertexData.transformedVertex[1].v + + resultTriangle[2] = {} + resultTriangle[2].x = vertexData.transformedVertex[2].x + resultTriangle[2].y = vertexData.transformedVertex[2].y + resultTriangle[2].u = vertexData.transformedVertex[2].u + resultTriangle[2].v = vertexData.transformedVertex[2].v + + resultTriangle[3] = {} + resultTriangle[3].x = vertexData.transformedVertex[3].x + resultTriangle[3].y = vertexData.transformedVertex[3].y + resultTriangle[3].u = vertexData.transformedVertex[3].u + resultTriangle[3].v = vertexData.transformedVertex[3].v + + -- Additional processing + if (self.VertexCulling == 1) or (self.VertexLighting == 1) then + -- Get vertices (world space) + local v1 = vertexData.vertex[1] + local v2 = vertexData.vertex[2] + local v3 = vertexData.vertex[3] + + -- Compute barycenter (world space) + local vpos = { + x = (v1.x+v2.x) * 1/3, + y = (v1.y+v2.y) * 1/3, + z = (v1.z+v2.z) * 1/3 + } + + -- Compute normal (world space) + local x1 = v2.x - v1.x + local y1 = v2.y - v1.y + local z1 = v2.z - v1.z + + local x2 = v3.x - v1.x + local y2 = v3.y - v1.y + local z2 = v3.z - v1.z + + local normal = { + x = y1*z2-y2*z1, + y = z1*x2-z2*x1, + z = x1*y2-x2*y1 + } + + -- Normalize it + local d = (normal.x^2 + normal.y^2 + normal.z^2)^(1/2)+1e-7 + normal.x = normal.x / d + normal.y = normal.y / d + normal.z = normal.z / d + + -- Perform culling + if self.VertexCulling == 1 then + if self.Memory[65469] == 0 then + cullVertex = (normal.x*v1.x + normal.y*v1.y + normal.z*v1.z) <= 0 + else + cullVertex = (normal.x*v1.x + normal.y*v1.y + normal.z*v1.z) >= 0 + end + end + + -- Perform vertex lighting + if (self.VertexLighting == 1) and (not cullVertex) then + -- Extra color generated by lights + local lightColor = { x = 0, y = 0, z = 0, w = 255} + + -- Apply all lights (world space calculations) + for i=0,7 do + if self.Lights[i] then + local lightPosition = { + x = self.Lights[i].Position.x, + y = self.Lights[i].Position.y, + z = self.Lights[i].Position.z + } + local lightLength = (lightPosition.x^2+lightPosition.y^2+lightPosition.z^2)^(1/2)+1e-7 + lightPosition.x = lightPosition.x / lightLength + lightPosition.y = lightPosition.y / lightLength + lightPosition.z = lightPosition.z / lightLength + + local lightDot + if self.Memory[65468] == 0 then + lightDot = math.abs(lightPosition.x*normal.x + + lightPosition.y*normal.y + + lightPosition.z*normal.z)*self.Lights[i].Color.w + else + lightDot = math.max(0,self.Memory[65468]*(lightPosition.x*normal.x + + lightPosition.y*normal.y + + lightPosition.z*normal.z))*self.Lights[i].Color.w + + end + + lightColor.x = math.min(lightColor.x + self.Lights[i].Color.x * lightDot,255) + lightColor.y = math.min(lightColor.y + self.Lights[i].Color.y * lightDot,255) + lightColor.z = math.min(lightColor.z + self.Lights[i].Color.z * lightDot,255) + end + end + + -- Modulate object color with light color + resultColor.x = (1/255) * resultColor.x * lightColor.x + resultColor.y = (1/255) * resultColor.y * lightColor.y + resultColor.z = (1/255) * resultColor.z * lightColor.z + end + + -- Perform distance culling + if (self.DistanceCulling == 1) and (not cullVertex) then + local Infront = {} + local Behind = {} + + local frontCullDistance = self.Memory[65470] + local K = -frontCullDistance + + -- Generate list of vertices which go behind the camera + if v1.z - K >= 0 + then Behind [#Behind + 1] = v1 + else Infront[#Infront + 1] = v1 + end + + if v2.z - K >= 0 + then Behind [#Behind + 1] = v2 + else Infront[#Infront + 1] = v2 + end + + if v3.z - K >= 0 + then Behind [#Behind + 1] = v3 + else Infront[#Infront + 1] = v3 + end + + if #Behind == 1 then + local Point1 = Infront[1] + local Point2 = Infront[2] + local Point3 = Behind[1] + local Point4 = {} + + local D1 = { + x = Point3.x - Point1.x, + y = Point3.y - Point1.y, + z = Point3.z - Point1.z, + } + local D2 = { + x = Point3.x - Point2.x, + y = Point3.y - Point2.y, + z = Point3.z - Point2.z, + } + + local T1 = D1.z + local T2 = D2.z + + if (T1 ~= 0) and (T2 ~= 0) then + local S1 = (K - Point1.z)/T1 + local S2 = (K - Point2.z)/T2 + + -- Calculate the new UV values + Point4.u = Point2.u + S2 * (Point3.u - Point2.u) + Point4.v = Point2.v + S2 * (Point3.v - Point2.v) + + Point3.u = Point1.u + S1 * (Point3.u - Point1.u) + Point3.v = Point1.v + S1 * (Point3.v - Point1.v) + + -- Calculate new coordinates + Point3.x = Point1.x + S1 * D1.x + Point3.y = Point1.y + S1 * D1.y + Point3.z = Point1.z + S1 * D1.z + + Point4.x = Point2.x + S2 * D2.x + Point4.y = Point2.y + S2 * D2.y + Point4.z = Point2.z + S2 * D2.z + + -- Transform the points (from world space to screen space) + local P1t = self:VertexTransform(Point1,true) + local P2t = self:VertexTransform(Point2,true) + local P3t = self:VertexTransform(Point3,true) + local P4t = self:VertexTransform(Point4,true) + + resultTriangle[1] = P1t + resultTriangle[2] = P2t + resultTriangle[3] = P3t + + resultTriangle2 = {} + resultTriangle2[1] = P2t + resultTriangle2[2] = P3t + resultTriangle2[3] = P4t + end + elseif #Behind == 2 then + local Point1 = Infront[1] + local Point2 = Behind[1] + local Point3 = Behind[2] + + local D1 = { + x = Point2.x - Point1.x, + y = Point2.y - Point1.y, + z = Point2.z - Point1.z, + } + local D2 = { + x = Point3.x - Point1.x, + y = Point3.y - Point1.y, + z = Point3.z - Point1.z, + } + + local T1 = D1.z + local T2 = D2.z + + if (T1 ~= 0) and (T2 ~= 0) then + local S1 = (K - Point1.z)/T1 + local S2 = (K - Point1.z)/T2 + + --Calculate the new UV values + Point2.u = Point1.u + S1 * (Point2.u - Point1.u) + Point2.v = Point1.v + S1 * (Point2.v - Point1.v) + + Point3.u = Point1.u + S2 * (Point3.u - Point1.u) + Point3.v = Point1.v + S2 * (Point3.v - Point1.v) + + -- Calculate new coordinates + Point2.x = Point1.x + S1 * D1.x + Point2.y = Point1.y + S1 * D1.y + Point2.z = Point1.z + S1 * D1.z + + Point3.x = Point1.x + S2 * D2.x + Point3.y = Point1.y + S2 * D2.y + Point3.z = Point1.z + S2 * D2.z + + -- Transform the points (from world space to screen space) + local P1t = self:VertexTransform(Point1,true) + local P2t = self:VertexTransform(Point2,true) + local P3t = self:VertexTransform(Point3,true) + + resultTriangle[1] = P1t + resultTriangle[2] = P2t + resultTriangle[3] = P3t + end + elseif #Behind == 3 then + cullVertex = true + end + end + end -- End additional processing + end + + + if not cullVertex then + -- self:FixDrawDirection(DrawInfo) + if self.RenderEnable == 1 then + if resultMaterial then + surface.SetMaterial(resultMaterial) + resultTriangle = { + [1] = resultTriangle[3], + [2] = resultTriangle[2], + [3] = resultTriangle[1], + } + else + surface.SetTexture(0) + end + surface.SetDrawColor(resultColor.x,resultColor.y,resultColor.z,resultColor.w) + if vertexData.wireframe then + if resultTriangle then + for i=1,#resultTriangle do + local point1 = resultTriangle[i] + local point2 = resultTriangle[i+1] + if not point2 then point2 = resultTriangle[1] end + self:DrawLine(point1,point2,true) + end + end + + if resultTriangle2 then + for i=1,#resultTriangle2 do + local point1 = resultTriangle2[i] + local point2 = resultTriangle2[i+1] + if not point2 then point2 = resultTriangle2[1] end + self:DrawLine(point1,point2,true) + end + end + else + if resultTriangle then surface.DrawPoly(resultTriangle) end + if resultTriangle2 then surface.DrawPoly(resultTriangle2) end + end + end + end + end + + self.VertexBuffer = {} + end end @@ -1415,17 +1415,17 @@ end -- Set current color -------------------------------------------------------------------------------- function VM:SetColor(color) - if self.VertexBufEnabled == 1 then - if #self.VertexBuffer > 0 then - self.VertexBuffer[#self.VertexBuffer].color = self:ColorTransform(color) - else - self.VertexBuffer[1] = { - color = self:ColorTransform(color), - } - end - end + if self.VertexBufEnabled == 1 then + if #self.VertexBuffer > 0 then + self.VertexBuffer[#self.VertexBuffer].color = self:ColorTransform(color) + else + self.VertexBuffer[1] = { + color = self:ColorTransform(color), + } + end + end - self.Color = self:ColorTransform(color) + self.Color = self:ColorTransform(color) end @@ -1435,17 +1435,17 @@ end -- Set current material -------------------------------------------------------------------------------- function VM:SetMaterial(material) - if self.VertexBufEnabled == 1 then - if #self.VertexBuffer > 0 then - self.VertexBuffer[#self.VertexBuffer].material = material - else - self.VertexBuffer[1] = { - material = material, - } - end - end + if self.VertexBufEnabled == 1 then + if #self.VertexBuffer > 0 then + self.VertexBuffer[#self.VertexBuffer].material = material + else + self.VertexBuffer[1] = { + material = material, + } + end + end - self.Material = material + self.Material = material end @@ -1454,27 +1454,27 @@ end -- Bind rendering state (color, texture) -------------------------------------------------------------------------------- function VM:BindState() - surface.SetDrawColor(self.Color.x,self.Color.y,self.Color.z,self.Color.w) - if self.VertexTexturing == 1 then - if self.Memory[65517] == 1 then - --[@entities\gmod_wire_gpu\cl_gpuvm.lua:1276] bad argument #2 to 'SetTexture' (ITexture expected, got nil) - self.Entity:AssertSpriteBufferExists() - if self.Entity.SpriteGPU.RT then - WireGPU_matBuffer:SetTexture("$basetexture", self.Entity.SpriteGPU.RT) - end - else - if self.Entity.GPU.RT then - WireGPU_matBuffer:SetTexture("$basetexture", self.Entity.GPU.RT) - end - end - surface.SetMaterial(WireGPU_matBuffer) - else - if self.Material then - surface.SetMaterial(self.Material) - else - surface.SetTexture(0) - end - end + surface.SetDrawColor(self.Color.x,self.Color.y,self.Color.z,self.Color.w) + if self.VertexTexturing == 1 then + if self.Memory[65517] == 1 then + --[@entities\gmod_wire_gpu\cl_gpuvm.lua:1276] bad argument #2 to 'SetTexture' (ITexture expected, got nil) + self.Entity:AssertSpriteBufferExists() + if self.Entity.SpriteGPU.RT then + WireGPU_matBuffer:SetTexture("$basetexture", self.Entity.SpriteGPU.RT) + end + else + if self.Entity.GPU.RT then + WireGPU_matBuffer:SetTexture("$basetexture", self.Entity.GPU.RT) + end + end + surface.SetMaterial(WireGPU_matBuffer) + else + if self.Material then + surface.SetMaterial(self.Material) + else + surface.SetTexture(0) + end + end end @@ -1483,63 +1483,63 @@ end -- Draw a buffer (or add it to vertex buffer) -------------------------------------------------------------------------------- function VM:DrawToBuffer(vertexData,isWireframe) - if self.VertexBufEnabled == 1 then - -- Add new entry - if (not self.VertexBuffer[#self.VertexBuffer]) or self.VertexBuffer[#self.VertexBuffer].vertex then - self.VertexBuffer[#self.VertexBuffer+1] = { - color = self.Color, - material = self.Material, - vertex = {}, - transformedVertex = {}, - wireframe = isWireframe, - } - else - self.VertexBuffer[#self.VertexBuffer].vertex = {} - self.VertexBuffer[#self.VertexBuffer].transformedVertex = {} - self.VertexBuffer[#self.VertexBuffer].wireframe = isWireframe - end - - -- Add RT material if required - if self.VertexTexturing == 1 then - if self.Memory[65517] == 1 then - self.Entity:AssertSpriteBufferExists() - self.VertexBuffer[#self.VertexBuffer].rt = self.Entity.SpriteGPU.RT - else - self.VertexBuffer[#self.VertexBuffer].rt = self.Entity.GPU.RT - end - end - - - -- Add all vertices - for _,vertex in ipairs(vertexData) do - local screenVertex,worldVertex = self:VertexTransform(vertex) - table.insert(self.VertexBuffer[#self.VertexBuffer].vertex,worldVertex) - table.insert(self.VertexBuffer[#self.VertexBuffer].transformedVertex,screenVertex) - end - else - local resultPoly = {} - - -- Transform vertices - for _,vertex in ipairs(vertexData) do - local screenVertex,worldVertex = self:VertexTransform(vertex) - table.insert(resultPoly,screenVertex) - end - - -- Draw - if self.RenderEnable == 1 then - self:BindState() - if isWireframe then - for i=1,#resultPoly do - local point1 = resultPoly[i] - local point2 = resultPoly[i+1] - if not point2 then point2 = resultPoly[1] end - self:DrawLine(point1,point2,true) - end - else - surface.DrawPoly(resultPoly) - end - end - end + if self.VertexBufEnabled == 1 then + -- Add new entry + if (not self.VertexBuffer[#self.VertexBuffer]) or self.VertexBuffer[#self.VertexBuffer].vertex then + self.VertexBuffer[#self.VertexBuffer+1] = { + color = self.Color, + material = self.Material, + vertex = {}, + transformedVertex = {}, + wireframe = isWireframe, + } + else + self.VertexBuffer[#self.VertexBuffer].vertex = {} + self.VertexBuffer[#self.VertexBuffer].transformedVertex = {} + self.VertexBuffer[#self.VertexBuffer].wireframe = isWireframe + end + + -- Add RT material if required + if self.VertexTexturing == 1 then + if self.Memory[65517] == 1 then + self.Entity:AssertSpriteBufferExists() + self.VertexBuffer[#self.VertexBuffer].rt = self.Entity.SpriteGPU.RT + else + self.VertexBuffer[#self.VertexBuffer].rt = self.Entity.GPU.RT + end + end + + + -- Add all vertices + for _,vertex in ipairs(vertexData) do + local screenVertex,worldVertex = self:VertexTransform(vertex) + table.insert(self.VertexBuffer[#self.VertexBuffer].vertex,worldVertex) + table.insert(self.VertexBuffer[#self.VertexBuffer].transformedVertex,screenVertex) + end + else + local resultPoly = {} + + -- Transform vertices + for _,vertex in ipairs(vertexData) do + local screenVertex,worldVertex = self:VertexTransform(vertex) + table.insert(resultPoly,screenVertex) + end + + -- Draw + if self.RenderEnable == 1 then + self:BindState() + if isWireframe then + for i=1,#resultPoly do + local point1 = resultPoly[i] + local point2 = resultPoly[i+1] + if not point2 then point2 = resultPoly[1] end + self:DrawLine(point1,point2,true) + end + else + surface.DrawPoly(resultPoly) + end + end + end end @@ -1555,970 +1555,970 @@ end -------------------------------------------------------------------------------- VM.OpcodeTable = {} VM.OpcodeTable[98] = function(self) --TIMER - self:Dyn_Emit("if VM.ASYNC == 1 then") - self:Dyn_EmitOperand(1,"(VM.TIMER+"..(self.PrecompileInstruction or 0).."*VM.TimerDT)",true) - self:Dyn_Emit("else") - self:Dyn_EmitOperand(1,"VM.TIMER",true) - self:Dyn_Emit("end") + self:Dyn_Emit("if VM.ASYNC == 1 then") + self:Dyn_EmitOperand(1,"(VM.TIMER+"..(self.PrecompileInstruction or 0).."*VM.TimerDT)",true) + self:Dyn_Emit("else") + self:Dyn_EmitOperand(1,"VM.TIMER",true) + self:Dyn_Emit("end") end VM.OpcodeTable[111] = function(self) --IDLE - self:Dyn_Emit("VM.INTR = 1") - self:Dyn_EmitBreak() - self.PrecompileBreak = true + self:Dyn_Emit("VM.INTR = 1") + self:Dyn_EmitBreak() + self.PrecompileBreak = true end -------------------------------------------------------------------------------- VM.OpcodeTable[200] = function(self) --DRECT_TEST - self:Dyn_Emit("if VM.RenderEnable == 1 then") - self:Dyn_Emit("$L W = VM.ScreenWidth") - self:Dyn_Emit("$L H = VM.ScreenHeight") - - self:Dyn_Emit("surface.SetTexture(0)") - self:Dyn_Emit("surface.SetDrawColor(200,200,200,255)") - self:Dyn_Emit("surface.DrawRect(W*0.125*0,0,W*0.125,H*0.80)") - self:Dyn_Emit("surface.SetDrawColor(200,200,000,255)") - self:Dyn_Emit("surface.DrawRect(W*0.125*1,0,W*0.125,H*0.80)") - self:Dyn_Emit("surface.SetDrawColor(000,200,200,255)") - self:Dyn_Emit("surface.DrawRect(W*0.125*2,0,W*0.125,H*0.80)") - self:Dyn_Emit("surface.SetDrawColor(000,200,000,255)") - self:Dyn_Emit("surface.DrawRect(W*0.125*3,0,W*0.125,H*0.80)") - self:Dyn_Emit("surface.SetDrawColor(200,000,200,255)") - self:Dyn_Emit("surface.DrawRect(W*0.125*4,0,W*0.125,H*0.80)") - self:Dyn_Emit("surface.SetDrawColor(200,000,000,255)") - self:Dyn_Emit("surface.DrawRect(W*0.125*5,0,W*0.125,H*0.80)") - self:Dyn_Emit("surface.SetDrawColor(000,000,200,255)") - self:Dyn_Emit("surface.DrawRect(W*0.125*6,0,W*0.125,H*0.80)") - - self:Dyn_Emit("for gray=0,7 do") - self:Dyn_Emit("surface.SetDrawColor(31*gray,31*gray,31*gray,255)") - self:Dyn_Emit("surface.DrawRect(W*0.125*gray,H*0.80,W*0.125,H*0.20)") - self:Dyn_Emit("end") - self:Dyn_Emit("end") + self:Dyn_Emit("if VM.RenderEnable == 1 then") + self:Dyn_Emit("$L W = VM.ScreenWidth") + self:Dyn_Emit("$L H = VM.ScreenHeight") + + self:Dyn_Emit("surface.SetTexture(0)") + self:Dyn_Emit("surface.SetDrawColor(200,200,200,255)") + self:Dyn_Emit("surface.DrawRect(W*0.125*0,0,W*0.125,H*0.80)") + self:Dyn_Emit("surface.SetDrawColor(200,200,000,255)") + self:Dyn_Emit("surface.DrawRect(W*0.125*1,0,W*0.125,H*0.80)") + self:Dyn_Emit("surface.SetDrawColor(000,200,200,255)") + self:Dyn_Emit("surface.DrawRect(W*0.125*2,0,W*0.125,H*0.80)") + self:Dyn_Emit("surface.SetDrawColor(000,200,000,255)") + self:Dyn_Emit("surface.DrawRect(W*0.125*3,0,W*0.125,H*0.80)") + self:Dyn_Emit("surface.SetDrawColor(200,000,200,255)") + self:Dyn_Emit("surface.DrawRect(W*0.125*4,0,W*0.125,H*0.80)") + self:Dyn_Emit("surface.SetDrawColor(200,000,000,255)") + self:Dyn_Emit("surface.DrawRect(W*0.125*5,0,W*0.125,H*0.80)") + self:Dyn_Emit("surface.SetDrawColor(000,000,200,255)") + self:Dyn_Emit("surface.DrawRect(W*0.125*6,0,W*0.125,H*0.80)") + + self:Dyn_Emit("for gray=0,7 do") + self:Dyn_Emit("surface.SetDrawColor(31*gray,31*gray,31*gray,255)") + self:Dyn_Emit("surface.DrawRect(W*0.125*gray,H*0.80,W*0.125,H*0.20)") + self:Dyn_Emit("end") + self:Dyn_Emit("end") end VM.OpcodeTable[201] = function(self) --DEXIT - self:Dyn_Emit("VM.INTR = 1") - self:Dyn_EmitBreak() - self.PrecompileBreak = true + self:Dyn_Emit("VM.INTR = 1") + self:Dyn_EmitBreak() + self.PrecompileBreak = true end VM.OpcodeTable[202] = function(self) --DCLR - self:Dyn_Emit("if VM.RenderEnable == 1 then") - self:Dyn_Emit("surface.SetTexture(0)") - self:Dyn_Emit("surface.SetDrawColor(0,0,0,255)") - self:Dyn_Emit("surface.DrawRect(0,0,VM.ScreenWidth,VM.ScreenHeight)") - self:Dyn_Emit("end") + self:Dyn_Emit("if VM.RenderEnable == 1 then") + self:Dyn_Emit("surface.SetTexture(0)") + self:Dyn_Emit("surface.SetDrawColor(0,0,0,255)") + self:Dyn_Emit("surface.DrawRect(0,0,VM.ScreenWidth,VM.ScreenHeight)") + self:Dyn_Emit("end") end VM.OpcodeTable[203] = function(self) --DCLRTEX - self:Dyn_Emit("if VM.RenderEnable == 1 then") - self:Dyn_Emit("VM:BindState()") - self:Dyn_Emit("surface.SetDrawColor(255,255,255,255)") - self:Dyn_Emit("surface.DrawTexturedRect(0,0,VM.ScreenWidth,VM.ScreenHeight)") - self:Dyn_Emit("end") + self:Dyn_Emit("if VM.RenderEnable == 1 then") + self:Dyn_Emit("VM:BindState()") + self:Dyn_Emit("surface.SetDrawColor(255,255,255,255)") + self:Dyn_Emit("surface.DrawTexturedRect(0,0,VM.ScreenWidth,VM.ScreenHeight)") + self:Dyn_Emit("end") end VM.OpcodeTable[204] = function(self) --DVXFLUSH - self:Dyn_Emit("VM:FlushBuffer()") + self:Dyn_Emit("VM:FlushBuffer()") end VM.OpcodeTable[205] = function(self) --DVXCLEAR - self:Dyn_Emit("VM.VertexBuffer = {}") + self:Dyn_Emit("VM.VertexBuffer = {}") end VM.OpcodeTable[206] = function(self) --DSETBUF_VX - self:Dyn_Emit("VM.Entity:SetRendertarget()") - self:Dyn_Emit("VM.LastBuffer = 2") + self:Dyn_Emit("VM.Entity:SetRendertarget()") + self:Dyn_Emit("VM.LastBuffer = 2") end VM.OpcodeTable[207] = function(self) --DSETBUF_SPR - self:Dyn_Emit("VM.Entity:SetRendertarget(1)") - self:Dyn_Emit("VM.LastBuffer = 1") + self:Dyn_Emit("VM.Entity:SetRendertarget(1)") + self:Dyn_Emit("VM.LastBuffer = 1") end VM.OpcodeTable[208] = function(self) --DSETBUF_FBO - self:Dyn_Emit("VM.Entity:SetRendertarget(0)") - self:Dyn_Emit("VM.LastBuffer = 0") + self:Dyn_Emit("VM.Entity:SetRendertarget(0)") + self:Dyn_Emit("VM.LastBuffer = 0") end VM.OpcodeTable[209] = function(self) --DSWAP - self:Dyn_Emit("if VM.RenderEnable == 1 then") - self:Dyn_Emit("VM.Entity:AssertSpriteBufferExists()") - self:Dyn_Emit("if VM.Entity.SpriteGPU.RT and VM.Entity.GPU.RT then") - self:Dyn_Emit("render.CopyTexture(VM.Entity.SpriteGPU.RT,VM.Entity.GPU.RT)") - self:Dyn_Emit("end") - self:Dyn_Emit("end") + self:Dyn_Emit("if VM.RenderEnable == 1 then") + self:Dyn_Emit("VM.Entity:AssertSpriteBufferExists()") + self:Dyn_Emit("if VM.Entity.SpriteGPU.RT and VM.Entity.GPU.RT then") + self:Dyn_Emit("render.CopyTexture(VM.Entity.SpriteGPU.RT,VM.Entity.GPU.RT)") + self:Dyn_Emit("end") + self:Dyn_Emit("end") end -------------------------------------------------------------------------------- VM.OpcodeTable[210] = function(self) --DVXPIPE - self:Dyn_Emit("VM.VertexPipe = $1") + self:Dyn_Emit("VM.VertexPipe = $1") end VM.OpcodeTable[211] = function(self) --DCVXPIPE - self:Dyn_Emit("VM.CoordinatePipe = $1") + self:Dyn_Emit("VM.CoordinatePipe = $1") end VM.OpcodeTable[212] = function(self) --DENABLE - self:Dyn_Emit("$L IDX = $1") - self:Dyn_Emit("if IDX == 0 then VM.VertexBufEnabled = 1 end") - self:Dyn_Emit("if IDX == 1 then VM.VertexBufZSort = 1 end") - self:Dyn_Emit("if IDX == 2 then VM.VertexLighting = 1 end") - self:Dyn_Emit("if IDX == 3 then VM.VertexCulling = 1 end") - self:Dyn_Emit("if IDX == 4 then VM.DistanceCulling = 1 end") - self:Dyn_Emit("if IDX == 5 then VM.VertexTexturing = 1 end") + self:Dyn_Emit("$L IDX = $1") + self:Dyn_Emit("if IDX == 0 then VM.VertexBufEnabled = 1 end") + self:Dyn_Emit("if IDX == 1 then VM.VertexBufZSort = 1 end") + self:Dyn_Emit("if IDX == 2 then VM.VertexLighting = 1 end") + self:Dyn_Emit("if IDX == 3 then VM.VertexCulling = 1 end") + self:Dyn_Emit("if IDX == 4 then VM.DistanceCulling = 1 end") + self:Dyn_Emit("if IDX == 5 then VM.VertexTexturing = 1 end") end VM.OpcodeTable[213] = function(self) --DDISABLE - self:Dyn_Emit("$L IDX = $1") - self:Dyn_Emit("if IDX == 0 then VM.VertexBufEnabled = 0 end") - self:Dyn_Emit("if IDX == 1 then VM.VertexBufZSort = 0 end") - self:Dyn_Emit("if IDX == 2 then VM.VertexLighting = 0 end") - self:Dyn_Emit("if IDX == 3 then VM.VertexCulling = 0 end") - self:Dyn_Emit("if IDX == 4 then VM.DistanceCulling = 0 end") - self:Dyn_Emit("if IDX == 5 then VM.VertexTexturing = 0 end") + self:Dyn_Emit("$L IDX = $1") + self:Dyn_Emit("if IDX == 0 then VM.VertexBufEnabled = 0 end") + self:Dyn_Emit("if IDX == 1 then VM.VertexBufZSort = 0 end") + self:Dyn_Emit("if IDX == 2 then VM.VertexLighting = 0 end") + self:Dyn_Emit("if IDX == 3 then VM.VertexCulling = 0 end") + self:Dyn_Emit("if IDX == 4 then VM.DistanceCulling = 0 end") + self:Dyn_Emit("if IDX == 5 then VM.VertexTexturing = 0 end") end VM.OpcodeTable[214] = function(self) --DCLRSCR - self:Dyn_Emit("if VM.RenderEnable == 1 then") - self:Dyn_Emit("VM:SetColor(VM:ReadVector4f($1))") - self:Dyn_Emit("VM:BindState()") - self:Dyn_Emit("surface.SetTexture(0)") - self:Dyn_Emit("surface.DrawRect(0,0,VM.ScreenWidth,VM.ScreenHeight)") - self:Dyn_Emit("end") + self:Dyn_Emit("if VM.RenderEnable == 1 then") + self:Dyn_Emit("VM:SetColor(VM:ReadVector4f($1))") + self:Dyn_Emit("VM:BindState()") + self:Dyn_Emit("surface.SetTexture(0)") + self:Dyn_Emit("surface.DrawRect(0,0,VM.ScreenWidth,VM.ScreenHeight)") + self:Dyn_Emit("end") end VM.OpcodeTable[215] = function(self) --DCOLOR - self:Dyn_Emit("VM:SetColor(VM:ReadVector4f($1))") - self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:SetColor(VM:ReadVector4f($1))") + self:Dyn_EmitInterruptCheck() end VM.OpcodeTable[216] = function(self) --DTEXTURE - self:Dyn_Emit("VM.Texture = $1") + self:Dyn_Emit("VM.Texture = $1") end VM.OpcodeTable[217] = function(self) --DSETFONT - self:Dyn_Emit("VM.Font = math.Clamp(math.floor($1),0,7)") - end + self:Dyn_Emit("VM.Font = math.Clamp(math.floor($1),0,7)") + end VM.OpcodeTable[218] = function(self) --DSETSIZE - self:Dyn_Emit("VM.FontSize = math.floor(math.max(4,math.min($1,200)))") + self:Dyn_Emit("VM.FontSize = math.floor(math.max(4,math.min($1,200)))") end VM.OpcodeTable[219] = function(self) --DMOVE - self:Dyn_Emit("$L ADDR = $1") - self:Dyn_Emit("if ADDR == 0 then") - self:Dyn_Emit("VM:WriteCell(65484,0)") - self:Dyn_Emit("VM:WriteCell(65483,0)") - self:Dyn_EmitInterruptCheck() - self:Dyn_Emit("else") - self:Dyn_Emit("VM:WriteCell(65484,VM:ReadCell(ADDR+0))") - self:Dyn_Emit("VM:WriteCell(65483,VM:ReadCell(ADDR+1))") - self:Dyn_EmitInterruptCheck() - self:Dyn_Emit("end") + self:Dyn_Emit("$L ADDR = $1") + self:Dyn_Emit("if ADDR == 0 then") + self:Dyn_Emit("VM:WriteCell(65484,0)") + self:Dyn_Emit("VM:WriteCell(65483,0)") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("else") + self:Dyn_Emit("VM:WriteCell(65484,VM:ReadCell(ADDR+0))") + self:Dyn_Emit("VM:WriteCell(65483,VM:ReadCell(ADDR+1))") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("end") end -------------------------------------------------------------------------------- VM.OpcodeTable[220] = function(self) --DVXDATA_2F - self:Dyn_Emit("$L VD = {}") - self:Dyn_Emit("$L ADDR = $1") - self:Dyn_Emit("$L VDATA = VM:ReadCell(65467)") - self:Dyn_Emit("for IDX=1,math.min(128,$2) do") - self:Dyn_Emit("if VDATA > 0 then") - self:Dyn_Emit("$L VIDX = VM:ReadCell(ADDR+IDX-1)") - self:Dyn_Emit("VD[IDX] = {") - self:Dyn_Emit(" x = VM:ReadCell(VDATA+VIDX*2+0),") - self:Dyn_Emit(" y = VM:ReadCell(VDATA+VIDX*2+1),") - self:Dyn_Emit("}") - self:Dyn_Emit("else") - self:Dyn_Emit("VD[IDX] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR+(IDX-1)*2+0),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR+(IDX-1)*2+1),") - self:Dyn_Emit("}") - self:Dyn_Emit("end") - - self:Dyn_Emit("VM:ComputeTextureUV(VD[IDX],VD[IDX].x/512,VD[IDX].y/512)") - self:Dyn_Emit("end") - - self:Dyn_EmitInterruptCheck() - self:Dyn_Emit("VM:DrawToBuffer(VD)") + self:Dyn_Emit("$L VD = {}") + self:Dyn_Emit("$L ADDR = $1") + self:Dyn_Emit("$L VDATA = VM:ReadCell(65467)") + self:Dyn_Emit("for IDX=1,math.min(128,$2) do") + self:Dyn_Emit("if VDATA > 0 then") + self:Dyn_Emit("$L VIDX = VM:ReadCell(ADDR+IDX-1)") + self:Dyn_Emit("VD[IDX] = {") + self:Dyn_Emit(" x = VM:ReadCell(VDATA+VIDX*2+0),") + self:Dyn_Emit(" y = VM:ReadCell(VDATA+VIDX*2+1),") + self:Dyn_Emit("}") + self:Dyn_Emit("else") + self:Dyn_Emit("VD[IDX] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR+(IDX-1)*2+0),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR+(IDX-1)*2+1),") + self:Dyn_Emit("}") + self:Dyn_Emit("end") + + self:Dyn_Emit("VM:ComputeTextureUV(VD[IDX],VD[IDX].x/512,VD[IDX].y/512)") + self:Dyn_Emit("end") + + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:DrawToBuffer(VD)") end VM.OpcodeTable[221] = function(self) --DVXDATA_2F_TEX - self:Dyn_Emit("$L VD = {}") - self:Dyn_Emit("$L ADDR = $1") - self:Dyn_Emit("$L VDATA = VM:ReadCell(65467)") - self:Dyn_Emit("for IDX=1,math.min(128,$2) do") - self:Dyn_Emit("if VDATA > 0 then") - self:Dyn_Emit("$L VIDX = VM:ReadCell(ADDR+IDX-1)") - self:Dyn_Emit("VD[IDX] = {") - self:Dyn_Emit(" x = VM:ReadCell(VDATA+VIDX*4+0),") - self:Dyn_Emit(" y = VM:ReadCell(VDATA+VIDX*4+1),") - self:Dyn_Emit("}") - - self:Dyn_Emit("VM:ComputeTextureUV(VD[IDX],VM:ReadCell(VDATA+VIDX*4+2),VM:ReadCell(VDATA+VIDX*4+3))") - self:Dyn_Emit("else") - self:Dyn_Emit("VD[IDX] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR+(IDX-1)*4+0),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR+(IDX-1)*4+1),") - self:Dyn_Emit("}") - - self:Dyn_Emit("VM:ComputeTextureUV(VD[IDX],VM:ReadCell(ADDR+(IDX-1)*4+2),VM:ReadCell(ADDR+(IDX-1)*4+3))") - self:Dyn_Emit("end") - self:Dyn_Emit("end") - - self:Dyn_EmitInterruptCheck() - self:Dyn_Emit("VM:DrawToBuffer(VD)") + self:Dyn_Emit("$L VD = {}") + self:Dyn_Emit("$L ADDR = $1") + self:Dyn_Emit("$L VDATA = VM:ReadCell(65467)") + self:Dyn_Emit("for IDX=1,math.min(128,$2) do") + self:Dyn_Emit("if VDATA > 0 then") + self:Dyn_Emit("$L VIDX = VM:ReadCell(ADDR+IDX-1)") + self:Dyn_Emit("VD[IDX] = {") + self:Dyn_Emit(" x = VM:ReadCell(VDATA+VIDX*4+0),") + self:Dyn_Emit(" y = VM:ReadCell(VDATA+VIDX*4+1),") + self:Dyn_Emit("}") + + self:Dyn_Emit("VM:ComputeTextureUV(VD[IDX],VM:ReadCell(VDATA+VIDX*4+2),VM:ReadCell(VDATA+VIDX*4+3))") + self:Dyn_Emit("else") + self:Dyn_Emit("VD[IDX] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR+(IDX-1)*4+0),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR+(IDX-1)*4+1),") + self:Dyn_Emit("}") + + self:Dyn_Emit("VM:ComputeTextureUV(VD[IDX],VM:ReadCell(ADDR+(IDX-1)*4+2),VM:ReadCell(ADDR+(IDX-1)*4+3))") + self:Dyn_Emit("end") + self:Dyn_Emit("end") + + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:DrawToBuffer(VD)") end VM.OpcodeTable[222] = function(self) --DVXDATA_3F - self:Dyn_Emit("$L VD = {}") - self:Dyn_Emit("$L ADDR = $1") - self:Dyn_Emit("$L VDATA = VM:ReadCell(65467)") - self:Dyn_Emit("for IDX=1,math.min(128,$2) do") - self:Dyn_Emit("if VDATA > 0 then") - self:Dyn_Emit("$L VIDX1 = VM:ReadCell(ADDR+(IDX-1)*3+0)") - self:Dyn_Emit("$L VIDX2 = VM:ReadCell(ADDR+(IDX-1)*3+1)") - self:Dyn_Emit("$L VIDX3 = VM:ReadCell(ADDR+(IDX-1)*3+2)") - self:Dyn_Emit("VD[1] = {") - self:Dyn_Emit(" x = VM:ReadCell(VDATA+VIDX1*3+0),") - self:Dyn_Emit(" y = VM:ReadCell(VDATA+VIDX1*3+1),") - self:Dyn_Emit(" z = VM:ReadCell(VDATA+VIDX1*3+2),") - self:Dyn_Emit("}") - self:Dyn_Emit("VD[2] = {") - self:Dyn_Emit(" x = VM:ReadCell(VDATA+VIDX2*3+0),") - self:Dyn_Emit(" y = VM:ReadCell(VDATA+VIDX2*3+1),") - self:Dyn_Emit(" z = VM:ReadCell(VDATA+VIDX2*3+2),") - self:Dyn_Emit("}") - self:Dyn_Emit("VD[3] = {") - self:Dyn_Emit(" x = VM:ReadCell(VDATA+VIDX3*3+0),") - self:Dyn_Emit(" y = VM:ReadCell(VDATA+VIDX3*3+1),") - self:Dyn_Emit(" z = VM:ReadCell(VDATA+VIDX3*3+2),") - self:Dyn_Emit("}") - self:Dyn_Emit("else") - self:Dyn_Emit("VD[1] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR+(IDX-1)*9+0),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR+(IDX-1)*9+1),") - self:Dyn_Emit(" z = VM:ReadCell(ADDR+(IDX-1)*9+2),") - self:Dyn_Emit("}") - self:Dyn_Emit("VD[2] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR+(IDX-1)*9+3),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR+(IDX-1)*9+4),") - self:Dyn_Emit(" z = VM:ReadCell(ADDR+(IDX-1)*9+5),") - self:Dyn_Emit("}") - self:Dyn_Emit("VD[3] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR+(IDX-1)*9+6),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR+(IDX-1)*9+7),") - self:Dyn_Emit(" z = VM:ReadCell(ADDR+(IDX-1)*9+8),") - self:Dyn_Emit("}") - self:Dyn_Emit("end") - - self:Dyn_Emit("VM:ComputeTextureUV(VD[1],0,0)") - self:Dyn_Emit("VM:ComputeTextureUV(VD[2],1,0)") - self:Dyn_Emit("VM:ComputeTextureUV(VD[3],1,1)") - - self:Dyn_EmitInterruptCheck() - self:Dyn_Emit("VM:DrawToBuffer(VD)") - self:Dyn_Emit("end") + self:Dyn_Emit("$L VD = {}") + self:Dyn_Emit("$L ADDR = $1") + self:Dyn_Emit("$L VDATA = VM:ReadCell(65467)") + self:Dyn_Emit("for IDX=1,math.min(128,$2) do") + self:Dyn_Emit("if VDATA > 0 then") + self:Dyn_Emit("$L VIDX1 = VM:ReadCell(ADDR+(IDX-1)*3+0)") + self:Dyn_Emit("$L VIDX2 = VM:ReadCell(ADDR+(IDX-1)*3+1)") + self:Dyn_Emit("$L VIDX3 = VM:ReadCell(ADDR+(IDX-1)*3+2)") + self:Dyn_Emit("VD[1] = {") + self:Dyn_Emit(" x = VM:ReadCell(VDATA+VIDX1*3+0),") + self:Dyn_Emit(" y = VM:ReadCell(VDATA+VIDX1*3+1),") + self:Dyn_Emit(" z = VM:ReadCell(VDATA+VIDX1*3+2),") + self:Dyn_Emit("}") + self:Dyn_Emit("VD[2] = {") + self:Dyn_Emit(" x = VM:ReadCell(VDATA+VIDX2*3+0),") + self:Dyn_Emit(" y = VM:ReadCell(VDATA+VIDX2*3+1),") + self:Dyn_Emit(" z = VM:ReadCell(VDATA+VIDX2*3+2),") + self:Dyn_Emit("}") + self:Dyn_Emit("VD[3] = {") + self:Dyn_Emit(" x = VM:ReadCell(VDATA+VIDX3*3+0),") + self:Dyn_Emit(" y = VM:ReadCell(VDATA+VIDX3*3+1),") + self:Dyn_Emit(" z = VM:ReadCell(VDATA+VIDX3*3+2),") + self:Dyn_Emit("}") + self:Dyn_Emit("else") + self:Dyn_Emit("VD[1] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR+(IDX-1)*9+0),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR+(IDX-1)*9+1),") + self:Dyn_Emit(" z = VM:ReadCell(ADDR+(IDX-1)*9+2),") + self:Dyn_Emit("}") + self:Dyn_Emit("VD[2] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR+(IDX-1)*9+3),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR+(IDX-1)*9+4),") + self:Dyn_Emit(" z = VM:ReadCell(ADDR+(IDX-1)*9+5),") + self:Dyn_Emit("}") + self:Dyn_Emit("VD[3] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR+(IDX-1)*9+6),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR+(IDX-1)*9+7),") + self:Dyn_Emit(" z = VM:ReadCell(ADDR+(IDX-1)*9+8),") + self:Dyn_Emit("}") + self:Dyn_Emit("end") + + self:Dyn_Emit("VM:ComputeTextureUV(VD[1],0,0)") + self:Dyn_Emit("VM:ComputeTextureUV(VD[2],1,0)") + self:Dyn_Emit("VM:ComputeTextureUV(VD[3],1,1)") + + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:DrawToBuffer(VD)") + self:Dyn_Emit("end") end VM.OpcodeTable[223] = function(self) --DVXDATA_3F_TEX - self:Dyn_Emit("$L VD = {}") - self:Dyn_Emit("$L ADDR = $1") - self:Dyn_Emit("$L VDATA = VM:ReadCell(65467)") - self:Dyn_Emit("for IDX=1,math.min(128,$2) do") - self:Dyn_Emit("if VDATA > 0 then") - self:Dyn_Emit("$L VIDX1 = VM:ReadCell(ADDR+(IDX-1)*3+0)") - self:Dyn_Emit("$L VIDX2 = VM:ReadCell(ADDR+(IDX-1)*3+1)") - self:Dyn_Emit("$L VIDX3 = VM:ReadCell(ADDR+(IDX-1)*3+2)") - self:Dyn_Emit("VD[1] = {") - self:Dyn_Emit(" x = VM:ReadCell(VDATA+VIDX1*5+0),") - self:Dyn_Emit(" y = VM:ReadCell(VDATA+VIDX1*5+1),") - self:Dyn_Emit(" z = VM:ReadCell(VDATA+VIDX1*5+2),") - self:Dyn_Emit("}") - self:Dyn_Emit("VD[2] = {") - self:Dyn_Emit(" x = VM:ReadCell(VDATA+VIDX2*5+0),") - self:Dyn_Emit(" y = VM:ReadCell(VDATA+VIDX2*5+1),") - self:Dyn_Emit(" z = VM:ReadCell(VDATA+VIDX2*5+2),") - self:Dyn_Emit("}") - self:Dyn_Emit("VD[3] = {") - self:Dyn_Emit(" x = VM:ReadCell(VDATA+VIDX3*5+0),") - self:Dyn_Emit(" y = VM:ReadCell(VDATA+VIDX3*5+1),") - self:Dyn_Emit(" z = VM:ReadCell(VDATA+VIDX3*5+2),") - self:Dyn_Emit("}") - - self:Dyn_Emit("VM:ComputeTextureUV(VD[1],VM:ReadCell(VDATA+VIDX1*5+3),VM:ReadCell(VDATA+VIDX1*5+4))") - self:Dyn_Emit("VM:ComputeTextureUV(VD[2],VM:ReadCell(VDATA+VIDX2*5+3),VM:ReadCell(VDATA+VIDX2*5+4))") - self:Dyn_Emit("VM:ComputeTextureUV(VD[3],VM:ReadCell(VDATA+VIDX3*5+3),VM:ReadCell(VDATA+VIDX3*5+4))") - self:Dyn_Emit("else") - self:Dyn_Emit("VD[1] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR+(IDX-1)*15+0),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR+(IDX-1)*15+1),") - self:Dyn_Emit(" z = VM:ReadCell(ADDR+(IDX-1)*15+2),") - self:Dyn_Emit("}") - self:Dyn_Emit("VD[2] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR+(IDX-1)*15+5),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR+(IDX-1)*15+6),") - self:Dyn_Emit(" z = VM:ReadCell(ADDR+(IDX-1)*15+7),") - self:Dyn_Emit("}") - self:Dyn_Emit("VD[3] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR+(IDX-1)*15+10),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR+(IDX-1)*15+11),") - self:Dyn_Emit(" z = VM:ReadCell(ADDR+(IDX-1)*15+12),") - self:Dyn_Emit("}") - - self:Dyn_Emit("VM:ComputeTextureUV(VD[1],VM:ReadCell(ADDR+(IDX-1)*15+ 3),VM:ReadCell(ADDR+(IDX-1)*15+ 4))") - self:Dyn_Emit("VM:ComputeTextureUV(VD[2],VM:ReadCell(ADDR+(IDX-1)*15+ 8),VM:ReadCell(ADDR+(IDX-1)*15+ 9))") - self:Dyn_Emit("VM:ComputeTextureUV(VD[3],VM:ReadCell(ADDR+(IDX-1)*15+13),VM:ReadCell(ADDR+(IDX-1)*15+14))") - self:Dyn_Emit("end") - - self:Dyn_EmitInterruptCheck() - self:Dyn_Emit("VM:DrawToBuffer(VD)") - self:Dyn_Emit("end") + self:Dyn_Emit("$L VD = {}") + self:Dyn_Emit("$L ADDR = $1") + self:Dyn_Emit("$L VDATA = VM:ReadCell(65467)") + self:Dyn_Emit("for IDX=1,math.min(128,$2) do") + self:Dyn_Emit("if VDATA > 0 then") + self:Dyn_Emit("$L VIDX1 = VM:ReadCell(ADDR+(IDX-1)*3+0)") + self:Dyn_Emit("$L VIDX2 = VM:ReadCell(ADDR+(IDX-1)*3+1)") + self:Dyn_Emit("$L VIDX3 = VM:ReadCell(ADDR+(IDX-1)*3+2)") + self:Dyn_Emit("VD[1] = {") + self:Dyn_Emit(" x = VM:ReadCell(VDATA+VIDX1*5+0),") + self:Dyn_Emit(" y = VM:ReadCell(VDATA+VIDX1*5+1),") + self:Dyn_Emit(" z = VM:ReadCell(VDATA+VIDX1*5+2),") + self:Dyn_Emit("}") + self:Dyn_Emit("VD[2] = {") + self:Dyn_Emit(" x = VM:ReadCell(VDATA+VIDX2*5+0),") + self:Dyn_Emit(" y = VM:ReadCell(VDATA+VIDX2*5+1),") + self:Dyn_Emit(" z = VM:ReadCell(VDATA+VIDX2*5+2),") + self:Dyn_Emit("}") + self:Dyn_Emit("VD[3] = {") + self:Dyn_Emit(" x = VM:ReadCell(VDATA+VIDX3*5+0),") + self:Dyn_Emit(" y = VM:ReadCell(VDATA+VIDX3*5+1),") + self:Dyn_Emit(" z = VM:ReadCell(VDATA+VIDX3*5+2),") + self:Dyn_Emit("}") + + self:Dyn_Emit("VM:ComputeTextureUV(VD[1],VM:ReadCell(VDATA+VIDX1*5+3),VM:ReadCell(VDATA+VIDX1*5+4))") + self:Dyn_Emit("VM:ComputeTextureUV(VD[2],VM:ReadCell(VDATA+VIDX2*5+3),VM:ReadCell(VDATA+VIDX2*5+4))") + self:Dyn_Emit("VM:ComputeTextureUV(VD[3],VM:ReadCell(VDATA+VIDX3*5+3),VM:ReadCell(VDATA+VIDX3*5+4))") + self:Dyn_Emit("else") + self:Dyn_Emit("VD[1] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR+(IDX-1)*15+0),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR+(IDX-1)*15+1),") + self:Dyn_Emit(" z = VM:ReadCell(ADDR+(IDX-1)*15+2),") + self:Dyn_Emit("}") + self:Dyn_Emit("VD[2] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR+(IDX-1)*15+5),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR+(IDX-1)*15+6),") + self:Dyn_Emit(" z = VM:ReadCell(ADDR+(IDX-1)*15+7),") + self:Dyn_Emit("}") + self:Dyn_Emit("VD[3] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR+(IDX-1)*15+10),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR+(IDX-1)*15+11),") + self:Dyn_Emit(" z = VM:ReadCell(ADDR+(IDX-1)*15+12),") + self:Dyn_Emit("}") + + self:Dyn_Emit("VM:ComputeTextureUV(VD[1],VM:ReadCell(ADDR+(IDX-1)*15+ 3),VM:ReadCell(ADDR+(IDX-1)*15+ 4))") + self:Dyn_Emit("VM:ComputeTextureUV(VD[2],VM:ReadCell(ADDR+(IDX-1)*15+ 8),VM:ReadCell(ADDR+(IDX-1)*15+ 9))") + self:Dyn_Emit("VM:ComputeTextureUV(VD[3],VM:ReadCell(ADDR+(IDX-1)*15+13),VM:ReadCell(ADDR+(IDX-1)*15+14))") + self:Dyn_Emit("end") + + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:DrawToBuffer(VD)") + self:Dyn_Emit("end") end VM.OpcodeTable[224] = function(self) --DVXDATA_3F_WF - self:Dyn_Emit("$L VD = {}") - self:Dyn_Emit("$L ADDR = $1") - self:Dyn_Emit("$L VDATA = VM:ReadCell(65467)") - self:Dyn_Emit("for IDX=1,math.min(128,$2) do") - self:Dyn_Emit("if VDATA > 0 then") - self:Dyn_Emit("$L VIDX1 = VM:ReadCell(ADDR+(IDX-1)*3+0)") - self:Dyn_Emit("$L VIDX2 = VM:ReadCell(ADDR+(IDX-1)*3+1)") - self:Dyn_Emit("$L VIDX3 = VM:ReadCell(ADDR+(IDX-1)*3+2)") - self:Dyn_Emit("VD[1] = {") - self:Dyn_Emit(" x = VM:ReadCell(VDATA+VIDX1*3+0),") - self:Dyn_Emit(" y = VM:ReadCell(VDATA+VIDX1*3+1),") - self:Dyn_Emit(" z = VM:ReadCell(VDATA+VIDX1*3+2),") - self:Dyn_Emit("}") - self:Dyn_Emit("VD[2] = {") - self:Dyn_Emit(" x = VM:ReadCell(VDATA+VIDX2*3+0),") - self:Dyn_Emit(" y = VM:ReadCell(VDATA+VIDX2*3+1),") - self:Dyn_Emit(" z = VM:ReadCell(VDATA+VIDX2*3+2),") - self:Dyn_Emit("}") - self:Dyn_Emit("VD[3] = {") - self:Dyn_Emit(" x = VM:ReadCell(VDATA+VIDX3*3+0),") - self:Dyn_Emit(" y = VM:ReadCell(VDATA+VIDX3*3+1),") - self:Dyn_Emit(" z = VM:ReadCell(VDATA+VIDX3*3+2),") - self:Dyn_Emit("}") - self:Dyn_Emit("else") - self:Dyn_Emit("VD[1] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR+(IDX-1)*9+0),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR+(IDX-1)*9+1),") - self:Dyn_Emit(" z = VM:ReadCell(ADDR+(IDX-1)*9+2),") - self:Dyn_Emit("}") - self:Dyn_Emit("VD[2] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR+(IDX-1)*9+3),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR+(IDX-1)*9+4),") - self:Dyn_Emit(" z = VM:ReadCell(ADDR+(IDX-1)*9+5),") - self:Dyn_Emit("}") - self:Dyn_Emit("VD[3] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR+(IDX-1)*9+6),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR+(IDX-1)*9+7),") - self:Dyn_Emit(" z = VM:ReadCell(ADDR+(IDX-1)*9+8),") - self:Dyn_Emit("}") - self:Dyn_Emit("end") - - self:Dyn_EmitInterruptCheck() - self:Dyn_Emit("VM:DrawToBuffer(VD,true)") - self:Dyn_Emit("end") + self:Dyn_Emit("$L VD = {}") + self:Dyn_Emit("$L ADDR = $1") + self:Dyn_Emit("$L VDATA = VM:ReadCell(65467)") + self:Dyn_Emit("for IDX=1,math.min(128,$2) do") + self:Dyn_Emit("if VDATA > 0 then") + self:Dyn_Emit("$L VIDX1 = VM:ReadCell(ADDR+(IDX-1)*3+0)") + self:Dyn_Emit("$L VIDX2 = VM:ReadCell(ADDR+(IDX-1)*3+1)") + self:Dyn_Emit("$L VIDX3 = VM:ReadCell(ADDR+(IDX-1)*3+2)") + self:Dyn_Emit("VD[1] = {") + self:Dyn_Emit(" x = VM:ReadCell(VDATA+VIDX1*3+0),") + self:Dyn_Emit(" y = VM:ReadCell(VDATA+VIDX1*3+1),") + self:Dyn_Emit(" z = VM:ReadCell(VDATA+VIDX1*3+2),") + self:Dyn_Emit("}") + self:Dyn_Emit("VD[2] = {") + self:Dyn_Emit(" x = VM:ReadCell(VDATA+VIDX2*3+0),") + self:Dyn_Emit(" y = VM:ReadCell(VDATA+VIDX2*3+1),") + self:Dyn_Emit(" z = VM:ReadCell(VDATA+VIDX2*3+2),") + self:Dyn_Emit("}") + self:Dyn_Emit("VD[3] = {") + self:Dyn_Emit(" x = VM:ReadCell(VDATA+VIDX3*3+0),") + self:Dyn_Emit(" y = VM:ReadCell(VDATA+VIDX3*3+1),") + self:Dyn_Emit(" z = VM:ReadCell(VDATA+VIDX3*3+2),") + self:Dyn_Emit("}") + self:Dyn_Emit("else") + self:Dyn_Emit("VD[1] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR+(IDX-1)*9+0),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR+(IDX-1)*9+1),") + self:Dyn_Emit(" z = VM:ReadCell(ADDR+(IDX-1)*9+2),") + self:Dyn_Emit("}") + self:Dyn_Emit("VD[2] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR+(IDX-1)*9+3),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR+(IDX-1)*9+4),") + self:Dyn_Emit(" z = VM:ReadCell(ADDR+(IDX-1)*9+5),") + self:Dyn_Emit("}") + self:Dyn_Emit("VD[3] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR+(IDX-1)*9+6),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR+(IDX-1)*9+7),") + self:Dyn_Emit(" z = VM:ReadCell(ADDR+(IDX-1)*9+8),") + self:Dyn_Emit("}") + self:Dyn_Emit("end") + + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:DrawToBuffer(VD,true)") + self:Dyn_Emit("end") end VM.OpcodeTable[225] = function(self) --DRECT - self:Dyn_Emit("$L VD = {}") - self:Dyn_Emit("$L ADDR1 = $1") - self:Dyn_Emit("$L ADDR2 = $2") - - self:Dyn_Emit("VD[1] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR1+0),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR1+1)}") - self:Dyn_Emit("VD[2] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR2+0),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR1+1)}") - self:Dyn_Emit("VD[3] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR2+0),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR2+1)}") - self:Dyn_Emit("VD[4] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR1+0),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR2+1)}") - - self:Dyn_Emit("VM:ComputeTextureUV(VD[1],0,0)") - self:Dyn_Emit("VM:ComputeTextureUV(VD[2],1,0)") - self:Dyn_Emit("VM:ComputeTextureUV(VD[3],1,1)") - self:Dyn_Emit("VM:ComputeTextureUV(VD[4],0,1)") - - self:Dyn_EmitInterruptCheck() - self:Dyn_Emit("VM:DrawToBuffer(VD)") + self:Dyn_Emit("$L VD = {}") + self:Dyn_Emit("$L ADDR1 = $1") + self:Dyn_Emit("$L ADDR2 = $2") + + self:Dyn_Emit("VD[1] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR1+0),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR1+1)}") + self:Dyn_Emit("VD[2] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR2+0),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR1+1)}") + self:Dyn_Emit("VD[3] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR2+0),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR2+1)}") + self:Dyn_Emit("VD[4] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR1+0),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR2+1)}") + + self:Dyn_Emit("VM:ComputeTextureUV(VD[1],0,0)") + self:Dyn_Emit("VM:ComputeTextureUV(VD[2],1,0)") + self:Dyn_Emit("VM:ComputeTextureUV(VD[3],1,1)") + self:Dyn_Emit("VM:ComputeTextureUV(VD[4],0,1)") + + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:DrawToBuffer(VD)") end VM.OpcodeTable[226] = function(self) --DCIRCLE - self:Dyn_Emit("$L VD = {}") - self:Dyn_Emit("$L R = $2") - self:Dyn_Emit("$L SIDES = math.max(3,math.min(64,VM:ReadCell(65485)))") - self:Dyn_Emit("$L START = VM:ReadCell(65478)") - self:Dyn_Emit("$L END = VM:ReadCell(65477)") - self:Dyn_Emit("$L STEP = (END-START)/SIDES") - - self:Dyn_Emit("$L VEC = VM:ReadVector2f($1)") - self:Dyn_EmitInterruptCheck() - - self:Dyn_Emit("for IDX=1,SIDES do") - self:Dyn_Emit("VD[1] = {") - self:Dyn_Emit(" x = VEC.x + R*math.sin(START+STEP*(IDX+0)),") - self:Dyn_Emit(" y = VEC.y + R*math.cos(START+STEP*(IDX+0))}") - self:Dyn_Emit("VD[2] = {") - self:Dyn_Emit(" x = VEC.x,") - self:Dyn_Emit(" y = VEC.y}") - self:Dyn_Emit("VD[3] = {") - self:Dyn_Emit(" x = VEC.x + R*math.sin(START+STEP*(IDX+1)),") - self:Dyn_Emit(" y = VEC.y + R*math.cos(START+STEP*(IDX+1))}") - - self:Dyn_Emit("VM:ComputeTextureUV(VD[1],0,0)") - self:Dyn_Emit("VM:ComputeTextureUV(VD[2],1,0)") - self:Dyn_Emit("VM:ComputeTextureUV(VD[3],1,1)") - - self:Dyn_Emit("VM:DrawToBuffer(VD)") - self:Dyn_Emit("end") + self:Dyn_Emit("$L VD = {}") + self:Dyn_Emit("$L R = $2") + self:Dyn_Emit("$L SIDES = math.max(3,math.min(64,VM:ReadCell(65485)))") + self:Dyn_Emit("$L START = VM:ReadCell(65478)") + self:Dyn_Emit("$L END = VM:ReadCell(65477)") + self:Dyn_Emit("$L STEP = (END-START)/SIDES") + + self:Dyn_Emit("$L VEC = VM:ReadVector2f($1)") + self:Dyn_EmitInterruptCheck() + + self:Dyn_Emit("for IDX=1,SIDES do") + self:Dyn_Emit("VD[1] = {") + self:Dyn_Emit(" x = VEC.x + R*math.sin(START+STEP*(IDX+0)),") + self:Dyn_Emit(" y = VEC.y + R*math.cos(START+STEP*(IDX+0))}") + self:Dyn_Emit("VD[2] = {") + self:Dyn_Emit(" x = VEC.x,") + self:Dyn_Emit(" y = VEC.y}") + self:Dyn_Emit("VD[3] = {") + self:Dyn_Emit(" x = VEC.x + R*math.sin(START+STEP*(IDX+1)),") + self:Dyn_Emit(" y = VEC.y + R*math.cos(START+STEP*(IDX+1))}") + + self:Dyn_Emit("VM:ComputeTextureUV(VD[1],0,0)") + self:Dyn_Emit("VM:ComputeTextureUV(VD[2],1,0)") + self:Dyn_Emit("VM:ComputeTextureUV(VD[3],1,1)") + + self:Dyn_Emit("VM:DrawToBuffer(VD)") + self:Dyn_Emit("end") end VM.OpcodeTable[227] = function(self) --DLINE - self:Dyn_Emit("VM:DrawLine(VM:ReadVector2f($1),VM:ReadVector2f($2))") + self:Dyn_Emit("VM:DrawLine(VM:ReadVector2f($1),VM:ReadVector2f($2))") end VM.OpcodeTable[228] = function(self) --DRECTWH - self:Dyn_Emit("$L VD = {}") - self:Dyn_Emit("$L ADDR1 = $1") - self:Dyn_Emit("$L ADDR2 = $2") - - self:Dyn_Emit("VD[1] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR1+0),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR1+1)}") - self:Dyn_Emit("VD[2] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR1+0)+VM:ReadCell(ADDR2+0),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR1+1)}") - self:Dyn_Emit("VD[3] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR1+0)+VM:ReadCell(ADDR2+0),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR1+1)+VM:ReadCell(ADDR2+1)}") - self:Dyn_Emit("VD[4] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR1+0),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR1+1)+VM:ReadCell(ADDR2+1)}") - - self:Dyn_Emit("VM:ComputeTextureUV(VD[1],0,0)") - self:Dyn_Emit("VM:ComputeTextureUV(VD[2],1,0)") - self:Dyn_Emit("VM:ComputeTextureUV(VD[3],1,1)") - self:Dyn_Emit("VM:ComputeTextureUV(VD[4],0,1)") - - self:Dyn_EmitInterruptCheck() - self:Dyn_Emit("VM:DrawToBuffer(VD)") + self:Dyn_Emit("$L VD = {}") + self:Dyn_Emit("$L ADDR1 = $1") + self:Dyn_Emit("$L ADDR2 = $2") + + self:Dyn_Emit("VD[1] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR1+0),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR1+1)}") + self:Dyn_Emit("VD[2] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR1+0)+VM:ReadCell(ADDR2+0),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR1+1)}") + self:Dyn_Emit("VD[3] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR1+0)+VM:ReadCell(ADDR2+0),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR1+1)+VM:ReadCell(ADDR2+1)}") + self:Dyn_Emit("VD[4] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR1+0),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR1+1)+VM:ReadCell(ADDR2+1)}") + + self:Dyn_Emit("VM:ComputeTextureUV(VD[1],0,0)") + self:Dyn_Emit("VM:ComputeTextureUV(VD[2],1,0)") + self:Dyn_Emit("VM:ComputeTextureUV(VD[3],1,1)") + self:Dyn_Emit("VM:ComputeTextureUV(VD[4],0,1)") + + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:DrawToBuffer(VD)") end VM.OpcodeTable[229] = function(self) --DORECT - self:Dyn_Emit("$L VD = {}") - self:Dyn_Emit("$L ADDR1 = $1") - self:Dyn_Emit("$L ADDR2 = $2") - - self:Dyn_Emit("VD[1] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR1+0),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR1+1)}") - self:Dyn_Emit("VD[2] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR2+0),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR1+1)}") - self:Dyn_Emit("VD[3] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR2+0),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR2+1)}") - self:Dyn_Emit("VD[4] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR1+0),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR2+1)}") - - self:Dyn_EmitInterruptCheck() - self:Dyn_Emit("VM:DrawLine(VD[1],VD[2])") - self:Dyn_Emit("VM:DrawLine(VD[2],VD[3])") - self:Dyn_Emit("VM:DrawLine(VD[3],VD[4])") - self:Dyn_Emit("VM:DrawLine(VD[4],VD[1])") + self:Dyn_Emit("$L VD = {}") + self:Dyn_Emit("$L ADDR1 = $1") + self:Dyn_Emit("$L ADDR2 = $2") + + self:Dyn_Emit("VD[1] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR1+0),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR1+1)}") + self:Dyn_Emit("VD[2] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR2+0),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR1+1)}") + self:Dyn_Emit("VD[3] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR2+0),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR2+1)}") + self:Dyn_Emit("VD[4] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR1+0),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR2+1)}") + + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:DrawLine(VD[1],VD[2])") + self:Dyn_Emit("VM:DrawLine(VD[2],VD[3])") + self:Dyn_Emit("VM:DrawLine(VD[3],VD[4])") + self:Dyn_Emit("VM:DrawLine(VD[4],VD[1])") end -------------------------------------------------------------------------------- VM.OpcodeTable[230] = function(self) --DTRANSFORM2F - self:Dyn_Emit("$L VEC = VM:ReadVector2f($2)") - self:Dyn_EmitInterruptCheck() - self:Dyn_Emit("VEC = VM:VertexTransform(VEC)") - self:Dyn_Emit("VM:WriteVector2f($1,VEC)") - self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("$L VEC = VM:ReadVector2f($2)") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VEC = VM:VertexTransform(VEC)") + self:Dyn_Emit("VM:WriteVector2f($1,VEC)") + self:Dyn_EmitInterruptCheck() end VM.OpcodeTable[231] = function(self) --DTRANSFORM3F - self:Dyn_Emit("$L VEC = VM:ReadVector3f($2)") - self:Dyn_EmitInterruptCheck() - self:Dyn_Emit("VEC = VM:VertexTransform(VEC)") - self:Dyn_Emit("VM:WriteVector3f($1,VEC)") - self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("$L VEC = VM:ReadVector3f($2)") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VEC = VM:VertexTransform(VEC)") + self:Dyn_Emit("VM:WriteVector3f($1,VEC)") + self:Dyn_EmitInterruptCheck() end VM.OpcodeTable[232] = function(self) --DSCRSIZE - self:Dyn_Emit("VM:WriteCell(65515,$1)") - self:Dyn_Emit("VM:WriteCell(65514,$2)") - self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:WriteCell(65515,$1)") + self:Dyn_Emit("VM:WriteCell(65514,$2)") + self:Dyn_EmitInterruptCheck() end VM.OpcodeTable[233] = function(self) --DROTATESCALE - self:Dyn_Emit("VM:WriteCell(65482,$1)") - self:Dyn_Emit("VM:WriteCell(65481,$2)") - self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:WriteCell(65482,$1)") + self:Dyn_Emit("VM:WriteCell(65481,$2)") + self:Dyn_EmitInterruptCheck() end VM.OpcodeTable[234] = function(self) --DORECTWH - self:Dyn_Emit("$L VD = {}") - self:Dyn_Emit("$L ADDR1 = $1") - self:Dyn_Emit("$L ADDR2 = $2") - - self:Dyn_Emit("VD[1] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR1+0),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR1+1)}") - self:Dyn_Emit("VD[2] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR1+0)+VM:ReadCell(ADDR2+0),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR1+1)}") - self:Dyn_Emit("VD[3] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR1+0)+VM:ReadCell(ADDR2+0),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR1+1)+VM:ReadCell(ADDR2+1)}") - self:Dyn_Emit("VD[4] = {") - self:Dyn_Emit(" x = VM:ReadCell(ADDR1+0),") - self:Dyn_Emit(" y = VM:ReadCell(ADDR1+1)+VM:ReadCell(ADDR2+1)}") - - self:Dyn_EmitInterruptCheck() - self:Dyn_Emit("VM:DrawLine(VD[1],VD[2])") - self:Dyn_Emit("VM:DrawLine(VD[2],VD[3])") - self:Dyn_Emit("VM:DrawLine(VD[3],VD[4])") - self:Dyn_Emit("VM:DrawLine(VD[4],VD[1])") + self:Dyn_Emit("$L VD = {}") + self:Dyn_Emit("$L ADDR1 = $1") + self:Dyn_Emit("$L ADDR2 = $2") + + self:Dyn_Emit("VD[1] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR1+0),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR1+1)}") + self:Dyn_Emit("VD[2] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR1+0)+VM:ReadCell(ADDR2+0),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR1+1)}") + self:Dyn_Emit("VD[3] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR1+0)+VM:ReadCell(ADDR2+0),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR1+1)+VM:ReadCell(ADDR2+1)}") + self:Dyn_Emit("VD[4] = {") + self:Dyn_Emit(" x = VM:ReadCell(ADDR1+0),") + self:Dyn_Emit(" y = VM:ReadCell(ADDR1+1)+VM:ReadCell(ADDR2+1)}") + + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:DrawLine(VD[1],VD[2])") + self:Dyn_Emit("VM:DrawLine(VD[2],VD[3])") + self:Dyn_Emit("VM:DrawLine(VD[3],VD[4])") + self:Dyn_Emit("VM:DrawLine(VD[4],VD[1])") end VM.OpcodeTable[235] = function(self) --DCULLMODE - self:Dyn_Emit("VM:WriteCell(65469,$1)") - self:Dyn_Emit("VM:WriteCell(65468,$2)") + self:Dyn_Emit("VM:WriteCell(65469,$1)") + self:Dyn_Emit("VM:WriteCell(65468,$2)") end VM.OpcodeTable[236] = function(self) --DARRAY - self:Dyn_Emit("$L RX = math.floor(VM:ReadCell($2))") - self:Dyn_Emit("$L RY = math.floor(VM:ReadCell(($2)+1))") - self:Dyn_Emit("$L PX = VM:ReadCell(($2)+2)") - self:Dyn_Emit("$L PY = VM:ReadCell(($2)+3)") - self:Dyn_Emit("$L CM = math.floor(VM:ReadCell(($2)+4))") - self:Dyn_Emit("$L PPTR = VM:ReadCell(($2)+5)") - self:Dyn_Emit("if not VM.ColorConversionModes[CM] then") - self:Dyn_Emit("VM:Interrupt(24,CM)") - self:Dyn_EmitInterruptCheck() - self:Dyn_Emit("end") - self:Dyn_Emit("$L PAMT = (RX*RY)*VM.ColorConversionModes[CM].PixelSize") - self:Dyn_Emit("$L PCVT = VM.ColorConversionModes[CM].Converter") - self:Dyn_Emit("$L StartX, StartY, DPTR = VM:ReadCell($1),VM:ReadCell(($1)+1),0") - self:Dyn_Emit("$L CurX, CurY = StartX,StartY") - self:Dyn_Emit("$L VD = {}") - self:Dyn_Emit("while(DPTR < PAMT) do") - self:Dyn_Emit("CurX = StartX") - self:Dyn_Emit("for x = 0, RX-1,1 do") - self:Dyn_Emit("$L ccolor,size = PCVT(VM,PPTR+DPTR)") - self:Dyn_EmitInterruptCheck() - self:Dyn_Emit("DPTR = DPTR + size") - self:Dyn_Emit("VD[1] = {x = CurX, y = CurY}") - self:Dyn_Emit("VD[2] = {x = CurX + PX, y = CurY}") - self:Dyn_Emit("VD[3] = {x = CurX + PX, y = CurY + PY}") - self:Dyn_Emit("VD[4] = {x = CurX, y = CurY + PY}") - self:Dyn_Emit("VM:SetColor(ccolor)") - self:Dyn_Emit("VM:DrawToBuffer(VD)") - self:Dyn_Emit("CurX = CurX + PX") - self:Dyn_BeginQuotaOnlyCode() - self:Dyn_Emit("VM.DARRAYCarriedX = x+1") - self:Dyn_Emit("VM.DARRAYInterruptedLoop = true") - self:Dyn_EndQuotaOnlyCode() - -- Copy of above for loop for quota override - self:Dyn_StartQuotaInterrupt() - self:Dyn_Emit("while(DPTR < PAMT) do") - self:Dyn_Emit("if not VM.DARRAYInterruptedLoop then") - self:Dyn_Emit("CurX = StartX") - self:Dyn_Emit("end") - self:Dyn_Emit("for x = VM.DARRAYCarriedX or 0, RX-1,1 do") - self:Dyn_Emit("$L ccolor,size = PCVT(VM,PPTR+DPTR)") - self:Dyn_EmitInterruptCheck() - self:Dyn_Emit("DPTR = DPTR + size") - self:Dyn_Emit("VD[1] = {x = CurX, y = CurY}") - self:Dyn_Emit("VD[2] = {x = CurX + PX, y = CurY}") - self:Dyn_Emit("VD[3] = {x = CurX + PX, y = CurY + PY}") - self:Dyn_Emit("VD[4] = {x = CurX, y = CurY + PY}") - self:Dyn_Emit("VM:SetColor(ccolor)") - self:Dyn_Emit("VM:DrawToBuffer(VD)") - self:Dyn_Emit("CurX = CurX + PX") - self:Dyn_BeginQuotaOnlyCode() - self:Dyn_Emit("VM.DARRAYCarriedX = x+1") - self:Dyn_Emit("VM.DARRAYInterruptedLoop = true") - self:Dyn_Emit("return") - self:Dyn_EndQuotaOnlyCode() - self:Dyn_Emit("end") - self:Dyn_Emit("VM.DARRAYCarriedX = nil") - self:Dyn_Emit("VM.DARRAYInterruptedLoop = nil") - self:Dyn_Emit("CurY = CurY + PY") - self:Dyn_Emit("end") - self:Dyn_Emit("VM.DARRAYCarriedX = nil") - self:Dyn_Emit("VM.DARRAYInterruptedLoop = nil") - self:Dyn_Emit("VM.QuotaOverrunFunc = nil") - self:Dyn_EndQuotaInterrupt() - self:Dyn_Emit("end") - self:Dyn_Emit("CurY = CurY + PY") - self:Dyn_Emit("end") + self:Dyn_Emit("$L RX = math.floor(VM:ReadCell($2))") + self:Dyn_Emit("$L RY = math.floor(VM:ReadCell(($2)+1))") + self:Dyn_Emit("$L PX = VM:ReadCell(($2)+2)") + self:Dyn_Emit("$L PY = VM:ReadCell(($2)+3)") + self:Dyn_Emit("$L CM = math.floor(VM:ReadCell(($2)+4))") + self:Dyn_Emit("$L PPTR = VM:ReadCell(($2)+5)") + self:Dyn_Emit("if not VM.ColorConversionModes[CM] then") + self:Dyn_Emit("VM:Interrupt(24,CM)") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("end") + self:Dyn_Emit("$L PAMT = (RX*RY)*VM.ColorConversionModes[CM].PixelSize") + self:Dyn_Emit("$L PCVT = VM.ColorConversionModes[CM].Converter") + self:Dyn_Emit("$L StartX, StartY, DPTR = VM:ReadCell($1),VM:ReadCell(($1)+1),0") + self:Dyn_Emit("$L CurX, CurY = StartX,StartY") + self:Dyn_Emit("$L VD = {}") + self:Dyn_Emit("while(DPTR < PAMT) do") + self:Dyn_Emit("CurX = StartX") + self:Dyn_Emit("for x = 0, RX-1,1 do") + self:Dyn_Emit("$L ccolor,size = PCVT(VM,PPTR+DPTR)") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("DPTR = DPTR + size") + self:Dyn_Emit("VD[1] = {x = CurX, y = CurY}") + self:Dyn_Emit("VD[2] = {x = CurX + PX, y = CurY}") + self:Dyn_Emit("VD[3] = {x = CurX + PX, y = CurY + PY}") + self:Dyn_Emit("VD[4] = {x = CurX, y = CurY + PY}") + self:Dyn_Emit("VM:SetColor(ccolor)") + self:Dyn_Emit("VM:DrawToBuffer(VD)") + self:Dyn_Emit("CurX = CurX + PX") + self:Dyn_BeginQuotaOnlyCode() + self:Dyn_Emit("VM.DARRAYCarriedX = x+1") + self:Dyn_Emit("VM.DARRAYInterruptedLoop = true") + self:Dyn_EndQuotaOnlyCode() + -- Copy of above for loop for quota override + self:Dyn_StartQuotaInterrupt() + self:Dyn_Emit("while(DPTR < PAMT) do") + self:Dyn_Emit("if not VM.DARRAYInterruptedLoop then") + self:Dyn_Emit("CurX = StartX") + self:Dyn_Emit("end") + self:Dyn_Emit("for x = VM.DARRAYCarriedX or 0, RX-1,1 do") + self:Dyn_Emit("$L ccolor,size = PCVT(VM,PPTR+DPTR)") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("DPTR = DPTR + size") + self:Dyn_Emit("VD[1] = {x = CurX, y = CurY}") + self:Dyn_Emit("VD[2] = {x = CurX + PX, y = CurY}") + self:Dyn_Emit("VD[3] = {x = CurX + PX, y = CurY + PY}") + self:Dyn_Emit("VD[4] = {x = CurX, y = CurY + PY}") + self:Dyn_Emit("VM:SetColor(ccolor)") + self:Dyn_Emit("VM:DrawToBuffer(VD)") + self:Dyn_Emit("CurX = CurX + PX") + self:Dyn_BeginQuotaOnlyCode() + self:Dyn_Emit("VM.DARRAYCarriedX = x+1") + self:Dyn_Emit("VM.DARRAYInterruptedLoop = true") + self:Dyn_Emit("return") + self:Dyn_EndQuotaOnlyCode() + self:Dyn_Emit("end") + self:Dyn_Emit("VM.DARRAYCarriedX = nil") + self:Dyn_Emit("VM.DARRAYInterruptedLoop = nil") + self:Dyn_Emit("CurY = CurY + PY") + self:Dyn_Emit("end") + self:Dyn_Emit("VM.DARRAYCarriedX = nil") + self:Dyn_Emit("VM.DARRAYInterruptedLoop = nil") + self:Dyn_Emit("VM.QuotaOverrunFunc = nil") + self:Dyn_EndQuotaInterrupt() + self:Dyn_Emit("end") + self:Dyn_Emit("CurY = CurY + PY") + self:Dyn_Emit("end") end VM.OpcodeTable[237] = function(self) --DDTERMINAL end VM.OpcodeTable[238] = function(self) --DPIXEL - self:Dyn_Emit("$L COLOR = VM:ColorTransform(VM:ReadVector4f($2))") - self:Dyn_Emit("$L POS = VM:ReadVector2f($1)") - self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("$L COLOR = VM:ColorTransform(VM:ReadVector4f($2))") + self:Dyn_Emit("$L POS = VM:ReadVector2f($1)") + self:Dyn_EmitInterruptCheck() - self:Dyn_Emit("surface.SetTexture(0)") - self:Dyn_Emit("surface.SetDrawColor(COLOR.x,COLOR.y,COLOR.z,COLOR.w)") - self:Dyn_Emit("surface.DrawRect(math.floor(POS.x),math.floor(POS.y),1,1)") + self:Dyn_Emit("surface.SetTexture(0)") + self:Dyn_Emit("surface.SetDrawColor(COLOR.x,COLOR.y,COLOR.z,COLOR.w)") + self:Dyn_Emit("surface.DrawRect(math.floor(POS.x),math.floor(POS.y),1,1)") end VM.OpcodeTable[239] = function(self) --RESERVED end -------------------------------------------------------------------------------- VM.OpcodeTable[240] = function(self) --DWRITE - self:Dyn_Emit("$L TEXT = VM:ReadString($2)") - self:Dyn_EmitInterruptCheck() - self:Dyn_Emit("VM:FontWrite($1,TEXT)") - self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("$L TEXT = VM:ReadString($2)") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:FontWrite($1,TEXT)") + self:Dyn_EmitInterruptCheck() end VM.OpcodeTable[241] = function(self) --DWRITEI - self:Dyn_Emit("VM:FontWrite($1,math.floor($2))") - self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:FontWrite($1,math.floor($2))") + self:Dyn_EmitInterruptCheck() end VM.OpcodeTable[242] = function(self) --DWRITEF - self:Dyn_Emit("VM:FontWrite($1,$2)") - self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:FontWrite($1,$2)") + self:Dyn_EmitInterruptCheck() end VM.OpcodeTable[243] = function(self) --DENTRYPOINT - self:Dyn_Emit("$L IDX = $1") - self:Dyn_Emit("if IDX == 0 then VM.EntryPoint0 = $2 end") - self:Dyn_Emit("if IDX == 1 then VM.EntryPoint1 = $2 end") - self:Dyn_Emit("if IDX == 2 then VM.EntryPoint2 = $2 end") - self:Dyn_Emit("if IDX == 3 then VM.EntryPoint3 = $2 end") - self:Dyn_Emit("if IDX == 4 then VM.EntryPoint4 = $2 end") + self:Dyn_Emit("$L IDX = $1") + self:Dyn_Emit("if IDX == 0 then VM.EntryPoint0 = $2 end") + self:Dyn_Emit("if IDX == 1 then VM.EntryPoint1 = $2 end") + self:Dyn_Emit("if IDX == 2 then VM.EntryPoint2 = $2 end") + self:Dyn_Emit("if IDX == 3 then VM.EntryPoint3 = $2 end") + self:Dyn_Emit("if IDX == 4 then VM.EntryPoint4 = $2 end") end VM.OpcodeTable[244] = function(self) --DSETLIGHT - self:Dyn_Emit("$L IDX = math.floor($1)") - self:Dyn_Emit("$L ADDR = $2") - self:Dyn_Emit("if (IDX < 0) or (IDX > 7) then") - self:Dyn_EmitInterrupt("19","0") - self:Dyn_Emit("else") - self:Dyn_Emit("VM.Lights[IDX] = {") - self:Dyn_Emit(" Position = VM:ReadVector4f(ADDR+0),") - self:Dyn_Emit(" Color = VM:ReadVector4f(ADDR+4)}") - self:Dyn_Emit("end") - self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("$L IDX = math.floor($1)") + self:Dyn_Emit("$L ADDR = $2") + self:Dyn_Emit("if (IDX < 0) or (IDX > 7) then") + self:Dyn_EmitInterrupt("19","0") + self:Dyn_Emit("else") + self:Dyn_Emit("VM.Lights[IDX] = {") + self:Dyn_Emit(" Position = VM:ReadVector4f(ADDR+0),") + self:Dyn_Emit(" Color = VM:ReadVector4f(ADDR+4)}") + self:Dyn_Emit("end") + self:Dyn_EmitInterruptCheck() end VM.OpcodeTable[245] = function(self) --DGETLIGHT - self:Dyn_Emit("$L IDX = math.floor($1)") - self:Dyn_Emit("$L ADDR = $2") - self:Dyn_Emit("if (IDX < 0) or (IDX > 7) then") - self:Dyn_EmitInterrupt("19","0") - self:Dyn_Emit("else") - self:Dyn_Emit("if VM.Lights[IDX] then") - self:Dyn_Emit("VM:WriteVector4f(ADDR+0,VM.Lights[IDX].Position)") - self:Dyn_Emit("VM:WriteVector4f(ADDR+4,VM.Lights[IDX].Color)") - self:Dyn_Emit("else") - self:Dyn_Emit("VM:WriteVector4f(ADDR+0,0)") - self:Dyn_Emit("VM:WriteVector4f(ADDR+4,0)") - self:Dyn_Emit("end") - self:Dyn_Emit("end") - self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("$L IDX = math.floor($1)") + self:Dyn_Emit("$L ADDR = $2") + self:Dyn_Emit("if (IDX < 0) or (IDX > 7) then") + self:Dyn_EmitInterrupt("19","0") + self:Dyn_Emit("else") + self:Dyn_Emit("if VM.Lights[IDX] then") + self:Dyn_Emit("VM:WriteVector4f(ADDR+0,VM.Lights[IDX].Position)") + self:Dyn_Emit("VM:WriteVector4f(ADDR+4,VM.Lights[IDX].Color)") + self:Dyn_Emit("else") + self:Dyn_Emit("VM:WriteVector4f(ADDR+0,0)") + self:Dyn_Emit("VM:WriteVector4f(ADDR+4,0)") + self:Dyn_Emit("end") + self:Dyn_Emit("end") + self:Dyn_EmitInterruptCheck() end VM.OpcodeTable[246] = function(self) --DWRITEFMT string.format( - self:Dyn_Emit("$L text = VM:ReadString($2)") - self:Dyn_EmitInterruptCheck() - - self:Dyn_Emit("$L ptr = $2 + #text + 1") - self:Dyn_Emit("$L ptr2 = VM.Memory[65512] or 0") - self:Dyn_Emit("if ptr2 ~= 0 then ptr = ptr2 end") - - self:Dyn_Emit("local finaltext = \"\"") - - self:Dyn_Emit("local inparam = false") - self:Dyn_Emit("local lengthmod = nil") - - self:Dyn_Emit("while (text ~= \"\") do") - self:Dyn_Emit("local chr = string.sub(text,1,1)") - self:Dyn_Emit("text = string.sub(text,2,65536)") - - self:Dyn_Emit("if (inparam == false) then") - self:Dyn_Emit("if (chr == \"%%%%\") then") - self:Dyn_Emit("inparam = true") - self:Dyn_Emit("else") - self:Dyn_Emit("finaltext = finaltext .. chr") - self:Dyn_Emit("end") - self:Dyn_Emit("else") - self:Dyn_Emit("if (chr == \".\") then") - self:Dyn_Emit("chr = string.sub(text,1,1)") - self:Dyn_Emit("text = string.sub(text,2,65536)") - - self:Dyn_Emit("if (tonumber(chr)) then") - self:Dyn_Emit("lengthmod = tonumber(chr)") - self:Dyn_Emit("end") - self:Dyn_Emit("elseif (chr == \"i\") or (chr == \"d\") then") - self:Dyn_Emit("if (lengthmod) then") - self:Dyn_Emit("local digits = 0") - self:Dyn_Emit("local num = math.floor(VM:ReadCell(ptr))") - self:Dyn_Emit("local temp = num") - self:Dyn_Emit("while (temp > 0) do") - self:Dyn_Emit("digits = digits + 1") - self:Dyn_Emit("temp = math.floor(temp / 10)") - self:Dyn_Emit("end") - self:Dyn_Emit("if (num == 0) then") - self:Dyn_Emit("digits = 1") - self:Dyn_Emit("end") - - self:Dyn_Emit("local fnum = tostring(num)") - self:Dyn_Emit("while (digits < lengthmod) do") - self:Dyn_Emit("digits = digits + 1") - self:Dyn_Emit("fnum = \"0\"..fnum") - self:Dyn_Emit("end") - - self:Dyn_Emit("finaltext = finaltext ..fnum") - self:Dyn_Emit("else") - self:Dyn_Emit("finaltext = finaltext .. math.floor(VM:ReadCell(ptr))") - self:Dyn_Emit("end") - self:Dyn_Emit("ptr = ptr + 1") - self:Dyn_Emit("inparam = false") - self:Dyn_Emit("lengthmod = nil") - self:Dyn_Emit("elseif (chr == \"f\") then") - self:Dyn_Emit("finaltext = finaltext .. VM:ReadCell(ptr)") - self:Dyn_Emit("ptr = ptr + 1") - self:Dyn_Emit("inparam = false") - self:Dyn_Emit("lengthmod = nil") - self:Dyn_Emit("elseif (chr == \"s\") then") - self:Dyn_Emit("local addr = VM:ReadCell(ptr)") - self:Dyn_Emit("local str = VM:ReadString(addr)") - self:Dyn_Emit("finaltext = finaltext .. str") - self:Dyn_Emit("ptr = ptr + 1") - self:Dyn_Emit("inparam = false") - self:Dyn_Emit("lengthmod = nil") - self:Dyn_Emit("elseif (chr == \"t\") then") - self:Dyn_Emit("while (math.mod(string.len(finaltext),(lengthmod or 6)) != 0) do") - self:Dyn_Emit("finaltext = finaltext..\" \"") - self:Dyn_Emit("end") - self:Dyn_Emit("inparam = false") - self:Dyn_Emit("lengthmod = nil") - self:Dyn_Emit("elseif (chr == \"%%%%\") then") - self:Dyn_Emit("finaltext = finaltext .. \"%%%%\"") - self:Dyn_Emit("inparam = false") - self:Dyn_Emit("lengthmod = nil") - self:Dyn_Emit("end") - self:Dyn_Emit("end") - self:Dyn_Emit("end") - - self:Dyn_Emit("VM:FontWrite($1,finaltext)") - self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("$L text = VM:ReadString($2)") + self:Dyn_EmitInterruptCheck() + + self:Dyn_Emit("$L ptr = $2 + #text + 1") + self:Dyn_Emit("$L ptr2 = VM.Memory[65512] or 0") + self:Dyn_Emit("if ptr2 ~= 0 then ptr = ptr2 end") + + self:Dyn_Emit("local finaltext = \"\"") + + self:Dyn_Emit("local inparam = false") + self:Dyn_Emit("local lengthmod = nil") + + self:Dyn_Emit("while (text ~= \"\") do") + self:Dyn_Emit("local chr = string.sub(text,1,1)") + self:Dyn_Emit("text = string.sub(text,2,65536)") + + self:Dyn_Emit("if (inparam == false) then") + self:Dyn_Emit("if (chr == \"%%%%\") then") + self:Dyn_Emit("inparam = true") + self:Dyn_Emit("else") + self:Dyn_Emit("finaltext = finaltext .. chr") + self:Dyn_Emit("end") + self:Dyn_Emit("else") + self:Dyn_Emit("if (chr == \".\") then") + self:Dyn_Emit("chr = string.sub(text,1,1)") + self:Dyn_Emit("text = string.sub(text,2,65536)") + + self:Dyn_Emit("if (tonumber(chr)) then") + self:Dyn_Emit("lengthmod = tonumber(chr)") + self:Dyn_Emit("end") + self:Dyn_Emit("elseif (chr == \"i\") or (chr == \"d\") then") + self:Dyn_Emit("if (lengthmod) then") + self:Dyn_Emit("local digits = 0") + self:Dyn_Emit("local num = math.floor(VM:ReadCell(ptr))") + self:Dyn_Emit("local temp = num") + self:Dyn_Emit("while (temp > 0) do") + self:Dyn_Emit("digits = digits + 1") + self:Dyn_Emit("temp = math.floor(temp / 10)") + self:Dyn_Emit("end") + self:Dyn_Emit("if (num == 0) then") + self:Dyn_Emit("digits = 1") + self:Dyn_Emit("end") + + self:Dyn_Emit("local fnum = tostring(num)") + self:Dyn_Emit("while (digits < lengthmod) do") + self:Dyn_Emit("digits = digits + 1") + self:Dyn_Emit("fnum = \"0\"..fnum") + self:Dyn_Emit("end") + + self:Dyn_Emit("finaltext = finaltext ..fnum") + self:Dyn_Emit("else") + self:Dyn_Emit("finaltext = finaltext .. math.floor(VM:ReadCell(ptr))") + self:Dyn_Emit("end") + self:Dyn_Emit("ptr = ptr + 1") + self:Dyn_Emit("inparam = false") + self:Dyn_Emit("lengthmod = nil") + self:Dyn_Emit("elseif (chr == \"f\") then") + self:Dyn_Emit("finaltext = finaltext .. VM:ReadCell(ptr)") + self:Dyn_Emit("ptr = ptr + 1") + self:Dyn_Emit("inparam = false") + self:Dyn_Emit("lengthmod = nil") + self:Dyn_Emit("elseif (chr == \"s\") then") + self:Dyn_Emit("local addr = VM:ReadCell(ptr)") + self:Dyn_Emit("local str = VM:ReadString(addr)") + self:Dyn_Emit("finaltext = finaltext .. str") + self:Dyn_Emit("ptr = ptr + 1") + self:Dyn_Emit("inparam = false") + self:Dyn_Emit("lengthmod = nil") + self:Dyn_Emit("elseif (chr == \"t\") then") + self:Dyn_Emit("while (math.mod(string.len(finaltext),(lengthmod or 6)) != 0) do") + self:Dyn_Emit("finaltext = finaltext..\" \"") + self:Dyn_Emit("end") + self:Dyn_Emit("inparam = false") + self:Dyn_Emit("lengthmod = nil") + self:Dyn_Emit("elseif (chr == \"%%%%\") then") + self:Dyn_Emit("finaltext = finaltext .. \"%%%%\"") + self:Dyn_Emit("inparam = false") + self:Dyn_Emit("lengthmod = nil") + self:Dyn_Emit("end") + self:Dyn_Emit("end") + self:Dyn_Emit("end") + + self:Dyn_Emit("VM:FontWrite($1,finaltext)") + self:Dyn_EmitInterruptCheck() end VM.OpcodeTable[247] = function(self) --DWRITEFIX - self:Dyn_Emit("$L TEXT = $2") - self:Dyn_Emit("if TEXT == math.floor(TEXT) then TEXT = TEXT .. \"0\" end") - self:Dyn_Emit("VM:FontWrite($1,TEXT)") - self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("$L TEXT = $2") + self:Dyn_Emit("if TEXT == math.floor(TEXT) then TEXT = TEXT .. \"0\" end") + self:Dyn_Emit("VM:FontWrite($1,TEXT)") + self:Dyn_EmitInterruptCheck() end VM.OpcodeTable[248] = function(self) --DTEXTWIDTH - self:Dyn_Emit("$L TEXT = VM:ReadString($2)") - self:Dyn_EmitInterruptCheck() - self:Dyn_Emit("$L W,H = VM:TextSize(TEXT)") - self:Dyn_EmitOperand("W") + self:Dyn_Emit("$L TEXT = VM:ReadString($2)") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("$L W,H = VM:TextSize(TEXT)") + self:Dyn_EmitOperand("W") end VM.OpcodeTable[249] = function(self) --DTEXTHEIGHT - self:Dyn_Emit("$L TEXT = VM:ReadString($2)") - self:Dyn_EmitInterruptCheck() - self:Dyn_Emit("$L W,H = VM:TextSize(TEXT)") - self:Dyn_EmitOperand("H") + self:Dyn_Emit("$L TEXT = VM:ReadString($2)") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("$L W,H = VM:TextSize(TEXT)") + self:Dyn_EmitOperand("H") end -------------------------------------------------------------------------------- VM.OpcodeTable[271] = function(self) --MLOADPROJ - self:Dyn_Emit("VM.ProjectionMatrix = VM:ReadMatrix($1)") - self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM.ProjectionMatrix = VM:ReadMatrix($1)") + self:Dyn_EmitInterruptCheck() end VM.OpcodeTable[272] = function(self) --MREAD - self:Dyn_Emit("VM:WriteMatrix($1,VM.ModelMatrix)") - self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:WriteMatrix($1,VM.ModelMatrix)") + self:Dyn_EmitInterruptCheck() end VM.OpcodeTable[274] = function(self) --DT - self:Dyn_EmitOperand("VM.TimerDT") + self:Dyn_EmitOperand("VM.TimerDT") end VM.OpcodeTable[276] = function(self) --DSHADE - self:Dyn_Emit("$L SHADE = $1") - self:Dyn_Emit("VM.Color.x = VM.Color.x*SHADE") - self:Dyn_Emit("VM.Color.y = VM.Color.y*SHADE") - self:Dyn_Emit("VM.Color.z = VM.Color.z*SHADE") - self:Dyn_Emit("VM:SetColor(VM.Color)") + self:Dyn_Emit("$L SHADE = $1") + self:Dyn_Emit("VM.Color.x = VM.Color.x*SHADE") + self:Dyn_Emit("VM.Color.y = VM.Color.y*SHADE") + self:Dyn_Emit("VM.Color.z = VM.Color.z*SHADE") + self:Dyn_Emit("VM:SetColor(VM.Color)") end VM.OpcodeTable[277] = function(self) --DSETWIDTH - self:Dyn_Emit("VM:WriteCell(65476,$1)") - self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:WriteCell(65476,$1)") + self:Dyn_EmitInterruptCheck() end VM.OpcodeTable[278] = function(self) --MLOAD - self:Dyn_Emit("VM.ModelMatrix = VM:ReadMatrix($1)") - self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM.ModelMatrix = VM:ReadMatrix($1)") + self:Dyn_EmitInterruptCheck() end VM.OpcodeTable[279] = function(self) --DSHADENORM - self:Dyn_Emit("$L SHADE = $1") - self:Dyn_Emit("VM.Color.x = math.Clamp(VM.Color.x*SHADE,0,255)") - self:Dyn_Emit("VM.Color.y = math.Clamp(VM.Color.y*SHADE,0,255)") - self:Dyn_Emit("VM.Color.z = math.Clamp(VM.Color.z*SHADE,0,255)") - self:Dyn_Emit("VM:SetColor(VM.Color)") + self:Dyn_Emit("$L SHADE = $1") + self:Dyn_Emit("VM.Color.x = math.Clamp(VM.Color.x*SHADE,0,255)") + self:Dyn_Emit("VM.Color.y = math.Clamp(VM.Color.y*SHADE,0,255)") + self:Dyn_Emit("VM.Color.z = math.Clamp(VM.Color.z*SHADE,0,255)") + self:Dyn_Emit("VM:SetColor(VM.Color)") end -------------------------------------------------------------------------------- VM.OpcodeTable[280] = function(self) --DDFRAME - self:Dyn_Emit("$L ADDR = $1") - self:Dyn_Emit("$L V1 = VM:ReadVector2f(ADDR+0)") -- X,Y - self:Dyn_Emit("$L V2 = VM:ReadVector2f(ADDR+2)") -- W,H - self:Dyn_Emit("$L V3 = VM:ReadVector4f(ADDR+4)") -- C1,C2,C3,BorderSize - self:Dyn_EmitInterruptCheck() - - self:Dyn_Emit("$L CSHADOW = VM:ReadVector3f(V3.x)") - self:Dyn_Emit("$L CHIGHLIGHT = VM:ReadVector3f(V3.y)") - self:Dyn_Emit("$L CFACE = VM:ReadVector3f(V3.z)") - - -- Shadow rectangle - self:Dyn_Emit("$L VD1 = {}") - self:Dyn_Emit("VD1[1] = {") - self:Dyn_Emit(" x = V3.w + V1.x,") - self:Dyn_Emit(" y = V3.w + V1.y}") - self:Dyn_Emit("VD1[2] = {") - self:Dyn_Emit(" x = V3.w + V1.x + V2.x,") - self:Dyn_Emit(" y = V3.w + V1.y}") - self:Dyn_Emit("VD1[3] = {") - self:Dyn_Emit(" x = V3.w + V1.x + V2.x,") - self:Dyn_Emit(" y = V3.w + V1.y + V2.y}") - self:Dyn_Emit("VD1[4] = {") - self:Dyn_Emit(" x = V3.w + V1.x,") - self:Dyn_Emit(" y = V3.w + V1.y + V2.y}") - - -- Highlight rectangle - self:Dyn_Emit("$L VD2 = {}") - self:Dyn_Emit("VD2[1] = {") - self:Dyn_Emit(" x = -V3.w + V1.x,") - self:Dyn_Emit(" y = -V3.w + V1.y}") - self:Dyn_Emit("VD2[2] = {") - self:Dyn_Emit(" x = -V3.w + V1.x + V2.x,") - self:Dyn_Emit(" y = -V3.w + V1.y}") - self:Dyn_Emit("VD2[3] = {") - self:Dyn_Emit(" x = -V3.w + V1.x + V2.x,") - self:Dyn_Emit(" y = -V3.w + V1.y + V2.y}") - self:Dyn_Emit("VD2[4] = {") - self:Dyn_Emit(" x = -V3.w + V1.x,") - self:Dyn_Emit(" y = -V3.w + V1.y + V2.y}") - - -- Face rectangle - self:Dyn_Emit("$L VD3 = {}") - self:Dyn_Emit("VD3[1] = {") - self:Dyn_Emit(" x = V1.x,") - self:Dyn_Emit(" y = V1.y}") - self:Dyn_Emit("VD3[2] = {") - self:Dyn_Emit(" x = V1.x + V2.x,") - self:Dyn_Emit(" y = V1.y}") - self:Dyn_Emit("VD3[3] = {") - self:Dyn_Emit(" x = V1.x + V2.x,") - self:Dyn_Emit(" y = V1.y + V2.y}") - self:Dyn_Emit("VD3[4] = {") - self:Dyn_Emit(" x = V1.x,") - self:Dyn_Emit(" y = V1.y + V2.y}") - - self:Dyn_Emit("VM:ComputeTextureUV(VD1[1],0,0)") - self:Dyn_Emit("VM:ComputeTextureUV(VD1[2],1,0)") - self:Dyn_Emit("VM:ComputeTextureUV(VD1[3],1,1)") - self:Dyn_Emit("VM:ComputeTextureUV(VD1[4],0,1)") - - self:Dyn_Emit("VM:ComputeTextureUV(VD2[1],0,0)") - self:Dyn_Emit("VM:ComputeTextureUV(VD2[2],1,0)") - self:Dyn_Emit("VM:ComputeTextureUV(VD2[3],1,1)") - self:Dyn_Emit("VM:ComputeTextureUV(VD2[4],0,1)") - - self:Dyn_Emit("VM:ComputeTextureUV(VD3[1],0,0)") - self:Dyn_Emit("VM:ComputeTextureUV(VD3[2],1,0)") - self:Dyn_Emit("VM:ComputeTextureUV(VD3[3],1,1)") - self:Dyn_Emit("VM:ComputeTextureUV(VD3[4],0,1)") - - self:Dyn_Emit("VM:SetColor(CSHADOW)") - self:Dyn_Emit("VM:DrawToBuffer(VD1)") - self:Dyn_Emit("VM:SetColor(CHIGHLIGHT)") - self:Dyn_Emit("VM:DrawToBuffer(VD2)") - self:Dyn_Emit("VM:SetColor(CFACE)") - self:Dyn_Emit("VM:DrawToBuffer(VD3)") + self:Dyn_Emit("$L ADDR = $1") + self:Dyn_Emit("$L V1 = VM:ReadVector2f(ADDR+0)") -- X,Y + self:Dyn_Emit("$L V2 = VM:ReadVector2f(ADDR+2)") -- W,H + self:Dyn_Emit("$L V3 = VM:ReadVector4f(ADDR+4)") -- C1,C2,C3,BorderSize + self:Dyn_EmitInterruptCheck() + + self:Dyn_Emit("$L CSHADOW = VM:ReadVector3f(V3.x)") + self:Dyn_Emit("$L CHIGHLIGHT = VM:ReadVector3f(V3.y)") + self:Dyn_Emit("$L CFACE = VM:ReadVector3f(V3.z)") + + -- Shadow rectangle + self:Dyn_Emit("$L VD1 = {}") + self:Dyn_Emit("VD1[1] = {") + self:Dyn_Emit(" x = V3.w + V1.x,") + self:Dyn_Emit(" y = V3.w + V1.y}") + self:Dyn_Emit("VD1[2] = {") + self:Dyn_Emit(" x = V3.w + V1.x + V2.x,") + self:Dyn_Emit(" y = V3.w + V1.y}") + self:Dyn_Emit("VD1[3] = {") + self:Dyn_Emit(" x = V3.w + V1.x + V2.x,") + self:Dyn_Emit(" y = V3.w + V1.y + V2.y}") + self:Dyn_Emit("VD1[4] = {") + self:Dyn_Emit(" x = V3.w + V1.x,") + self:Dyn_Emit(" y = V3.w + V1.y + V2.y}") + + -- Highlight rectangle + self:Dyn_Emit("$L VD2 = {}") + self:Dyn_Emit("VD2[1] = {") + self:Dyn_Emit(" x = -V3.w + V1.x,") + self:Dyn_Emit(" y = -V3.w + V1.y}") + self:Dyn_Emit("VD2[2] = {") + self:Dyn_Emit(" x = -V3.w + V1.x + V2.x,") + self:Dyn_Emit(" y = -V3.w + V1.y}") + self:Dyn_Emit("VD2[3] = {") + self:Dyn_Emit(" x = -V3.w + V1.x + V2.x,") + self:Dyn_Emit(" y = -V3.w + V1.y + V2.y}") + self:Dyn_Emit("VD2[4] = {") + self:Dyn_Emit(" x = -V3.w + V1.x,") + self:Dyn_Emit(" y = -V3.w + V1.y + V2.y}") + + -- Face rectangle + self:Dyn_Emit("$L VD3 = {}") + self:Dyn_Emit("VD3[1] = {") + self:Dyn_Emit(" x = V1.x,") + self:Dyn_Emit(" y = V1.y}") + self:Dyn_Emit("VD3[2] = {") + self:Dyn_Emit(" x = V1.x + V2.x,") + self:Dyn_Emit(" y = V1.y}") + self:Dyn_Emit("VD3[3] = {") + self:Dyn_Emit(" x = V1.x + V2.x,") + self:Dyn_Emit(" y = V1.y + V2.y}") + self:Dyn_Emit("VD3[4] = {") + self:Dyn_Emit(" x = V1.x,") + self:Dyn_Emit(" y = V1.y + V2.y}") + + self:Dyn_Emit("VM:ComputeTextureUV(VD1[1],0,0)") + self:Dyn_Emit("VM:ComputeTextureUV(VD1[2],1,0)") + self:Dyn_Emit("VM:ComputeTextureUV(VD1[3],1,1)") + self:Dyn_Emit("VM:ComputeTextureUV(VD1[4],0,1)") + + self:Dyn_Emit("VM:ComputeTextureUV(VD2[1],0,0)") + self:Dyn_Emit("VM:ComputeTextureUV(VD2[2],1,0)") + self:Dyn_Emit("VM:ComputeTextureUV(VD2[3],1,1)") + self:Dyn_Emit("VM:ComputeTextureUV(VD2[4],0,1)") + + self:Dyn_Emit("VM:ComputeTextureUV(VD3[1],0,0)") + self:Dyn_Emit("VM:ComputeTextureUV(VD3[2],1,0)") + self:Dyn_Emit("VM:ComputeTextureUV(VD3[3],1,1)") + self:Dyn_Emit("VM:ComputeTextureUV(VD3[4],0,1)") + + self:Dyn_Emit("VM:SetColor(CSHADOW)") + self:Dyn_Emit("VM:DrawToBuffer(VD1)") + self:Dyn_Emit("VM:SetColor(CHIGHLIGHT)") + self:Dyn_Emit("VM:DrawToBuffer(VD2)") + self:Dyn_Emit("VM:SetColor(CFACE)") + self:Dyn_Emit("VM:DrawToBuffer(VD3)") end VM.OpcodeTable[283] = function(self) --DRASTER - self:Dyn_Emit("VM:WriteCell(65518,$1)") - self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:WriteCell(65518,$1)") + self:Dyn_EmitInterruptCheck() end VM.OpcodeTable[284] = function(self) --DDTERRAIN - self:Dyn_Emit("$L ADDR = $1") - self:Dyn_Emit("$L W = VM:ReadCell(ADDR+0)") -- Total width/height of the terrain - self:Dyn_Emit("$L H = VM:ReadCell(ADDR+1)") - self:Dyn_Emit("$L R = math.Clamp(math.floor(VM:ReadCell(ADDR+2)),0,16)") -- Visibility radius - self:Dyn_Emit("$L U = VM:ReadCell(ADDR+3)") -- Point around which terrain must be drawn - self:Dyn_Emit("$L V = VM:ReadCell(ADDR+4)") - self:Dyn_EmitInterruptCheck() - - self:Dyn_Emit("$L VD = {}") - - -- Terrain size - self:Dyn_Emit("$L MinX = math.Clamp(math.floor(W/2 + U - R),1,W-1)") - self:Dyn_Emit("$L MinY = math.Clamp(math.floor(H/2 + V - R),1,H-1)") - self:Dyn_Emit("$L MaxX = math.Clamp(math.floor(W/2 + U + R),1,W-1)") - self:Dyn_Emit("$L MaxY = math.Clamp(math.floor(H/2 + V + R),1,H-1)") - - -- Draw terrain - self:Dyn_Emit("for X=MinX,math.min(MaxX, 64) do") - self:Dyn_Emit("for Y=MinY,math.min(MaxY, 64) do") - self:Dyn_Emit("$L XPOS = X - W/2 - U - 0.5") - self:Dyn_Emit("$L YPOS = Y - H/2 - U - 0.5") - - self:Dyn_Emit("if (X > 0) and (X <= W-1) and (Y > 0) and (Y <= H-1) and (XPOS^2+YPOS^2 <= R^2) then") - self:Dyn_Emit("$L Z1 = VM:ReadCell(ADDR+16+(Y-1)*W+(X-1))") - self:Dyn_Emit("$L Z2 = VM:ReadCell(ADDR+16+(Y-1)*W+(X-0))") - self:Dyn_Emit("$L Z3 = VM:ReadCell(ADDR+16+(Y-0)*W+(X-0))") - self:Dyn_Emit("$L Z4 = VM:ReadCell(ADDR+16+(Y-0)*W+(X-1))") - - self:Dyn_Emit("VD[1] = {") - self:Dyn_Emit(" x = XPOS+1,") - self:Dyn_Emit(" y = YPOS+1,") - self:Dyn_Emit(" z = Z3}") - self:Dyn_Emit("VD[2] = {") - self:Dyn_Emit(" x = XPOS+1,") - self:Dyn_Emit(" y = YPOS,") - self:Dyn_Emit(" z = Z2}") - self:Dyn_Emit("VD[3] = {") - self:Dyn_Emit(" x = XPOS,") - self:Dyn_Emit(" y = YPOS,") - self:Dyn_Emit(" z = Z1}") - - self:Dyn_Emit("VM:ComputeTextureUV(VD[1],0,0)") - self:Dyn_Emit("VM:ComputeTextureUV(VD[2],1,0)") - self:Dyn_Emit("VM:ComputeTextureUV(VD[3],1,1)") - self:Dyn_Emit("VM:DrawToBuffer(VD)") - - self:Dyn_Emit("VD[1] = {") - self:Dyn_Emit(" x = XPOS,") - self:Dyn_Emit(" y = YPOS,") - self:Dyn_Emit(" z = Z1}") - self:Dyn_Emit("VD[2] = {") - self:Dyn_Emit(" x = XPOS,") - self:Dyn_Emit(" y = YPOS+1,") - self:Dyn_Emit(" z = Z4}") - self:Dyn_Emit("VD[3] = {") - self:Dyn_Emit(" x = XPOS+1,") - self:Dyn_Emit(" y = YPOS+1,") - self:Dyn_Emit(" z = Z3}") - - self:Dyn_Emit("VM:ComputeTextureUV(VD[1],0,0)") - self:Dyn_Emit("VM:ComputeTextureUV(VD[2],0,1)") - self:Dyn_Emit("VM:ComputeTextureUV(VD[3],1,1)") - self:Dyn_Emit("VM:DrawToBuffer(VD)") - self:Dyn_Emit("end") - self:Dyn_Emit("end") - self:Dyn_Emit("end") + self:Dyn_Emit("$L ADDR = $1") + self:Dyn_Emit("$L W = VM:ReadCell(ADDR+0)") -- Total width/height of the terrain + self:Dyn_Emit("$L H = VM:ReadCell(ADDR+1)") + self:Dyn_Emit("$L R = math.Clamp(math.floor(VM:ReadCell(ADDR+2)),0,16)") -- Visibility radius + self:Dyn_Emit("$L U = VM:ReadCell(ADDR+3)") -- Point around which terrain must be drawn + self:Dyn_Emit("$L V = VM:ReadCell(ADDR+4)") + self:Dyn_EmitInterruptCheck() + + self:Dyn_Emit("$L VD = {}") + + -- Terrain size + self:Dyn_Emit("$L MinX = math.Clamp(math.floor(W/2 + U - R),1,W-1)") + self:Dyn_Emit("$L MinY = math.Clamp(math.floor(H/2 + V - R),1,H-1)") + self:Dyn_Emit("$L MaxX = math.Clamp(math.floor(W/2 + U + R),1,W-1)") + self:Dyn_Emit("$L MaxY = math.Clamp(math.floor(H/2 + V + R),1,H-1)") + + -- Draw terrain + self:Dyn_Emit("for X=MinX,math.min(MaxX, 64) do") + self:Dyn_Emit("for Y=MinY,math.min(MaxY, 64) do") + self:Dyn_Emit("$L XPOS = X - W/2 - U - 0.5") + self:Dyn_Emit("$L YPOS = Y - H/2 - U - 0.5") + + self:Dyn_Emit("if (X > 0) and (X <= W-1) and (Y > 0) and (Y <= H-1) and (XPOS^2+YPOS^2 <= R^2) then") + self:Dyn_Emit("$L Z1 = VM:ReadCell(ADDR+16+(Y-1)*W+(X-1))") + self:Dyn_Emit("$L Z2 = VM:ReadCell(ADDR+16+(Y-1)*W+(X-0))") + self:Dyn_Emit("$L Z3 = VM:ReadCell(ADDR+16+(Y-0)*W+(X-0))") + self:Dyn_Emit("$L Z4 = VM:ReadCell(ADDR+16+(Y-0)*W+(X-1))") + + self:Dyn_Emit("VD[1] = {") + self:Dyn_Emit(" x = XPOS+1,") + self:Dyn_Emit(" y = YPOS+1,") + self:Dyn_Emit(" z = Z3}") + self:Dyn_Emit("VD[2] = {") + self:Dyn_Emit(" x = XPOS+1,") + self:Dyn_Emit(" y = YPOS,") + self:Dyn_Emit(" z = Z2}") + self:Dyn_Emit("VD[3] = {") + self:Dyn_Emit(" x = XPOS,") + self:Dyn_Emit(" y = YPOS,") + self:Dyn_Emit(" z = Z1}") + + self:Dyn_Emit("VM:ComputeTextureUV(VD[1],0,0)") + self:Dyn_Emit("VM:ComputeTextureUV(VD[2],1,0)") + self:Dyn_Emit("VM:ComputeTextureUV(VD[3],1,1)") + self:Dyn_Emit("VM:DrawToBuffer(VD)") + + self:Dyn_Emit("VD[1] = {") + self:Dyn_Emit(" x = XPOS,") + self:Dyn_Emit(" y = YPOS,") + self:Dyn_Emit(" z = Z1}") + self:Dyn_Emit("VD[2] = {") + self:Dyn_Emit(" x = XPOS,") + self:Dyn_Emit(" y = YPOS+1,") + self:Dyn_Emit(" z = Z4}") + self:Dyn_Emit("VD[3] = {") + self:Dyn_Emit(" x = XPOS+1,") + self:Dyn_Emit(" y = YPOS+1,") + self:Dyn_Emit(" z = Z3}") + + self:Dyn_Emit("VM:ComputeTextureUV(VD[1],0,0)") + self:Dyn_Emit("VM:ComputeTextureUV(VD[2],0,1)") + self:Dyn_Emit("VM:ComputeTextureUV(VD[3],1,1)") + self:Dyn_Emit("VM:DrawToBuffer(VD)") + self:Dyn_Emit("end") + self:Dyn_Emit("end") + self:Dyn_Emit("end") end VM.OpcodeTable[288] = function(self) --DSETTEXTBOX - self:Dyn_Emit("VM.Textbox = VM:ReadVector2f($1)") - self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM.Textbox = VM:ReadVector2f($1)") + self:Dyn_EmitInterruptCheck() end VM.OpcodeTable[289] = function(self) --DSETTEXTWRAP - self:Dyn_Emit("VM.WordWrapMode = $1") + self:Dyn_Emit("VM.WordWrapMode = $1") end -------------------------------------------------------------------------------- VM.OpcodeTable[294] = function(self) --DMULDT - self:Dyn_EmitOperand("$2*VM.TimerDT") + self:Dyn_EmitOperand("$2*VM.TimerDT") end VM.OpcodeTable[297] = function(self) --DMULDT - self:Dyn_EmitOperand("$2*VM.TimerDT") + self:Dyn_EmitOperand("$2*VM.TimerDT") end VM.OpcodeTable[298] = function(self) --DBEGIN - self:Dyn_Emit("VM.Entity:SetRendertarget(1)") - self:Dyn_Emit("VM.LastBuffer = 1") + self:Dyn_Emit("VM.Entity:SetRendertarget(1)") + self:Dyn_Emit("VM.LastBuffer = 1") end VM.OpcodeTable[299] = function(self) --DEND - self:Dyn_Emit("VM:FlushBuffer()") - self:Dyn_Emit("VM.Entity:AssertSpriteBufferExists()") - self:Dyn_Emit("if VM.Entity.SpriteGPU.RT and VM.Entity.GPU.RT then") - self:Dyn_Emit("render.CopyTexture(VM.Entity.SpriteGPU.RT,VM.Entity.GPU.RT)") - self:Dyn_Emit("end") - self:Dyn_Emit("VM.Entity:SetRendertarget()") - self:Dyn_Emit("VM.LastBuffer = 2") + self:Dyn_Emit("VM:FlushBuffer()") + self:Dyn_Emit("VM.Entity:AssertSpriteBufferExists()") + self:Dyn_Emit("if VM.Entity.SpriteGPU.RT and VM.Entity.GPU.RT then") + self:Dyn_Emit("render.CopyTexture(VM.Entity.SpriteGPU.RT,VM.Entity.GPU.RT)") + self:Dyn_Emit("end") + self:Dyn_Emit("VM.Entity:SetRendertarget()") + self:Dyn_Emit("VM.LastBuffer = 2") end -------------------------------------------------------------------------------- VM.OpcodeTable[303] = function(self) --DXTEXTURE - self:Dyn_Emit("$L PTR = $1") - self:Dyn_Emit("if PTR > 0 then") - self:Dyn_Emit("$L NAME = VM:ReadString($1)") - self:Dyn_EmitInterruptCheck() - self:Dyn_Emit("VM:SetMaterial(GPULib.Material(NAME))") - self:Dyn_Emit("else") - self:Dyn_Emit("VM:SetMaterial(nil)") - self:Dyn_Emit("end") + self:Dyn_Emit("$L PTR = $1") + self:Dyn_Emit("if PTR > 0 then") + self:Dyn_Emit("$L NAME = VM:ReadString($1)") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:SetMaterial(GPULib.Material(NAME))") + self:Dyn_Emit("else") + self:Dyn_Emit("VM:SetMaterial(nil)") + self:Dyn_Emit("end") end diff --git a/lua/entities/gmod_wire_gpu/cl_init.lua b/lua/entities/gmod_wire_gpu/cl_init.lua index e5b91db609..444f3c7ba7 100644 --- a/lua/entities/gmod_wire_gpu/cl_init.lua +++ b/lua/entities/gmod_wire_gpu/cl_init.lua @@ -9,35 +9,35 @@ local HUDLookup = {} -- Update monitors certain GPU is linked to -------------------------------------------------------------------------------- local function recalculateMonitorLookup() - MonitorLookup = {} - HUDLookup = {} - for gpuIdx,linkedGPUs in pairs(Monitors) do - for _,linkedGPUIdx in pairs(linkedGPUs) do - local linkedEnt = ents.GetByIndex(linkedGPUIdx) - if linkedEnt and linkedEnt:IsValid() then - if linkedEnt:IsPlayer() then - HUDLookup[linkedGPUIdx] = gpuIdx - else - MonitorLookup[linkedGPUIdx] = gpuIdx - end - end - end - end + MonitorLookup = {} + HUDLookup = {} + for gpuIdx,linkedGPUs in pairs(Monitors) do + for _,linkedGPUIdx in pairs(linkedGPUs) do + local linkedEnt = ents.GetByIndex(linkedGPUIdx) + if linkedEnt and linkedEnt:IsValid() then + if linkedEnt:IsPlayer() then + HUDLookup[linkedGPUIdx] = gpuIdx + else + MonitorLookup[linkedGPUIdx] = gpuIdx + end + end + end + end end local function GPU_MonitorState(um) - -- Read monitors for this GPU - local gpuIdx = um:ReadLong() - Monitors[gpuIdx] = {} - - -- Fetch all monitors - local count = um:ReadShort() - for i=1,count do - Monitors[gpuIdx][i] = um:ReadLong() - end - - -- Recalculate small lookup table for monitor system - recalculateMonitorLookup() + -- Read monitors for this GPU + local gpuIdx = um:ReadLong() + Monitors[gpuIdx] = {} + + -- Fetch all monitors + local count = um:ReadShort() + for i=1,count do + Monitors[gpuIdx][i] = um:ReadLong() + end + + -- Recalculate small lookup table for monitor system + recalculateMonitorLookup() end usermessage.Hook("wire_gpu_monitorstate", GPU_MonitorState) @@ -46,83 +46,83 @@ usermessage.Hook("wire_gpu_monitorstate", GPU_MonitorState) -- Update GPU features/memory model -------------------------------------------------------------------------------- local function GPU_MemoryModel(um) - local GPU = ents.GetByIndex(um:ReadLong()) - if not GPU then return end - if not GPU:IsValid() then return end - - if GPU.VM then - GPU.VM.ROMSize = um:ReadLong() - GPU.VM.SerialNo = um:ReadFloat() - GPU.VM.RAMSize = GPU.VM.ROMSize - else - GPU.ROMSize = um:ReadLong() - GPU.SerialNo = um:ReadFloat() - end - GPU.ChipType = um:ReadShort() + local GPU = ents.GetByIndex(um:ReadLong()) + if not GPU then return end + if not GPU:IsValid() then return end + + if GPU.VM then + GPU.VM.ROMSize = um:ReadLong() + GPU.VM.SerialNo = um:ReadFloat() + GPU.VM.RAMSize = GPU.VM.ROMSize + else + GPU.ROMSize = um:ReadLong() + GPU.SerialNo = um:ReadFloat() + end + GPU.ChipType = um:ReadShort() 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 + 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() - -- Create virtual machine - self.VM = CPULib.VirtualMachine() - self.VM.SerialNo = CPULib.GenerateSN("GPU") - self.VM.RAMSize = 65536 - self.VM.ROMSize = 65536 - self.VM.PCAP = 0 - self.VM.RQCAP = 0 - 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() - self.VM:HardReset() - - -- Special variables - self.VM.CurrentBuffer = 2 - self.VM.LastBuffer = 2 - self.VM.RenderEnable = 0 - self.VM.VertexMode = 0 - self.VM.MemBusBuffer = {} - self.VM.MemBusCount = 0 - self.VM.LateFrames = 0 - - -- Create GPU - self.GPU = WireGPU(self) - self.In3D2D = false - self.In2D = false - - -- Setup caching - GPULib.ClientCacheCallback(self,function(Address,Value) - self.VM:WriteCell(Address,Value) - self.VM.ROM[Address] = Value - end) - - -- Draw outlines in chip mode - local tempDrawOutline = self.DrawEntityOutline - self.DrawEntityOutline = function(self) if self.ChipType ~= 0 then tempDrawOutline(self) end end + -- Create virtual machine + self.VM = CPULib.VirtualMachine() + self.VM.SerialNo = CPULib.GenerateSN("GPU") + self.VM.RAMSize = 65536 + self.VM.ROMSize = 65536 + self.VM.PCAP = 0 + self.VM.RQCAP = 0 + 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() + self.VM:HardReset() + + -- Special variables + self.VM.CurrentBuffer = 2 + self.VM.LastBuffer = 2 + self.VM.RenderEnable = 0 + self.VM.VertexMode = 0 + self.VM.MemBusBuffer = {} + self.VM.MemBusCount = 0 + self.VM.LateFrames = 0 + + -- Create GPU + self.GPU = WireGPU(self) + self.In3D2D = false + self.In2D = false + + -- Setup caching + GPULib.ClientCacheCallback(self,function(Address,Value) + self.VM:WriteCell(Address,Value) + self.VM.ROM[Address] = Value + end) + + -- Draw outlines in chip mode + local tempDrawOutline = self.DrawEntityOutline + self.DrawEntityOutline = function(self) if self.ChipType ~= 0 then tempDrawOutline(self) end end end -- Assert that sprite buffer exists and is available for any operations on it function ENT:AssertSpriteBufferExists() - if not self.SpriteGPU then self.SpriteGPU = WireGPU(self) end + if not self.SpriteGPU then self.SpriteGPU = WireGPU(self) end end @@ -131,9 +131,9 @@ end -------------------------------------------------------------------------------- -- Entity deleted function ENT:OnRemove() - GPULib.ClientCacheCallback(self,nil) - self.GPU:Finalize() - if self.SpriteGPU then self.SpriteGPU:Finalize() end + GPULib.ClientCacheCallback(self,nil) + self.GPU:Finalize() + if self.SpriteGPU then self.SpriteGPU:Finalize() end end @@ -142,76 +142,76 @@ end -------------------------------------------------------------------------------- -- Run GPU execution (isAsync: should be running async thread) function ENT:Run(isAsync) - -- How many steps VM must make to keep up with execution - local Cycles - if isAsync then - -- Limit frequency - self.VM.Memory[65527] = math.Clamp(self.VM.Memory[65527],1,1200000) - - -- Calculate timing - Cycles = math.max(1,math.floor(self.VM.Memory[65527]*self.DeltaTime*0.5)) - self.VM.TimerDT = self.DeltaTime/Cycles - self.VM.TIMER = self.CurrentTime - self.VM.QuotaOverrunFunc = self.VM.AsyncQuotaOverrun - self.VM.ASYNC = 1 - else - Cycles = 50000 - self.VM:Reset() - self.VM.TimerDT = self.DeltaTime - self.VM.TIMER = self.CurrentTime - if self.VM.INIT == 0 then - self.VM.IP = self.VM.EntryPoint1 - self.VM.INIT = 1 - else - if self.VM.SyncQuotaIP then - self.VM.IP = self.VM.SyncQuotaIP - else - self.VM.IP = self.VM.EntryPoint0 - end - end - self.VM.QuotaOverrunFunc = self.VM.SyncQuotaOverrun - self.VM.ASYNC = 0 - end - local function getCode(self) - local mem = {} - for i=0,16 do - mem[i] = self.VM:ReadCell(i) - end - return mem - end - -- Run until interrupt, or if async thread then until async thread stops existing - while (Cycles > 0) and (self.VM.INTR == 0) do -- and (not (isAsync and (self.VM.Entrypoint4 == 0))) - self.VM.QuotaSupported = 1 - self.VM.Quota = self.VM.TMR + Cycles - local previousTMR = self.VM.TMR - if self.VM.QuotaOverrunFunc then - self.VM:QuotaOverrunFunc() - else - self.VM:Step() - end - Cycles = Cycles - (self.VM.TMR - previousTMR) - - if (self.VM.ASYNC == 0) and (Cycles < 0) and not self.VM.QuotaOverrunFunc then self.VM:Interrupt(17,0) end - if (self.VM.ASYNC == 0) and (self.VM.LateFrames > 1) then self.VM:Interrupt(18,0) end - self.VM.QuotaSupported = 0 - end + -- How many steps VM must make to keep up with execution + local Cycles + if isAsync then + -- Limit frequency + self.VM.Memory[65527] = math.Clamp(self.VM.Memory[65527],1,1200000) + + -- Calculate timing + Cycles = math.max(1,math.floor(self.VM.Memory[65527]*self.DeltaTime*0.5)) + self.VM.TimerDT = self.DeltaTime/Cycles + self.VM.TIMER = self.CurrentTime + self.VM.QuotaOverrunFunc = self.VM.AsyncQuotaOverrun + self.VM.ASYNC = 1 + else + Cycles = 50000 + self.VM:Reset() + self.VM.TimerDT = self.DeltaTime + self.VM.TIMER = self.CurrentTime + if self.VM.INIT == 0 then + self.VM.IP = self.VM.EntryPoint1 + self.VM.INIT = 1 + else + if self.VM.SyncQuotaIP then + self.VM.IP = self.VM.SyncQuotaIP + else + self.VM.IP = self.VM.EntryPoint0 + end + end + self.VM.QuotaOverrunFunc = self.VM.SyncQuotaOverrun + self.VM.ASYNC = 0 + end + local function getCode(self) + local mem = {} + for i=0,16 do + mem[i] = self.VM:ReadCell(i) + end + return mem + end + -- Run until interrupt, or if async thread then until async thread stops existing + while (Cycles > 0) and (self.VM.INTR == 0) do -- and (not (isAsync and (self.VM.Entrypoint4 == 0))) + self.VM.QuotaSupported = 1 + self.VM.Quota = self.VM.TMR + Cycles + local previousTMR = self.VM.TMR + if self.VM.QuotaOverrunFunc then + self.VM:QuotaOverrunFunc() + else + self.VM:Step() + end + Cycles = Cycles - (self.VM.TMR - previousTMR) + + if (self.VM.ASYNC == 0) and (Cycles < 0) and not self.VM.QuotaOverrunFunc then self.VM:Interrupt(17,0) end + if (self.VM.ASYNC == 0) and (self.VM.LateFrames > 1) then self.VM:Interrupt(18,0) end + self.VM.QuotaSupported = 0 + end -- Will also handle cleanup of quota overrun, since VM.QuotaOverrun sets itself to nil if the func is done - if isAsync then - self.VM.AsyncQuotaOverrun = self.VM.QuotaOverrunFunc - else - self.VM.SyncQuotaOverrun = self.VM.QuotaOverrunFunc - if self.VM.SyncQuotaOverrun then - self.VM.SyncQuotaIP = self.VM.IP - self.VM.LateFrames = self.VM.LateFrames + 1 - else - self.VM.SyncQuotaIP = nil - self.VM.LateFrames = 0 - end - end - - -- Reset INTR register for async thread - if self.VM.ASYNC == 1 then self.VM.INTR = 0 end + if isAsync then + self.VM.AsyncQuotaOverrun = self.VM.QuotaOverrunFunc + else + self.VM.SyncQuotaOverrun = self.VM.QuotaOverrunFunc + if self.VM.SyncQuotaOverrun then + self.VM.SyncQuotaIP = self.VM.IP + self.VM.LateFrames = self.VM.LateFrames + 1 + else + self.VM.SyncQuotaIP = nil + self.VM.LateFrames = 0 + end + end + + -- Reset INTR register for async thread + if self.VM.ASYNC == 1 then self.VM.INTR = 0 end end @@ -220,63 +220,63 @@ end -------------------------------------------------------------------------------- -- Request rendering to rendertarget function ENT:SetRendertarget(ID) - if ID == 1 then self:AssertSpriteBufferExists() end - - if not ID then -- Restore state - if self.In2D == true then self.In2D = false cam.End2D() end - if self.ScreenRTSet then - render.SetViewPort(0,0,self.ScreenRTWidth,self.ScreenRTHeight) - render.SetRenderTarget(self.ScreenRT) - - self.ScreenRTSet = nil - self.ScreenRT = nil - - self.VM.ScreenWidth = self.VM.VertexScreenWidth or 512 - self.VM.ScreenHeight = self.VM.VertexScreenHeight or 512 - end - if self.VertexCamSettings and (not self.In3D2D) then - cam.Start3D2D(self.VertexCamSettings[1],self.VertexCamSettings[2],self.VertexCamSettings[3]) - self.In3D2D = true - end - self.VM.CurrentBuffer = 2 - if self.VM.VertexMode == 0 then self.VM.RenderEnable = 0 end - else - -- Remember screen RT if this is the first switch - local noRT = true - if not self.ScreenRTSet then - self.ScreenRT = render.GetRenderTarget() - self.ScreenRTWidth = ScrW() - self.ScreenRTHeight = ScrH() - self.ScreenRTSet = true - noRT = false - end - - -- Bind correct rendertarget - local newRT - if ID == 0 - then newRT = self.GPU.RT - else newRT = self.SpriteGPU.RT - end - - if not newRT then return end - - -- Start drawing to the RT - if self.In2D == true then self.In2D = false cam.End2D() end - -- Get out of the 2D3D camera if its set - if self.In3D2D == true then self.In3D2D = false cam.End3D2D() end - - render.SetRenderTarget(newRT) - render.SetViewPort(0,0,512,512) - cam.Start2D() - self.In2D = true - - -- RT size - self.VM.ScreenWidth = 512 - self.VM.ScreenHeight = 512 - self.VM.CurrentBuffer = ID - - if self.VM.VertexMode == 0 then self.VM.RenderEnable = 1 end - end + if ID == 1 then self:AssertSpriteBufferExists() end + + if not ID then -- Restore state + if self.In2D == true then self.In2D = false cam.End2D() end + if self.ScreenRTSet then + render.SetViewPort(0,0,self.ScreenRTWidth,self.ScreenRTHeight) + render.SetRenderTarget(self.ScreenRT) + + self.ScreenRTSet = nil + self.ScreenRT = nil + + self.VM.ScreenWidth = self.VM.VertexScreenWidth or 512 + self.VM.ScreenHeight = self.VM.VertexScreenHeight or 512 + end + if self.VertexCamSettings and (not self.In3D2D) then + cam.Start3D2D(self.VertexCamSettings[1],self.VertexCamSettings[2],self.VertexCamSettings[3]) + self.In3D2D = true + end + self.VM.CurrentBuffer = 2 + if self.VM.VertexMode == 0 then self.VM.RenderEnable = 0 end + else + -- Remember screen RT if this is the first switch + local noRT = true + if not self.ScreenRTSet then + self.ScreenRT = render.GetRenderTarget() + self.ScreenRTWidth = ScrW() + self.ScreenRTHeight = ScrH() + self.ScreenRTSet = true + noRT = false + end + + -- Bind correct rendertarget + local newRT + if ID == 0 + then newRT = self.GPU.RT + else newRT = self.SpriteGPU.RT + end + + if not newRT then return end + + -- Start drawing to the RT + if self.In2D == true then self.In2D = false cam.End2D() end + -- Get out of the 2D3D camera if its set + if self.In3D2D == true then self.In3D2D = false cam.End3D2D() end + + render.SetRenderTarget(newRT) + render.SetViewPort(0,0,512,512) + cam.Start2D() + self.In2D = true + + -- RT size + self.VM.ScreenWidth = 512 + self.VM.ScreenHeight = 512 + self.VM.CurrentBuffer = ID + + if self.VM.VertexMode == 0 then self.VM.RenderEnable = 1 end + end end @@ -285,81 +285,81 @@ end -------------------------------------------------------------------------------- -- Render GPU to rendertarget function ENT:RenderGPU() - self.VM.VertexMode = 0 - self:SetRendertarget(0) - - if self.VM:ReadCell(65531) == 0 then -- Halt register - if self.VM:ReadCell(65533) == 1 then -- Hardware clear - surface.SetDrawColor(0,0,0,255) - surface.DrawRect(0,0,self.VM.ScreenWidth,self.VM.ScreenHeight) - end - if self.VM:ReadCell(65535) == 1 then -- Clk - self:Run(false) - end - end - - -- Restore screen rendertarget - self:SetRendertarget() + self.VM.VertexMode = 0 + self:SetRendertarget(0) + + if self.VM:ReadCell(65531) == 0 then -- Halt register + if self.VM:ReadCell(65533) == 1 then -- Hardware clear + surface.SetDrawColor(0,0,0,255) + surface.DrawRect(0,0,self.VM.ScreenWidth,self.VM.ScreenHeight) + end + if self.VM:ReadCell(65535) == 1 then -- Clk + self:Run(false) + end + end + + -- Restore screen rendertarget + self:SetRendertarget() end -- Render GPU to world function ENT:RenderVertex(width,height) - self.VM.VertexMode = 1 - self.VM.RenderEnable = 1 - self:SetRendertarget() - - self.VM.ScreenWidth = width or 512 - self.VM.ScreenHeight = height or 512 - self.VM.VertexScreenWidth = self.VM.ScreenWidth - self.VM.VertexScreenHeight = self.VM.ScreenHeight - - if self.VM:ReadCell(65531) == 0 then -- Halt register - if self.VM:ReadCell(65533) == 1 then -- Hardware clear - surface.SetDrawColor(0,0,0,255) - surface.DrawRect(0,0,self.VM.ScreenWidth,self.VM.ScreenHeight) - end - if self.VM:ReadCell(65535) == 1 then -- Clk - self:Run(false) - end - end - - self.VM.VertexScreenWidth = nil - self.VM.VertexScreenHeight = nil - self.VM.VertexMode = 0 - self:SetRendertarget() + self.VM.VertexMode = 1 + self.VM.RenderEnable = 1 + self:SetRendertarget() + + self.VM.ScreenWidth = width or 512 + self.VM.ScreenHeight = height or 512 + self.VM.VertexScreenWidth = self.VM.ScreenWidth + self.VM.VertexScreenHeight = self.VM.ScreenHeight + + if self.VM:ReadCell(65531) == 0 then -- Halt register + if self.VM:ReadCell(65533) == 1 then -- Hardware clear + surface.SetDrawColor(0,0,0,255) + surface.DrawRect(0,0,self.VM.ScreenWidth,self.VM.ScreenHeight) + end + if self.VM:ReadCell(65535) == 1 then -- Clk + self:Run(false) + end + end + + self.VM.VertexScreenWidth = nil + self.VM.VertexScreenHeight = nil + self.VM.VertexMode = 0 + self:SetRendertarget() end -- Process misc GPU stuff function ENT:RenderMisc(pos, ang, resolution, aspect, monitor) - self.VM:WriteCell(65513, aspect) - local ply = LocalPlayer() - local trace = ply:GetEyeTraceNoCursor() - if (trace.Entity and trace.Entity:IsValid() and trace.Entity == self) then - local dist = trace.Normal:Dot(trace.HitNormal)*trace.Fraction*(-16384) - dist = math.max(dist, trace.Fraction*16384-self:BoundingRadius()) - - if (dist < 256) then - local pos = WorldToLocal( trace.HitPos, Angle(), pos, ang ) - local x = 0.5+pos.x/(monitor.RS*(1024/monitor.RatioX)) - local y = 0.5-pos.y/(monitor.RS*1024) - - local cursorOffset = 0 - if self.VM:ReadCell(65532) == 1 then -- Check for vertex mode to counter the faulty offset - cursorOffset = 0.5 - end - - self.VM:WriteCell(65505,x - cursorOffset) - self.VM:WriteCell(65504,y - cursorOffset) - - if (self.VM:ReadCell(65503) == 1) then - surface.SetDrawColor(255,255,255,255) - surface.SetTexture(surface.GetTextureID("gui/arrow")) - x = math.Clamp(x,0 + cursorOffset, 1 + cursorOffset) - y = math.Clamp(y,0 + cursorOffset, 1 + cursorOffset) - surface.DrawTexturedRectRotated(-512*aspect+x*1024*aspect+10,-512+y*1024+12,32,32,45) - end - end - end + self.VM:WriteCell(65513, aspect) + local ply = LocalPlayer() + local trace = ply:GetEyeTraceNoCursor() + if (trace.Entity and trace.Entity:IsValid() and trace.Entity == self) then + local dist = trace.Normal:Dot(trace.HitNormal)*trace.Fraction*(-16384) + dist = math.max(dist, trace.Fraction*16384-self:BoundingRadius()) + + if (dist < 256) then + local pos = WorldToLocal( trace.HitPos, Angle(), pos, ang ) + local x = 0.5+pos.x/(monitor.RS*(1024/monitor.RatioX)) + local y = 0.5-pos.y/(monitor.RS*1024) + + local cursorOffset = 0 + if self.VM:ReadCell(65532) == 1 then -- Check for vertex mode to counter the faulty offset + cursorOffset = 0.5 + end + + self.VM:WriteCell(65505,x - cursorOffset) + self.VM:WriteCell(65504,y - cursorOffset) + + if (self.VM:ReadCell(65503) == 1) then + surface.SetDrawColor(255,255,255,255) + surface.SetTexture(surface.GetTextureID("gui/arrow")) + x = math.Clamp(x,0 + cursorOffset, 1 + cursorOffset) + y = math.Clamp(y,0 + cursorOffset, 1 + cursorOffset) + surface.DrawTexturedRectRotated(-512*aspect+x*1024*aspect+10,-512+y*1024+12,32,32,45) + end + end + end end @@ -374,7 +374,7 @@ function ENT:Draw() -- Draw GPU itself self:DrawModel() - + local tone = render.GetToneMappingScaleLinear() render.SetToneMappingScaleLinear(VECTOR_1_1_1) @@ -418,7 +418,7 @@ function ENT:Draw() end end - -- Draw GPU to world + -- Draw GPU to world if self.ChipType == 0 then -- Not a microchip if self.VM.Memory[65532] == 0 then self.GPU:Render( @@ -458,7 +458,7 @@ function ENT:Draw() end end end - + render.SetToneMappingScaleLinear(tone) Wire_Render(self) end @@ -468,11 +468,11 @@ end -------------------------------------------------------------------------------- -- Think function function ENT:Think() - for k,v in pairs(self.VM.MemBusBuffer) do - RunConsoleCommand("wgm", self:EntIndex(), k, v) - end - self.VM.MemBusBuffer = {} - self.VM.MemBusCount = 0 + for k,v in pairs(self.VM.MemBusBuffer) do + RunConsoleCommand("wgm", self:EntIndex(), k, v) + end + self.VM.MemBusBuffer = {} + self.VM.MemBusCount = 0 end @@ -481,16 +481,16 @@ end -------------------------------------------------------------------------------- -- HUD drawing function local function GPU_DrawHUD() - local videoSource = HUDLookup[LocalPlayer():EntIndex()] - if videoSource then - local videoGPU = ents.GetByIndex(videoSource) - if videoGPU and videoGPU:IsValid() and videoGPU.RenderVertex then - local screenWidth = ScrW() - local screenHeight = ScrH() - - videoGPU:Draw() - videoGPU:RenderVertex(screenWidth,screenHeight) - end - end + local videoSource = HUDLookup[LocalPlayer():EntIndex()] + if videoSource then + local videoGPU = ents.GetByIndex(videoSource) + if videoGPU and videoGPU:IsValid() and videoGPU.RenderVertex then + local screenWidth = ScrW() + local screenHeight = ScrH() + + videoGPU:Draw() + videoGPU:RenderVertex(screenWidth,screenHeight) + end + end end hook.Add("HUDPaint","wire_gpu_drawhud",GPU_DrawHUD) diff --git a/lua/entities/gmod_wire_gpu/init.lua b/lua/entities/gmod_wire_gpu/init.lua index 2815f2ea47..382dc227c2 100644 --- a/lua/entities/gmod_wire_gpu/init.lua +++ b/lua/entities/gmod_wire_gpu/init.lua @@ -11,106 +11,106 @@ ENT.WireDebugName = "ZGPU" -------------------------------------------------------------------------------- function ENT:Initialize() - -- Physics properties - self:PhysicsInit(SOLID_VPHYSICS) - self:SetMoveType(MOVETYPE_VPHYSICS) - self:SetSolid(SOLID_VPHYSICS) - - -- Inputs/outputs - self.Inputs = Wire_CreateInputs(self, { "Clk", "Reset", "IOBus", "MemBus", "VideoOut" }) - self.Outputs = Wire_CreateOutputs(self, { "Memory" }) - - -- Setup platform settings - self.Clk = 1 - self.MemBusScanAddress = 65536 - self.SerialNo = CPULib.GenerateSN("GPU") - self:SetMemoryModel("64k",true) - - -- Create serverside memory and cache - self.Memory = {} - self.Cache = GPUCacheManager(self) - - -- Connected monitors - self.Monitors = { } - self:UpdateClientMonitorState() + -- Physics properties + self:PhysicsInit(SOLID_VPHYSICS) + self:SetMoveType(MOVETYPE_VPHYSICS) + self:SetSolid(SOLID_VPHYSICS) + + -- Inputs/outputs + self.Inputs = Wire_CreateInputs(self, { "Clk", "Reset", "IOBus", "MemBus", "VideoOut" }) + self.Outputs = Wire_CreateOutputs(self, { "Memory" }) + + -- Setup platform settings + self.Clk = 1 + self.MemBusScanAddress = 65536 + self.SerialNo = CPULib.GenerateSN("GPU") + self:SetMemoryModel("64k",true) + + -- Create serverside memory and cache + self.Memory = {} + self.Cache = GPUCacheManager(self) + + -- Connected monitors + self.Monitors = { } + self:UpdateClientMonitorState() end function ENT:UpdateClientMonitorState() - umsg.Start("wire_gpu_monitorstate") - umsg.Long(self:EntIndex()) - umsg.Short(#self.Monitors) - for idx=1,#self.Monitors do - umsg.Long(self.Monitors[idx]) - end - umsg.End() + umsg.Start("wire_gpu_monitorstate") + umsg.Long(self:EntIndex()) + umsg.Short(#self.Monitors) + for idx=1,#self.Monitors do + umsg.Long(self.Monitors[idx]) + end + umsg.End() end -------------------------------------------------------------------------------- -- Set processor -------------------------------------------------------------------------------- function ENT:SetMemoryModel(model,initial) - if model then - for i=6,11 do - if model == (2^i).."k" then - self.RAMSize = (2^i)*1024 - self.ChipType = 0 - elseif model == (2^i).."kc" then - self.RAMSize = (2^i)*1024 - self.ChipType = 1 - end - end - end - - if not initial then - timer.Simple(0.1+math.random()*0.3, - function() - if not self:IsValid() then return end - - umsg.Start("wire_gpu_memorymodel") - umsg.Long(self:EntIndex()) - umsg.Long (self.RAMSize) - umsg.Float(self.SerialNo) - umsg.Short(self.ChipType) - umsg.End() - end) - end + if model then + for i=6,11 do + if model == (2^i).."k" then + self.RAMSize = (2^i)*1024 + self.ChipType = 0 + elseif model == (2^i).."kc" then + self.RAMSize = (2^i)*1024 + self.ChipType = 1 + end + end + end + + if not initial then + timer.Simple(0.1+math.random()*0.3, + function() + if not self:IsValid() then return end + + umsg.Start("wire_gpu_memorymodel") + umsg.Long(self:EntIndex()) + umsg.Long (self.RAMSize) + umsg.Float(self.SerialNo) + umsg.Short(self.ChipType) + umsg.End() + end) + 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) + 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 -------------------------------------------------------------------------------- function ENT:ResendCache(player) - timer.Simple(0.4+math.random()*1.2, - function() - if not self:IsValid() then return end - - self.Cache:Flush() - for address,value in pairs(self.Memory) do - self:WriteCell(address,value,player) - end - self.Cache:Flush(player) - - self:WriteCell(65534,1,player) -- Reset GPU - self:WriteCell(65535,self.Clk,player) -- Update Clk - end) + timer.Simple(0.4+math.random()*1.2, + function() + if not self:IsValid() then return end + + self.Cache:Flush() + for address,value in pairs(self.Memory) do + self:WriteCell(address,value,player) + end + self.Cache:Flush(player) + + self:WriteCell(65534,1,player) -- Reset GPU + self:WriteCell(65535,self.Clk,player) -- Update Clk + end) end local function GPU_PlayerRespawn(player) - for _,Entity in ipairs(ents.FindByClass("gmod_wire_gpu")) do - Entity:ResendCache(player) - end + for _,Entity in ipairs(ents.FindByClass("gmod_wire_gpu")) do + Entity:ResendCache(player) + end end hook.Add("PlayerInitialSpawn", "GPUPlayerRespawn", GPU_PlayerRespawn) concommand.Add("wire_gpu_resendcache", GPU_PlayerRespawn) @@ -121,7 +121,7 @@ concommand.Add("wire_gpu_resendcache", GPU_PlayerRespawn) -- Checks if address is valid -------------------------------------------------------------------------------- local function isValidAddress(n) - return n and (math.floor(n) == n) and (n >= -140737488355327) and (n <= 140737488355328) + return n and (math.floor(n) == n) and (n >= -140737488355327) and (n <= 140737488355328) end @@ -130,22 +130,22 @@ end -- Read cell from GPU memory -------------------------------------------------------------------------------- function ENT:ReadCell(Address) - Address = math.floor(Address) - -- Check if address is valid - if not isValidAddress(Address) then - self:Interrupt(15,Address) - return - end - - if (Address < 0) or (Address >= self.RAMSize) then - return nil - else - if self.Memory[Address] then - return self.Memory[Address] - else - return 0 - end - end + Address = math.floor(Address) + -- Check if address is valid + if not isValidAddress(Address) then + self:Interrupt(15,Address) + return + end + + if (Address < 0) or (Address >= self.RAMSize) then + return nil + else + if self.Memory[Address] then + return self.Memory[Address] + else + return 0 + end + end end @@ -153,23 +153,23 @@ end -- Write cell to GPU memory -------------------------------------------------------------------------------- function ENT:WriteCell(Address, Value, Player) - Address = math.floor(Address) - if (Address < 0) or (Address >= self.RAMSize) then - return false - else - if (Address ~= 65535) and (Address ~= 65534) and (Address ~= 65502) then - -- Write to internal memory - self.Memory[Address] = Value - - -- Add address to cache if cache is not big enough yet - self.Cache:Write(Address,Value,Player) - return true - else - self.Cache:Flush(Player) - self.Cache:WriteNow(Address,Value,Player) - end - return true - end + Address = math.floor(Address) + if (Address < 0) or (Address >= self.RAMSize) then + return false + else + if (Address ~= 65535) and (Address ~= 65534) and (Address ~= 65502) then + -- Write to internal memory + self.Memory[Address] = Value + + -- Add address to cache if cache is not big enough yet + self.Cache:Write(Address,Value,Player) + return true + else + self.Cache:Flush(Player) + self.Cache:WriteNow(Address,Value,Player) + end + return true + end end @@ -186,19 +186,19 @@ end -- Write advanced dupe -------------------------------------------------------------------------------- function ENT:BuildDupeInfo() - local info = BaseClass.BuildDupeInfo(self) or {} + local info = BaseClass.BuildDupeInfo(self) or {} - info.SerialNo = self.SerialNo - info.RAMSize = self.RAMSize - info.ChipType = self.ChipType - info.Memory = {} - info.ZVMExtensions = self.ZVMExtensions + info.SerialNo = self.SerialNo + 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 - end + for address = 0,self.RAMSize-1 do + if self.Memory[address] and (self.Memory[address] ~= 0) then info.Memory[address] = self.Memory[address] end + end - return info + return info end @@ -206,20 +206,20 @@ end -- Read from advanced dupe -------------------------------------------------------------------------------- function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) - BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) - self.SerialNo = info.SerialNo or 999999 - self.RAMSize = math.Clamp(info.RAMSize or 65536, 0, 2097152) - self.ChipType = info.ChipType or 0 - self.Memory = {} - self:SetExtensionLoadOrder(info.ZVMExtensions) + self.SerialNo = info.SerialNo or 999999 + 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 - end + for address = 0,self.RAMSize-1 do + if info.Memory[address] then self.Memory[address] = tonumber(info.Memory[address]) or 0 end + end - self:SetMemoryModel() - self:ResendCache(nil) + self:SetMemoryModel() + self:ResendCache(nil) end @@ -227,12 +227,12 @@ end -- Handle external input -------------------------------------------------------------------------------- function ENT:TriggerInput(iname, value) - if iname == "Clk" then - self.Clk = (value >= 1 and 1 or 0) - self:WriteCell(65535,self.Clk) - elseif iname == "Reset" then - if value >= 1.0 then self:WriteCell(65534,1) end - end + if iname == "Clk" then + self.Clk = (value >= 1 and 1 or 0) + self:WriteCell(65535,self.Clk) + elseif iname == "Reset" then + if value >= 1.0 then self:WriteCell(65534,1) end + end end @@ -240,33 +240,33 @@ end -- Find out all monitors connected to the GPU -------------------------------------------------------------------------------- function ENT:QueryMonitors(entity) - self.QueryRecurseCounter = self.QueryRecurseCounter + 1 - if self.QueryRecurseCounter > 128 then return end - if (not entity) or (not entity:IsValid()) then return end - - if entity:GetClass() == "gmod_wire_gpu" then -- VideoOut connected to a GPU - table.insert(self.QueryResult,entity:EntIndex()) - elseif entity.Socket then -- VideoOut connected to a plug - self:QueryMonitors(entity.Socket.Inputs.Memory.Src) - elseif entity.Plug then -- VideoOut connected to a socket - self:QueryMonitors(entity.Plug.Inputs.Memory.Src) - elseif entity.Ply and entity.Ply:IsValid() then -- VideoOut connected to pod - table.insert(self.QueryResult,entity.Ply:EntIndex()) - elseif entity:GetClass() == "gmod_wire_addressbus" then -- VideoOut connected to address bus - self:QueryMonitors(entity.Inputs.Memory1.Src) - self:QueryMonitors(entity.Inputs.Memory2.Src) - self:QueryMonitors(entity.Inputs.Memory3.Src) - self:QueryMonitors(entity.Inputs.Memory4.Src) - elseif entity:GetClass() == "gmod_wire_extbus" then -- VideoOut connected to ext bus - self:QueryMonitors(entity.Inputs.Memory1.Src) - self:QueryMonitors(entity.Inputs.Memory2.Src) - self:QueryMonitors(entity.Inputs.Memory3.Src) - self:QueryMonitors(entity.Inputs.Memory4.Src) - self:QueryMonitors(entity.Inputs.Memory5.Src) - self:QueryMonitors(entity.Inputs.Memory6.Src) - self:QueryMonitors(entity.Inputs.Memory7.Src) - self:QueryMonitors(entity.Inputs.Memory8.Src) - end + self.QueryRecurseCounter = self.QueryRecurseCounter + 1 + if self.QueryRecurseCounter > 128 then return end + if (not entity) or (not entity:IsValid()) then return end + + if entity:GetClass() == "gmod_wire_gpu" then -- VideoOut connected to a GPU + table.insert(self.QueryResult,entity:EntIndex()) + elseif entity.Socket then -- VideoOut connected to a plug + self:QueryMonitors(entity.Socket.Inputs.Memory.Src) + elseif entity.Plug then -- VideoOut connected to a socket + self:QueryMonitors(entity.Plug.Inputs.Memory.Src) + elseif entity.Ply and entity.Ply:IsValid() then -- VideoOut connected to pod + table.insert(self.QueryResult,entity.Ply:EntIndex()) + elseif entity:GetClass() == "gmod_wire_addressbus" then -- VideoOut connected to address bus + self:QueryMonitors(entity.Inputs.Memory1.Src) + self:QueryMonitors(entity.Inputs.Memory2.Src) + self:QueryMonitors(entity.Inputs.Memory3.Src) + self:QueryMonitors(entity.Inputs.Memory4.Src) + elseif entity:GetClass() == "gmod_wire_extbus" then -- VideoOut connected to ext bus + self:QueryMonitors(entity.Inputs.Memory1.Src) + self:QueryMonitors(entity.Inputs.Memory2.Src) + self:QueryMonitors(entity.Inputs.Memory3.Src) + self:QueryMonitors(entity.Inputs.Memory4.Src) + self:QueryMonitors(entity.Inputs.Memory5.Src) + self:QueryMonitors(entity.Inputs.Memory6.Src) + self:QueryMonitors(entity.Inputs.Memory7.Src) + self:QueryMonitors(entity.Inputs.Memory8.Src) + end end @@ -274,112 +274,112 @@ end -- Update cache and external connections -------------------------------------------------------------------------------- function ENT:Think() - -- Update IOBus - if self.Inputs.IOBus.Src then - -- Was there any update in that that would require flushing - local DataUpdated = false - - -- Update any cells that must be updated - for port = 0,1023 do - if self.Inputs.IOBus.Src.ReadCell then - local var = self.Inputs.IOBus.Src:ReadCell(port) - if var then - if self:ReadCell(port+63488) ~= var then - self:WriteCell(port+63488,var) - DataUpdated = true - end - end - end - end - - -- Flush updated data - if DataUpdated then self.Cache:Flush() end - end - - -- Update MemBus - if self.Inputs.MemBus.Src then - for address=self.MemBusScanAddress,self.MemBusScanAddress+1023 do - local var = self.Inputs.MemBus.Src:ReadCell(address-65536) - if var then - if self:ReadCell(address) ~= var then - self:WriteCell(address,var) - end - end - end - self.MemBusScanAddress = self.MemBusScanAddress + 1024 - if self.MemBusScanAddress >= 131072 then - self.MemBusScanAddress = 65536 - end - end - - -- Flush any data in cache - self.Cache:Flush() - - -- Update video output, and send any changes to client - if self.Inputs.VideoOut.Src then - self.QueryRecurseCounter = 0 - self.QueryResult = { } - self:QueryMonitors(self.Inputs.VideoOut.Src) - - -- Check if monitors setup has changed - local monitorsChanged = false - for k,v in pairs(self.QueryResult) do - if self.Monitors[k] ~= v then - monitorsChanged = true - break - end - end - - if not monitorsChanged then - for k,v in pairs(self.Monitors) do - if self.QueryResult[k] ~= v then - monitorsChanged = true - break - end - end - end - - if #self.QueryResult ~= #self.Monitors then monitorsChanged = true end - - if monitorsChanged then - self.Monitors = self.QueryResult - end - - -- Send update to all clients - if monitorsChanged then - self:UpdateClientMonitorState() - end - end - - -- Update serverside cursor - local model = self:GetModel() - local monitor = WireGPU_Monitors[model] - local ang = self:LocalToWorldAngles(monitor.rot) - local pos = self:LocalToWorld(monitor.offset) - - for _,player in pairs(player.GetAll()) do - local trace = player:GetEyeTraceNoCursor() - local ent = trace.Entity - if ent:IsValid() then - local dist = trace.Normal:Dot(trace.HitNormal)*trace.Fraction*(-16384) - dist = math.max(dist, trace.Fraction*16384-ent:BoundingRadius()) - - if dist < 64 and ent == self then - if player:KeyDown(IN_ATTACK) or player:KeyDown(IN_USE) then - self:WriteCell(65502,1) - end - local cpos = WorldToLocal(trace.HitPos, Angle(), pos, ang) - local cx = 0.5+cpos.x/(monitor.RS*(512/monitor.RatioX)) - local cy = 0.5-cpos.y/(monitor.RS*(512)) - - self.Memory[65505] = cx - self.Memory[65504] = cy - end - end - end - - self:NextThink(CurTime()+0.05) - return true + -- Update IOBus + if self.Inputs.IOBus.Src then + -- Was there any update in that that would require flushing + local DataUpdated = false + + -- Update any cells that must be updated + for port = 0,1023 do + if self.Inputs.IOBus.Src.ReadCell then + local var = self.Inputs.IOBus.Src:ReadCell(port) + if var then + if self:ReadCell(port+63488) ~= var then + self:WriteCell(port+63488,var) + DataUpdated = true + end + end + end + end + + -- Flush updated data + if DataUpdated then self.Cache:Flush() end + end + + -- Update MemBus + if self.Inputs.MemBus.Src then + for address=self.MemBusScanAddress,self.MemBusScanAddress+1023 do + local var = self.Inputs.MemBus.Src:ReadCell(address-65536) + if var then + if self:ReadCell(address) ~= var then + self:WriteCell(address,var) + end + end + end + self.MemBusScanAddress = self.MemBusScanAddress + 1024 + if self.MemBusScanAddress >= 131072 then + self.MemBusScanAddress = 65536 + end + end + + -- Flush any data in cache + self.Cache:Flush() + + -- Update video output, and send any changes to client + if self.Inputs.VideoOut.Src then + self.QueryRecurseCounter = 0 + self.QueryResult = { } + self:QueryMonitors(self.Inputs.VideoOut.Src) + + -- Check if monitors setup has changed + local monitorsChanged = false + for k,v in pairs(self.QueryResult) do + if self.Monitors[k] ~= v then + monitorsChanged = true + break + end + end + + if not monitorsChanged then + for k,v in pairs(self.Monitors) do + if self.QueryResult[k] ~= v then + monitorsChanged = true + break + end + end + end + + if #self.QueryResult ~= #self.Monitors then monitorsChanged = true end + + if monitorsChanged then + self.Monitors = self.QueryResult + end + + -- Send update to all clients + if monitorsChanged then + self:UpdateClientMonitorState() + end + end + + -- Update serverside cursor + local model = self:GetModel() + local monitor = WireGPU_Monitors[model] + local ang = self:LocalToWorldAngles(monitor.rot) + local pos = self:LocalToWorld(monitor.offset) + + for _,player in pairs(player.GetAll()) do + local trace = player:GetEyeTraceNoCursor() + local ent = trace.Entity + if ent:IsValid() then + local dist = trace.Normal:Dot(trace.HitNormal)*trace.Fraction*(-16384) + dist = math.max(dist, trace.Fraction*16384-ent:BoundingRadius()) + + if dist < 64 and ent == self then + if player:KeyDown(IN_ATTACK) or player:KeyDown(IN_USE) then + self:WriteCell(65502,1) + end + local cpos = WorldToLocal(trace.HitPos, Angle(), pos, ang) + local cx = 0.5+cpos.x/(monitor.RS*(512/monitor.RatioX)) + local cy = 0.5-cpos.y/(monitor.RS*(512)) + + self.Memory[65505] = cx + self.Memory[65504] = cy + end + end + end + + self:NextThink(CurTime()+0.05) + return true end @@ -387,22 +387,22 @@ end -- GPU-to-MemBus support -------------------------------------------------------------------------------- concommand.Add("wgm", function(player, command, args) - -- Find the referenced GPU - local GPU = ents.GetByIndex(args[1]) - if not GPU then return end - if not GPU:IsValid() then return end + -- Find the referenced GPU + local GPU = ents.GetByIndex(args[1]) + if not GPU then return end + if not GPU:IsValid() then return end - -- Must be a valid GPU, and belong to the caller + -- Must be a valid GPU, and belong to the caller -- if GPU.player ~= player then return end - -- Write on membus - local Address = tonumber(args[2]) or 0 - local Value = tonumber(args[3]) or 0 + -- Write on membus + local Address = tonumber(args[2]) or 0 + local Value = tonumber(args[3]) or 0 - -- Perform external write - if GPU.Inputs.MemBus.Src then - GPU.Inputs.MemBus.Src:WriteCell(Address-65536,Value) - end + -- Perform external write + if GPU.Inputs.MemBus.Src then + GPU.Inputs.MemBus.Src:WriteCell(Address-65536,Value) + end end) duplicator.RegisterEntityClass("gmod_wire_gpu", WireLib.MakeWireEnt, "Data") diff --git a/lua/entities/gmod_wire_spu/cl_init.lua b/lua/entities/gmod_wire_spu/cl_init.lua index de014be929..3996d21f4d 100644 --- a/lua/entities/gmod_wire_spu/cl_init.lua +++ b/lua/entities/gmod_wire_spu/cl_init.lua @@ -17,53 +17,53 @@ local HUDLookup = {} -- Update sound emitters certain SPU is linked to -------------------------------------------------------------------------------- local function recalculateSoundEmitterLookup() - SoundEmitterLookup = {} - HUDLookup = {} - for gpuIdx,linkedSPUs in pairs(SoundEmitters) do - for _,linkedSPUIdx in pairs(linkedSPUs) do - local linkedEnt = ents.GetByIndex(linkedSPUIdx) - if linkedEnt and linkedEnt:IsValid() then - if linkedEnt:IsPlayer() then - HUDLookup[linkedSPUIdx] = gpuIdx - else - SoundEmitterLookup[linkedSPUIdx] = gpuIdx - end - end - end - end + SoundEmitterLookup = {} + HUDLookup = {} + for gpuIdx,linkedSPUs in pairs(SoundEmitters) do + for _,linkedSPUIdx in pairs(linkedSPUs) do + local linkedEnt = ents.GetByIndex(linkedSPUIdx) + if linkedEnt and linkedEnt:IsValid() then + if linkedEnt:IsPlayer() then + HUDLookup[linkedSPUIdx] = gpuIdx + else + SoundEmitterLookup[linkedSPUIdx] = gpuIdx + end + end + end + end end local function SPU_SoundEmitterState(um) - -- Read sound emitters for this SPU - local gpuIdx = um:ReadLong() - SoundEmitters[gpuIdx] = {} - - -- Fetch all sound emitters - local count = um:ReadShort() - for i=1,count do - SoundEmitters[gpuIdx][i] = um:ReadLong() - end - - -- Recalculate small lookup table for sound emitter system - recalculateSoundEmitterLookup() + -- Read sound emitters for this SPU + local gpuIdx = um:ReadLong() + SoundEmitters[gpuIdx] = {} + + -- Fetch all sound emitters + local count = um:ReadShort() + for i=1,count do + SoundEmitters[gpuIdx][i] = um:ReadLong() + end + + -- Recalculate small lookup table for sound emitter system + recalculateSoundEmitterLookup() end usermessage.Hook("wire_spu_soundstate", SPU_SoundEmitterState) local function SPU_SoundSources(um) - local SPU = ents.GetByIndex(um:ReadLong()) - if not SPU then return end - if not SPU:IsValid() then return end - - for i=0,WireSPU_MaxChannels-1 do - SPU.SoundSources[i] = ents.GetByIndex(um:ReadLong()) - SPU.SoundSources[i]:SetNoDraw(true) - SPU.SoundSources[i]:SetModelScale(0,0) - end - - -- Reset VM - SPU.VM:Reset() - SPU.VM.Memory[65535] = 1 - SPU.VM.Memory[65527] = 300000 + local SPU = ents.GetByIndex(um:ReadLong()) + if not SPU then return end + if not SPU:IsValid() then return end + + for i=0,WireSPU_MaxChannels-1 do + SPU.SoundSources[i] = ents.GetByIndex(um:ReadLong()) + SPU.SoundSources[i]:SetNoDraw(true) + SPU.SoundSources[i]:SetModelScale(0,0) + end + + -- Reset VM + SPU.VM:Reset() + SPU.VM.Memory[65535] = 1 + SPU.VM.Memory[65527] = 300000 end usermessage.Hook("wire_spu_soundsources", SPU_SoundSources) @@ -74,33 +74,33 @@ usermessage.Hook("wire_spu_soundsources", SPU_SoundSources) -- Update SPU features/memory model -------------------------------------------------------------------------------- local function SPU_MemoryModel(um) - local SPU = ents.GetByIndex(um:ReadLong()) - if not SPU then return end - if not SPU:IsValid() then return end - - if SPU.VM then - SPU.VM.ROMSize = um:ReadLong() - SPU.VM.SerialNo = um:ReadFloat() - SPU.VM.RAMSize = SPU.VM.ROMSize - else - SPU.ROMSize = um:ReadLong() - SPU.SerialNo = um:ReadFloat() - end - SPU.ChipType = um:ReadShort() + local SPU = ents.GetByIndex(um:ReadLong()) + if not SPU then return end + if not SPU:IsValid() then return end + + if SPU.VM then + SPU.VM.ROMSize = um:ReadLong() + SPU.VM.SerialNo = um:ReadFloat() + SPU.VM.RAMSize = SPU.VM.ROMSize + else + SPU.ROMSize = um:ReadLong() + SPU.SerialNo = um:ReadFloat() + end + SPU.ChipType = um:ReadShort() 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 + 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) @@ -108,30 +108,30 @@ usermessage.Hook("wire_spu_extensions", SPU_SetExtensions) -------------------------------------------------------------------------------- function ENT:Initialize() - -- Create virtual machine - self.VM = CPULib.VirtualMachine() - self.VM.SerialNo = CPULib.GenerateSN("SPU") - self.VM.RAMSize = 65536 - self.VM.ROMSize = 65536 - self.VM.PCAP = 0 - self.VM.RQCAP = 0 - 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 = {} - - -- Hard-reset VM and override it - self:OverrideVM() - self.VMReset = 0 - - -- Setup caching - GPULib.ClientCacheCallback(self,function(Address,Value) - self.VM:WriteCell(Address,Value) - self.VM.ROM[Address] = Value - end) + -- Create virtual machine + self.VM = CPULib.VirtualMachine() + self.VM.SerialNo = CPULib.GenerateSN("SPU") + self.VM.RAMSize = 65536 + self.VM.ROMSize = 65536 + self.VM.PCAP = 0 + self.VM.RQCAP = 0 + 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 = {} + + -- Hard-reset VM and override it + self:OverrideVM() + self.VMReset = 0 + + -- Setup caching + GPULib.ClientCacheCallback(self,function(Address,Value) + self.VM:WriteCell(Address,Value) + self.VM.ROM[Address] = Value + end) end @@ -140,12 +140,12 @@ end -------------------------------------------------------------------------------- -- Entity deleted function ENT:OnRemove() - GPULib.ClientCacheCallback(self,nil) - if self.VM.Channel then - for k,v in pairs(self.VM.Channel) do - v.Sound:Stop() - end - end + GPULib.ClientCacheCallback(self,nil) + if self.VM.Channel then + for k,v in pairs(self.VM.Channel) do + v.Sound:Stop() + end + end end @@ -154,47 +154,47 @@ end -------------------------------------------------------------------------------- -- Run SPU execution function ENT:Run() - -- Limit frequency - self.VM.Memory[65527] = math.Clamp(self.VM.Memory[65527],1,1200000) - - -- Calculate timing - local Cycles = math.max(1,math.floor(self.VM.Memory[65527]*self.DeltaTime*0.5)) - self.VM.TimerDT = self.DeltaTime/Cycles - self.VM.TIMER = self.CurrentTime - - -- Run until interrupt, or if async thread then until async thread stops existing - while (Cycles > 0) and (self.VM.INTR == 0) do - local previousTMR = self.VM.TMR - self.VM.QuotaSupported = 1 - self.VM.Quota = self.VM.TMR+Cycles - if self.VM.QuotaOverrunFunc then - self.VM:QuotaOverrunFunc() - else - self.VM:Step() - end - self.QuotaSupported = 0 - Cycles = Cycles - (self.VM.TMR - previousTMR) - end - - -- Reset INTR register for async thread - self.VM.INTR = 0 + -- Limit frequency + self.VM.Memory[65527] = math.Clamp(self.VM.Memory[65527],1,1200000) + + -- Calculate timing + local Cycles = math.max(1,math.floor(self.VM.Memory[65527]*self.DeltaTime*0.5)) + self.VM.TimerDT = self.DeltaTime/Cycles + self.VM.TIMER = self.CurrentTime + + -- Run until interrupt, or if async thread then until async thread stops existing + while (Cycles > 0) and (self.VM.INTR == 0) do + local previousTMR = self.VM.TMR + self.VM.QuotaSupported = 1 + self.VM.Quota = self.VM.TMR+Cycles + if self.VM.QuotaOverrunFunc then + self.VM:QuotaOverrunFunc() + else + self.VM:Step() + end + self.QuotaSupported = 0 + Cycles = Cycles - (self.VM.TMR - previousTMR) + end + + -- Reset INTR register for async thread + self.VM.INTR = 0 end -------------------------------------------------------------------------------- -- Think function function ENT:Think() - -- Calculate time-related variables - self.CurrentTime = CurTime() - self.DeltaTime = math.min(1/30,self.CurrentTime - (self.PreviousTime or 0)) - self.PreviousTime = self.CurrentTime - - -- Dont run until all sound sources are init - if #self.SoundSources == 0 then return end - - -- Run asynchronous thread - if self.VM.Memory[65535] == 1 then - self:Run() - -- Calculate ADSR - self.VM:CalculateADSR(self.DeltaTime) - end + -- Calculate time-related variables + self.CurrentTime = CurTime() + self.DeltaTime = math.min(1/30,self.CurrentTime - (self.PreviousTime or 0)) + self.PreviousTime = self.CurrentTime + + -- Dont run until all sound sources are init + if #self.SoundSources == 0 then return end + + -- Run asynchronous thread + if self.VM.Memory[65535] == 1 then + self:Run() + -- Calculate ADSR + self.VM:CalculateADSR(self.DeltaTime) + end end diff --git a/lua/entities/gmod_wire_spu/cl_spuvm.lua b/lua/entities/gmod_wire_spu/cl_spuvm.lua index 01994a9aa3..5eb8ebb8e8 100644 --- a/lua/entities/gmod_wire_spu/cl_spuvm.lua +++ b/lua/entities/gmod_wire_spu/cl_spuvm.lua @@ -4,189 +4,189 @@ local VM = {} function ENT:OverrideVM() - -- Store VM calls that will be overriden - self.VM.BaseReset = self.VM.Reset - - -- Add additional VM functionality - for k,v in pairs(VM) do - if k == "OpcodeTable" then - for k2,v2 in pairs(v) do - self.VM.OpcodeTable[k2] = v2 - end - else - self.VM[k] = v - end - end - - self.VM.Env["WireSPU_GetSound"] = WireSPU_GetSound - self.VM.Env["WireSPU_SoundCache"] = WireSPU_SoundCache - self.VM.Env["WireSPU_MaxChannels"] = WireSPU_MaxChannels - self.VM.Env["CreateSound"] = CreateSound - - self.VM.Entity = self - - self.VM.Interrupt = function(self,interruptNo,interruptParameter,isExternal,cascadeInterrupt) - self.IP = self.EntryPoint1 - self.LADD = interruptParameter - self.LINT = interruptNo - end - - -- Override ports - self.VM.WritePort = function(VM,Port,Value) - VM:WriteCell(63488+Port,Value) - end - self.VM.ReadPort = function(VM,Port) - return VM:ReadCell(63488+Port) - end - - -- Override writecell - self.VM.BaseWriteCell = self.VM.WriteCell - self.VM.WriteCell = function(VM,Address,Value) - VM:BaseWriteCell(Address,Value) - if Address == 65534 then - VM:Reset() - elseif Address == 65530 then - VM.ROM = {} - end - end - - -- Add internal registers - self.VM.InternalRegister[128] = "EntryPoint0" - self.VM.InternalRegister[129] = "EntryPoint1" - - -- Remove internal registers - self.VM.InternalRegister[24] = nil --IDTR - self.VM.InternalRegister[32] = nil --IF - self.VM.InternalRegister[33] = nil --PF - self.VM.InternalRegister[34] = nil --EF - self.VM.InternalRegister[45] = nil --BusLock - self.VM.InternalRegister[46] = nil --IDLE - self.VM.InternalRegister[47] = nil --INTR - self.VM.InternalRegister[52] = nil --NIDT - - -- Remove some instructions - self.VM.OperandCount[16] = nil --RD - self.VM.OperandCount[17] = nil --WD - self.VM.OperandCount[28] = nil --SPG - self.VM.OperandCount[29] = nil --CPG - self.VM.OperandCount[37] = nil --HALT - self.VM.OperandCount[41] = nil --IRET - self.VM.OperandCount[42] = nil --STI - self.VM.OperandCount[43] = nil --CLI - self.VM.OperandCount[44] = nil --STP - self.VM.OperandCount[45] = nil --CLP - self.VM.OperandCount[46] = nil --STD - self.VM.OperandCount[48] = nil --STEF - self.VM.OperandCount[49] = nil --CLEF - self.VM.OperandCount[70] = nil --EXTINT - self.VM.OperandCount[95] = nil --ERPG - self.VM.OperandCount[96] = nil --WRPG - self.VM.OperandCount[97] = nil --RDPG - self.VM.OperandCount[99] = nil --LIDTR - self.VM.OperandCount[100] = nil --STATESTORE - self.VM.OperandCount[109] = nil --STATERESTORE - self.VM.OperandCount[110] = nil --EXTRET - self.VM.OperandCount[113] = nil --RLADD - self.VM.OperandCount[116] = nil --STD2 - self.VM.OperandCount[118] = nil --STM - self.VM.OperandCount[119] = nil --CLM - self.VM.OperandCount[122] = nil --SPP - self.VM.OperandCount[123] = nil --CPP - self.VM.OperandCount[124] = nil --SRL - self.VM.OperandCount[125] = nil --GRL - self.VM.OperandCount[131] = nil --SMAP - self.VM.OperandCount[132] = nil --GMAP + -- Store VM calls that will be overriden + self.VM.BaseReset = self.VM.Reset + + -- Add additional VM functionality + for k,v in pairs(VM) do + if k == "OpcodeTable" then + for k2,v2 in pairs(v) do + self.VM.OpcodeTable[k2] = v2 + end + else + self.VM[k] = v + end + end + + self.VM.Env["WireSPU_GetSound"] = WireSPU_GetSound + self.VM.Env["WireSPU_SoundCache"] = WireSPU_SoundCache + self.VM.Env["WireSPU_MaxChannels"] = WireSPU_MaxChannels + self.VM.Env["CreateSound"] = CreateSound + + self.VM.Entity = self + + self.VM.Interrupt = function(self,interruptNo,interruptParameter,isExternal,cascadeInterrupt) + self.IP = self.EntryPoint1 + self.LADD = interruptParameter + self.LINT = interruptNo + end + + -- Override ports + self.VM.WritePort = function(VM,Port,Value) + VM:WriteCell(63488+Port,Value) + end + self.VM.ReadPort = function(VM,Port) + return VM:ReadCell(63488+Port) + end + + -- Override writecell + self.VM.BaseWriteCell = self.VM.WriteCell + self.VM.WriteCell = function(VM,Address,Value) + VM:BaseWriteCell(Address,Value) + if Address == 65534 then + VM:Reset() + elseif Address == 65530 then + VM.ROM = {} + end + end + + -- Add internal registers + self.VM.InternalRegister[128] = "EntryPoint0" + self.VM.InternalRegister[129] = "EntryPoint1" + + -- Remove internal registers + self.VM.InternalRegister[24] = nil --IDTR + self.VM.InternalRegister[32] = nil --IF + self.VM.InternalRegister[33] = nil --PF + self.VM.InternalRegister[34] = nil --EF + self.VM.InternalRegister[45] = nil --BusLock + self.VM.InternalRegister[46] = nil --IDLE + self.VM.InternalRegister[47] = nil --INTR + self.VM.InternalRegister[52] = nil --NIDT + + -- Remove some instructions + self.VM.OperandCount[16] = nil --RD + self.VM.OperandCount[17] = nil --WD + self.VM.OperandCount[28] = nil --SPG + self.VM.OperandCount[29] = nil --CPG + self.VM.OperandCount[37] = nil --HALT + self.VM.OperandCount[41] = nil --IRET + self.VM.OperandCount[42] = nil --STI + self.VM.OperandCount[43] = nil --CLI + self.VM.OperandCount[44] = nil --STP + self.VM.OperandCount[45] = nil --CLP + self.VM.OperandCount[46] = nil --STD + self.VM.OperandCount[48] = nil --STEF + self.VM.OperandCount[49] = nil --CLEF + self.VM.OperandCount[70] = nil --EXTINT + self.VM.OperandCount[95] = nil --ERPG + self.VM.OperandCount[96] = nil --WRPG + self.VM.OperandCount[97] = nil --RDPG + self.VM.OperandCount[99] = nil --LIDTR + self.VM.OperandCount[100] = nil --STATESTORE + self.VM.OperandCount[109] = nil --STATERESTORE + self.VM.OperandCount[110] = nil --EXTRET + self.VM.OperandCount[113] = nil --RLADD + self.VM.OperandCount[116] = nil --STD2 + self.VM.OperandCount[118] = nil --STM + self.VM.OperandCount[119] = nil --CLM + self.VM.OperandCount[122] = nil --SPP + self.VM.OperandCount[123] = nil --CPP + self.VM.OperandCount[124] = nil --SRL + self.VM.OperandCount[125] = nil --GRL + self.VM.OperandCount[131] = nil --SMAP + self.VM.OperandCount[132] = nil --GMAP end -------------------------------------------------------------------------------- -- Reset state each GPU frame -------------------------------------------------------------------------------- function VM:Reset() - -- Reset VM - 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 = 32767 - 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 - - self.ESZ = 32768 -- Stack size register - 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.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.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 - - -- Reset internal SPU registers - -- Hardware control registers: - -- [65535] - CLK - -- [65534] - RESET - -- [65527] - Async thread frequency - - if self.Channel then - for k,v in pairs(self.Channel) do - v.Sound:Stop() - end - end - - self.Waveform = {} - self.Channel = {} - - self.Waveform[0] = WireSPU_GetSound("synth/square.wav") - self.Waveform[1] = WireSPU_GetSound("synth/saw.wav") - self.Waveform[2] = WireSPU_GetSound("synth/tri.wav") - self.Waveform[3] = WireSPU_GetSound("synth/sine.wav") - - for chan=0,3 do - self.Channel[chan] = { - Sound = CreateSound(self.Entity.SoundSources[chan],self.Waveform[chan]), - Volume = 1.0, - Pitch = 100, - ADSR = {x=0, y=0, z=1, w=0}, - ADSRStage = 0, - ADSRTime = 0, - ADSRMode = 1, - ADSRVolume = 0, - } - end + -- Reset VM + 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 = 32767 + 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 + + self.ESZ = 32768 -- Stack size register + 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.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.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 + + -- Reset internal SPU registers + -- Hardware control registers: + -- [65535] - CLK + -- [65534] - RESET + -- [65527] - Async thread frequency + + if self.Channel then + for k,v in pairs(self.Channel) do + v.Sound:Stop() + end + end + + self.Waveform = {} + self.Channel = {} + + self.Waveform[0] = WireSPU_GetSound("synth/square.wav") + self.Waveform[1] = WireSPU_GetSound("synth/saw.wav") + self.Waveform[2] = WireSPU_GetSound("synth/tri.wav") + self.Waveform[3] = WireSPU_GetSound("synth/sine.wav") + + for chan=0,3 do + self.Channel[chan] = { + Sound = CreateSound(self.Entity.SoundSources[chan],self.Waveform[chan]), + Volume = 1.0, + Pitch = 100, + ADSR = {x=0, y=0, z=1, w=0}, + ADSRStage = 0, + ADSRTime = 0, + ADSRMode = 1, + ADSRVolume = 0, + } + end end WireSPU_SoundCache = {} function WireSPU_GetSound(name) - if not WireSPU_SoundCache[name] then - WireSPU_SoundCache[name] = Sound(name) - end - return WireSPU_SoundCache[name] + if not WireSPU_SoundCache[name] then + WireSPU_SoundCache[name] = Sound(name) + end + return WireSPU_SoundCache[name] end @@ -195,29 +195,29 @@ end -- Read a string by offset -------------------------------------------------------------------------------- function VM:ReadString(address) - local charString = "" - local charCount = 0 - local currentChar = 255 - - while currentChar ~= 0 do - currentChar = self:ReadCell(address + charCount) - - if (currentChar > 0) and (currentChar < 255) then - charString = charString .. string.char(currentChar) - else - if currentChar ~= 0 then - self:Interrupt(23,currentChar) - return "" - end - end - - charCount = charCount + 1 - if charCount > 8192 then - self:Interrupt(23,0) - return "" - end - end - return charString + local charString = "" + local charCount = 0 + local currentChar = 255 + + while currentChar ~= 0 do + currentChar = self:ReadCell(address + charCount) + + if (currentChar > 0) and (currentChar < 255) then + charString = charString .. string.char(currentChar) + else + if currentChar ~= 0 then + self:Interrupt(23,currentChar) + return "" + end + end + + charCount = charCount + 1 + if charCount > 8192 then + self:Interrupt(23,0) + return "" + end + end + return charString end -------------------------------------------------------------------------------- @@ -225,77 +225,77 @@ end -------------------------------------------------------------------------------- function VM:CalculateADSR(deltaTime) - for _, chan in pairs(self.Channel) do - if chan.ADSRStage ~= 0 then - -- break up the ADSR envelope for easier reading - local curTime = deltaTime + chan.ADSRTime - chan.ADSRTime = curTime - local attackTime = chan.ADSR.x / 1000 - local decayTime = chan.ADSR.y / 1000 - local sustainVol = chan.ADSR.z - local releaseTime = chan.ADSR.w / 1000 - local relVolume = chan.ADSRVolume - local maxVolume = chan.Volume - local curVolume = chan.Sound:GetVolume() - - -- ADSR Stages: - -- 0: Idle - -- 1: Attack - -- 2: Decay - -- 3: Sustain - -- 4: Release - - -- Attack - if chan.ADSRStage == 1 then - if curTime >= attackTime then -- Move to Decay - chan.ADSRStage = 2 - curTime = curTime - attackTime - chan.ADSRTime = curTime - else - local mag = curTime/attackTime - local vol = maxVolume * mag - chan.Sound:ChangeVolume(vol) - chan.ADSRVolume = vol - end - end - -- Decay - if chan.ADSRStage == 2 then - if curTime >= decayTime then -- Move to Sustain - if chan.ADSRMode == 0 then - chan.ADSRStage = 4 --Mode 0, no Sustain - else - chan.ADSRStage = 3 --Mode 1, Sustain - end - curTime = curTime - decayTime - chan.ADSRTime = curTime - else - local mag = curTime / decayTime - local vol = (maxVolume - (maxVolume * mag)) + ((maxVolume * sustainVol) * mag) - chan.Sound:ChangeVolume(vol) - chan.ADSRVolume = vol - end - end - -- Sustain - if chan.ADSRStage == 3 then - if curVolume ~= (maxVolume * sustainVol) then - chan.Sound:ChangeVolume(maxVolume * sustainVol) - chan.ADSRVolume = maxVolume * sustainVol - end - end - -- Release - if chan.ADSRStage == 4 then - if (releaseTime ~= 0) and (curTime < releaseTime) then -- The only place we COULD get a divide by zero error! - local mag = curTime / releaseTime - local vol = (maxVolume * relVolume) - ((maxVolume * relVolume) * mag) - chan.Sound:ChangeVolume(vol) - --We don't set ADSRVolume here as ADSRVolume is used to calculate the release curve - elseif curVolume ~=0 then - chan.ADSRStage = 0 - chan.Sound:ChangeVolume(0) - end - end - end - end + for _, chan in pairs(self.Channel) do + if chan.ADSRStage ~= 0 then + -- break up the ADSR envelope for easier reading + local curTime = deltaTime + chan.ADSRTime + chan.ADSRTime = curTime + local attackTime = chan.ADSR.x / 1000 + local decayTime = chan.ADSR.y / 1000 + local sustainVol = chan.ADSR.z + local releaseTime = chan.ADSR.w / 1000 + local relVolume = chan.ADSRVolume + local maxVolume = chan.Volume + local curVolume = chan.Sound:GetVolume() + + -- ADSR Stages: + -- 0: Idle + -- 1: Attack + -- 2: Decay + -- 3: Sustain + -- 4: Release + + -- Attack + if chan.ADSRStage == 1 then + if curTime >= attackTime then -- Move to Decay + chan.ADSRStage = 2 + curTime = curTime - attackTime + chan.ADSRTime = curTime + else + local mag = curTime/attackTime + local vol = maxVolume * mag + chan.Sound:ChangeVolume(vol) + chan.ADSRVolume = vol + end + end + -- Decay + if chan.ADSRStage == 2 then + if curTime >= decayTime then -- Move to Sustain + if chan.ADSRMode == 0 then + chan.ADSRStage = 4 --Mode 0, no Sustain + else + chan.ADSRStage = 3 --Mode 1, Sustain + end + curTime = curTime - decayTime + chan.ADSRTime = curTime + else + local mag = curTime / decayTime + local vol = (maxVolume - (maxVolume * mag)) + ((maxVolume * sustainVol) * mag) + chan.Sound:ChangeVolume(vol) + chan.ADSRVolume = vol + end + end + -- Sustain + if chan.ADSRStage == 3 then + if curVolume ~= (maxVolume * sustainVol) then + chan.Sound:ChangeVolume(maxVolume * sustainVol) + chan.ADSRVolume = maxVolume * sustainVol + end + end + -- Release + if chan.ADSRStage == 4 then + if (releaseTime ~= 0) and (curTime < releaseTime) then -- The only place we COULD get a divide by zero error! + local mag = curTime / releaseTime + local vol = (maxVolume * relVolume) - ((maxVolume * relVolume) * mag) + chan.Sound:ChangeVolume(vol) + --We don't set ADSRVolume here as ADSRVolume is used to calculate the release curve + elseif curVolume ~=0 then + chan.ADSRStage = 0 + chan.Sound:ChangeVolume(0) + end + end + end + end end -------------------------------------------------------------------------------- @@ -304,146 +304,146 @@ end VM.OpcodeTable = {} VM.OpcodeTable[111] = function(self) --IDLE -- self:Dyn_Emit("VM.INTR = 1") - self:Dyn_EmitBreak() - self.PrecompileBreak = true + self:Dyn_EmitBreak() + self.PrecompileBreak = true end -------------------------------------------------------------------------------- VM.OpcodeTable[320] = function(self) --CHRESET - self:Dyn_Emit("$L CHAN = math.floor($1)") - - self:Dyn_Emit("if CHAN == -1 then") - self:Dyn_Emit("for channel=0,WireSPU_MaxChannels-1 do") - self:Dyn_Emit("if VM.Channel[channel] then") - self:Dyn_Emit("VM.Channel[channel].Sound:Stop()") - self:Dyn_Emit("VM.Channel[channel].Pitch = 100") - self:Dyn_Emit("VM.Channel[channel].Volume = 1.0") - self:Dyn_Emit("VM.Channel[channel].ADSR = {x=0, y=0, z=1, w=0}") - self:Dyn_Emit("VM.Channel[channel].ADSRStage = 0") - self:Dyn_Emit("VM.Channel[channel].ADSRTime = 0") - self:Dyn_Emit("VM.Channel[channel].ADSRMode = 1") - self:Dyn_Emit("VM.Channel[channel].ADSRVolume = 0") - self:Dyn_Emit("end") - self:Dyn_Emit("end") - self:Dyn_Emit("else") - self:Dyn_Emit("if VM.Channel[CHAN] then") - self:Dyn_Emit("VM.Channel[CHAN].Sound:Stop()") - self:Dyn_Emit("VM.Channel[CHAN].Pitch = 100") - self:Dyn_Emit("VM.Channel[CHAN].Volume = 1.0") - self:Dyn_Emit("VM.Channel[CHAN].ADSR = {x=0, y=0, z=1, w=0}") - self:Dyn_Emit("VM.Channel[CHAN].ADSRStage = 0") - self:Dyn_Emit("VM.Channel[CHAN].ADSRTime = 0") - self:Dyn_Emit("VM.Channel[CHAN].ADSRMode = 1") - self:Dyn_Emit("VM.Channel[CHAN].ADSRVolume = 0") - self:Dyn_Emit("end") - self:Dyn_Emit("end") + self:Dyn_Emit("$L CHAN = math.floor($1)") + + self:Dyn_Emit("if CHAN == -1 then") + self:Dyn_Emit("for channel=0,WireSPU_MaxChannels-1 do") + self:Dyn_Emit("if VM.Channel[channel] then") + self:Dyn_Emit("VM.Channel[channel].Sound:Stop()") + self:Dyn_Emit("VM.Channel[channel].Pitch = 100") + self:Dyn_Emit("VM.Channel[channel].Volume = 1.0") + self:Dyn_Emit("VM.Channel[channel].ADSR = {x=0, y=0, z=1, w=0}") + self:Dyn_Emit("VM.Channel[channel].ADSRStage = 0") + self:Dyn_Emit("VM.Channel[channel].ADSRTime = 0") + self:Dyn_Emit("VM.Channel[channel].ADSRMode = 1") + self:Dyn_Emit("VM.Channel[channel].ADSRVolume = 0") + self:Dyn_Emit("end") + self:Dyn_Emit("end") + self:Dyn_Emit("else") + self:Dyn_Emit("if VM.Channel[CHAN] then") + self:Dyn_Emit("VM.Channel[CHAN].Sound:Stop()") + self:Dyn_Emit("VM.Channel[CHAN].Pitch = 100") + self:Dyn_Emit("VM.Channel[CHAN].Volume = 1.0") + self:Dyn_Emit("VM.Channel[CHAN].ADSR = {x=0, y=0, z=1, w=0}") + self:Dyn_Emit("VM.Channel[CHAN].ADSRStage = 0") + self:Dyn_Emit("VM.Channel[CHAN].ADSRTime = 0") + self:Dyn_Emit("VM.Channel[CHAN].ADSRMode = 1") + self:Dyn_Emit("VM.Channel[CHAN].ADSRVolume = 0") + self:Dyn_Emit("end") + self:Dyn_Emit("end") end VM.OpcodeTable[321] = function(self) --CHSTART - self:Dyn_Emit("$L CHAN = math.floor($1)") - - self:Dyn_Emit("if (CHAN >= 0) and (CHAN < WireSPU_MaxChannels) then") - self:Dyn_Emit("if VM.Channel[CHAN] then") - self:Dyn_Emit("VM.Channel[CHAN].Sound:PlayEx(VM.Channel[CHAN].Volume,VM.Channel[CHAN].Pitch)") - self:Dyn_Emit("VM.Channel[CHAN].ADSRStage = 1") - self:Dyn_Emit("VM.Channel[CHAN].ADSRTime = 0") - self:Dyn_Emit("VM.Channel[CHAN].ADSRVolume = 0") - self:Dyn_Emit("end") - self:Dyn_Emit("end") + self:Dyn_Emit("$L CHAN = math.floor($1)") + + self:Dyn_Emit("if (CHAN >= 0) and (CHAN < WireSPU_MaxChannels) then") + self:Dyn_Emit("if VM.Channel[CHAN] then") + self:Dyn_Emit("VM.Channel[CHAN].Sound:PlayEx(VM.Channel[CHAN].Volume,VM.Channel[CHAN].Pitch)") + self:Dyn_Emit("VM.Channel[CHAN].ADSRStage = 1") + self:Dyn_Emit("VM.Channel[CHAN].ADSRTime = 0") + self:Dyn_Emit("VM.Channel[CHAN].ADSRVolume = 0") + self:Dyn_Emit("end") + self:Dyn_Emit("end") end VM.OpcodeTable[322] = function(self) --CHSTOP - self:Dyn_Emit("$L CHAN = math.floor($1)") - - self:Dyn_Emit("if (CHAN >= 0) and (CHAN < WireSPU_MaxChannels) then") - self:Dyn_Emit("if VM.Channel[CHAN] then") - self:Dyn_Emit("VM.Channel[CHAN].Sound:Stop()") - self:Dyn_Emit("VM.Channel[CHAN].ADSRStage = 0") - self:Dyn_Emit("VM.Channel[CHAN].ADSRTime = 0") - self:Dyn_Emit("VM.Channel[CHAN].ADSRVolume = 0") - self:Dyn_Emit("end") - self:Dyn_Emit("end") + self:Dyn_Emit("$L CHAN = math.floor($1)") + + self:Dyn_Emit("if (CHAN >= 0) and (CHAN < WireSPU_MaxChannels) then") + self:Dyn_Emit("if VM.Channel[CHAN] then") + self:Dyn_Emit("VM.Channel[CHAN].Sound:Stop()") + self:Dyn_Emit("VM.Channel[CHAN].ADSRStage = 0") + self:Dyn_Emit("VM.Channel[CHAN].ADSRTime = 0") + self:Dyn_Emit("VM.Channel[CHAN].ADSRVolume = 0") + self:Dyn_Emit("end") + self:Dyn_Emit("end") end VM.OpcodeTable[323] = function(self) --CHTRIGGER - self:Dyn_Emit("$L CHAN = math.floor($1)") - - self:Dyn_Emit("if (CHAN >= 0) and (CHAN < WireSPU_MaxChannels) then") - self:Dyn_Emit("if VM.Channel[CHAN] then") - self:Dyn_Emit("VM.Channel[CHAN].ADSRStage = 1") - self:Dyn_Emit("VM.Channel[CHAN].ADSRTime = 0") - self:Dyn_Emit("VM.Channel[CHAN].ADSRVolume = 0") - self:Dyn_Emit("end") - self:Dyn_Emit("end") + self:Dyn_Emit("$L CHAN = math.floor($1)") + + self:Dyn_Emit("if (CHAN >= 0) and (CHAN < WireSPU_MaxChannels) then") + self:Dyn_Emit("if VM.Channel[CHAN] then") + self:Dyn_Emit("VM.Channel[CHAN].ADSRStage = 1") + self:Dyn_Emit("VM.Channel[CHAN].ADSRTime = 0") + self:Dyn_Emit("VM.Channel[CHAN].ADSRVolume = 0") + self:Dyn_Emit("end") + self:Dyn_Emit("end") end VM.OpcodeTable[324] = function(self) --CHRELEASE - self:Dyn_Emit("$L CHAN = math.floor($1)") - - self:Dyn_Emit("if (CHAN >= 0) and (CHAN < WireSPU_MaxChannels) then") - self:Dyn_Emit("if VM.Channel[CHAN] then") - self:Dyn_Emit("VM.Channel[CHAN].ADSRStage = 4") - self:Dyn_Emit("VM.Channel[CHAN].ADSRTime = 0") - self:Dyn_Emit("end") - self:Dyn_Emit("end") + self:Dyn_Emit("$L CHAN = math.floor($1)") + + self:Dyn_Emit("if (CHAN >= 0) and (CHAN < WireSPU_MaxChannels) then") + self:Dyn_Emit("if VM.Channel[CHAN] then") + self:Dyn_Emit("VM.Channel[CHAN].ADSRStage = 4") + self:Dyn_Emit("VM.Channel[CHAN].ADSRTime = 0") + self:Dyn_Emit("end") + self:Dyn_Emit("end") end -------------------------------------------------------------------------------- VM.OpcodeTable[330] = function(self) --WSET - self:Dyn_Emit("$L WAVE = math.floor($1)") - self:Dyn_Emit("$L NAME = VM:ReadString($2)") - self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("$L WAVE = math.floor($1)") + self:Dyn_Emit("$L NAME = VM:ReadString($2)") + self:Dyn_EmitInterruptCheck() - self:Dyn_Emit("if (WAVE >= 0) and (WAVE < 8192) then") - self:Dyn_Emit("VM.Waveform[WAVE] = WireSPU_GetSound(NAME)") - self:Dyn_Emit("end") + self:Dyn_Emit("if (WAVE >= 0) and (WAVE < 8192) then") + self:Dyn_Emit("VM.Waveform[WAVE] = WireSPU_GetSound(NAME)") + self:Dyn_Emit("end") end VM.OpcodeTable[331] = function(self) --CHWAVE - self:Dyn_Emit("$L CHAN = math.floor($1)") - self:Dyn_Emit("$L WAVE = math.floor($2)") - - self:Dyn_Emit("if (WAVE >= 0) and (WAVE < 8192) and (CHAN >= 0) and (CHAN < WireSPU_MaxChannels) then") - self:Dyn_Emit("if VM.Waveform[WAVE] then") - self:Dyn_Emit("if VM.Channel[CHAN] then") - self:Dyn_Emit("if VM.Channel[CHAN].Sound:IsPlaying() then") - self:Dyn_Emit("VM.Channel[CHAN].Sound:Stop()") - self:Dyn_Emit("end") - self:Dyn_Emit("end") - self:Dyn_Emit("VM.Channel[CHAN] = { Sound = CreateSound(VM.Entity.SoundSources[CHAN],VM.Waveform[WAVE]), Pitch = 100, Volume = 1.0 }") - self:Dyn_Emit("VM.Channel[CHAN].ADSR = {x=0, y=0, z=1, w=0}") - self:Dyn_Emit("VM.Channel[CHAN].ADSRStage = 0") - self:Dyn_Emit("VM.Channel[CHAN].ADSRTime = 0") - self:Dyn_Emit("VM.Channel[CHAN].ADSRMode = 1") - self:Dyn_Emit("VM.Channel[CHAN].ADSRVolume = 0") - self:Dyn_Emit("end") - self:Dyn_Emit("end") + self:Dyn_Emit("$L CHAN = math.floor($1)") + self:Dyn_Emit("$L WAVE = math.floor($2)") + + self:Dyn_Emit("if (WAVE >= 0) and (WAVE < 8192) and (CHAN >= 0) and (CHAN < WireSPU_MaxChannels) then") + self:Dyn_Emit("if VM.Waveform[WAVE] then") + self:Dyn_Emit("if VM.Channel[CHAN] then") + self:Dyn_Emit("if VM.Channel[CHAN].Sound:IsPlaying() then") + self:Dyn_Emit("VM.Channel[CHAN].Sound:Stop()") + self:Dyn_Emit("end") + self:Dyn_Emit("end") + self:Dyn_Emit("VM.Channel[CHAN] = { Sound = CreateSound(VM.Entity.SoundSources[CHAN],VM.Waveform[WAVE]), Pitch = 100, Volume = 1.0 }") + self:Dyn_Emit("VM.Channel[CHAN].ADSR = {x=0, y=0, z=1, w=0}") + self:Dyn_Emit("VM.Channel[CHAN].ADSRStage = 0") + self:Dyn_Emit("VM.Channel[CHAN].ADSRTime = 0") + self:Dyn_Emit("VM.Channel[CHAN].ADSRMode = 1") + self:Dyn_Emit("VM.Channel[CHAN].ADSRVolume = 0") + self:Dyn_Emit("end") + self:Dyn_Emit("end") end VM.OpcodeTable[332] = function(self) --CHLOOP - self:Dyn_Emit("$L CHAN = math.floor($1)") - self:Dyn_Emit("$L X = $2") - - self:Dyn_Emit("if (CHAN >= 0) and (CHAN < WireSPU_MaxChannels) then") - self:Dyn_Emit("if VM.Channel[CHAN] then") - self:Dyn_Emit("VM.Channel[CHAN].ADSRMode = X") - self:Dyn_Emit("end") - self:Dyn_Emit("end") + self:Dyn_Emit("$L CHAN = math.floor($1)") + self:Dyn_Emit("$L X = $2") + + self:Dyn_Emit("if (CHAN >= 0) and (CHAN < WireSPU_MaxChannels) then") + self:Dyn_Emit("if VM.Channel[CHAN] then") + self:Dyn_Emit("VM.Channel[CHAN].ADSRMode = X") + self:Dyn_Emit("end") + self:Dyn_Emit("end") end VM.OpcodeTable[333] = function(self) --CHVOLUME - self:Dyn_Emit("$L CHAN = math.floor($1)") - self:Dyn_Emit("$L X = $2") - - self:Dyn_Emit("if (CHAN >= 0) and (CHAN < WireSPU_MaxChannels) then") - self:Dyn_Emit("if VM.Channel[CHAN] then") - self:Dyn_Emit("VM.Channel[CHAN].Sound:ChangeVolume(math.Clamp(X,0,1),0)") - self:Dyn_Emit("VM.Channel[CHAN].Volume = math.Clamp(X,0,1)") - self:Dyn_Emit("end") - self:Dyn_Emit("end") + self:Dyn_Emit("$L CHAN = math.floor($1)") + self:Dyn_Emit("$L X = $2") + + self:Dyn_Emit("if (CHAN >= 0) and (CHAN < WireSPU_MaxChannels) then") + self:Dyn_Emit("if VM.Channel[CHAN] then") + self:Dyn_Emit("VM.Channel[CHAN].Sound:ChangeVolume(math.Clamp(X,0,1),0)") + self:Dyn_Emit("VM.Channel[CHAN].Volume = math.Clamp(X,0,1)") + self:Dyn_Emit("end") + self:Dyn_Emit("end") end VM.OpcodeTable[334] = function(self) --CHPITCH - self:Dyn_Emit("$L CHAN = math.floor($1)") - self:Dyn_Emit("$L X = $2") - - self:Dyn_Emit("if (CHAN >= 0) and (CHAN < WireSPU_MaxChannels) then") - self:Dyn_Emit("if VM.Channel[CHAN] then") - self:Dyn_Emit("VM.Channel[CHAN].Sound:ChangePitch(math.Clamp(X*100,0,255),0)") - self:Dyn_Emit("VM.Channel[CHAN].Pitch = math.Clamp(X*100,0,255)") - self:Dyn_Emit("end") - self:Dyn_Emit("end") + self:Dyn_Emit("$L CHAN = math.floor($1)") + self:Dyn_Emit("$L X = $2") + + self:Dyn_Emit("if (CHAN >= 0) and (CHAN < WireSPU_MaxChannels) then") + self:Dyn_Emit("if VM.Channel[CHAN] then") + self:Dyn_Emit("VM.Channel[CHAN].Sound:ChangePitch(math.Clamp(X*100,0,255),0)") + self:Dyn_Emit("VM.Channel[CHAN].Pitch = math.Clamp(X*100,0,255)") + self:Dyn_Emit("end") + self:Dyn_Emit("end") end VM.OpcodeTable[335] = function(self) --CHMODT end @@ -452,15 +452,15 @@ end VM.OpcodeTable[337] = function(self) --CHMODF end VM.OpcodeTable[338] = function(self) --CHADSR - self:Dyn_Emit("$L CHAN = math.floor($1)") - self:Dyn_Emit("$L VEC = VM:ReadVector4f($2 + VM."..(self.EmitOperandSegment[2] or "DS")..")") - self:Dyn_EmitInterruptCheck() - - self:Dyn_Emit("if (CHAN >= 0) and (CHAN < WireSPU_MaxChannels) then") - self:Dyn_Emit("if VM.Channel[CHAN] then") - self:Dyn_Emit("VM.Channel[CHAN].ADSR = VEC") - self:Dyn_Emit("VM.Channel[CHAN].ADSRStage = 0") - self:Dyn_Emit("VM.Channel[CHAN].ADSRTime = 0") - self:Dyn_Emit("end") - self:Dyn_Emit("end") + self:Dyn_Emit("$L CHAN = math.floor($1)") + self:Dyn_Emit("$L VEC = VM:ReadVector4f($2 + VM."..(self.EmitOperandSegment[2] or "DS")..")") + self:Dyn_EmitInterruptCheck() + + self:Dyn_Emit("if (CHAN >= 0) and (CHAN < WireSPU_MaxChannels) then") + self:Dyn_Emit("if VM.Channel[CHAN] then") + self:Dyn_Emit("VM.Channel[CHAN].ADSR = VEC") + self:Dyn_Emit("VM.Channel[CHAN].ADSRStage = 0") + self:Dyn_Emit("VM.Channel[CHAN].ADSRTime = 0") + self:Dyn_Emit("end") + self:Dyn_Emit("end") end diff --git a/lua/entities/gmod_wire_spu/init.lua b/lua/entities/gmod_wire_spu/init.lua index 520728ea98..3b2f1cafb1 100644 --- a/lua/entities/gmod_wire_spu/init.lua +++ b/lua/entities/gmod_wire_spu/init.lua @@ -13,62 +13,62 @@ WireSPU_MaxChannels = 32 -------------------------------------------------------------------------------- function ENT:Initialize() - -- Physics properties - self:PhysicsInit(SOLID_VPHYSICS) - self:SetMoveType(MOVETYPE_VPHYSICS) - self:SetSolid(SOLID_VPHYSICS) - - -- Inputs/outputs - self.Inputs = Wire_CreateInputs(self, { "Clk", "Reset", "IOBus", "SoundOut" }) - self.Outputs = Wire_CreateOutputs(self, { "Memory" }) - - -- Setup platform settings - self.Clk = 1 - self.MemBusScanAddress = 65536 - self.SerialNo = CPULib.GenerateSN("SPU") - self:SetMemoryModel("128k",true) - - -- Create serverside memory and cache - self.Memory = {} - self.Cache = GPUCacheManager(self) - - -- Connected sound emitters - self.SoundEmitters = {} - - -- Sound sources - self.SoundSources = {} - for i=0,WireSPU_MaxChannels-1 do - self.SoundSources[i] = ents.Create("prop_physics") - self.SoundSources[i]:SetParent(self) - self.SoundSources[i]:SetModel("models/cheeze/wires/nano_math.mdl") - self.SoundSources[i]:SetPos(self:GetPos()) + -- Physics properties + self:PhysicsInit(SOLID_VPHYSICS) + self:SetMoveType(MOVETYPE_VPHYSICS) + self:SetSolid(SOLID_VPHYSICS) + + -- Inputs/outputs + self.Inputs = Wire_CreateInputs(self, { "Clk", "Reset", "IOBus", "SoundOut" }) + self.Outputs = Wire_CreateOutputs(self, { "Memory" }) + + -- Setup platform settings + self.Clk = 1 + self.MemBusScanAddress = 65536 + self.SerialNo = CPULib.GenerateSN("SPU") + self:SetMemoryModel("128k",true) + + -- Create serverside memory and cache + self.Memory = {} + self.Cache = GPUCacheManager(self) + + -- Connected sound emitters + self.SoundEmitters = {} + + -- Sound sources + self.SoundSources = {} + for i=0,WireSPU_MaxChannels-1 do + self.SoundSources[i] = ents.Create("prop_physics") + self.SoundSources[i]:SetParent(self) + self.SoundSources[i]:SetModel("models/cheeze/wires/nano_math.mdl") + self.SoundSources[i]:SetPos(self:GetPos()) self.SoundSources[i].DoNotDuplicate = true - self.SoundSources[i]:Spawn() - self.SoundSources[i]:PhysicsDestroy() - end + self.SoundSources[i]:Spawn() + self.SoundSources[i]:PhysicsDestroy() + end - timer.Simple(0.1+math.random()*0.3, - function() - if not self:IsValid() then return end + timer.Simple(0.1+math.random()*0.3, + function() + if not self:IsValid() then return end - umsg.Start("wire_spu_soundsources") - umsg.Long(self:EntIndex()) - for i=0,WireSPU_MaxChannels-1 do - umsg.Long(self.SoundSources[i]:EntIndex()) - end - umsg.End() + umsg.Start("wire_spu_soundsources") + umsg.Long(self:EntIndex()) + for i=0,WireSPU_MaxChannels-1 do + umsg.Long(self.SoundSources[i]:EntIndex()) + end + umsg.End() -- for i=0,WireSPU_MaxChannels-1 do -- self.SoundSources[i]:SetModelScale(Vector(0)) -- self.SoundSources[i]:SetNoDraw(true) -- end - end) + end) end function ENT:OnRemove() - for i=0,WireSPU_MaxChannels-1 do - self.SoundSources[i]:Remove() - end + for i=0,WireSPU_MaxChannels-1 do + self.SoundSources[i]:Remove() + end end @@ -76,70 +76,70 @@ end -- Set processor -------------------------------------------------------------------------------- function ENT:SetMemoryModel(model,initial) - if model then - for i=6,11 do - if model == (2^i).."k" then - self.RAMSize = (2^i)*1024 - self.ChipType = 0 - elseif model == (2^i).."kc" then - self.RAMSize = (2^i)*1024 - self.ChipType = 1 - end - end - end - - if not initial then - timer.Simple(0.1+math.random()*0.3, - function() - if not self:IsValid() then return end - - umsg.Start("wire_spu_memorymodel") - umsg.Long(self:EntIndex()) - umsg.Long (self.RAMSize) - umsg.Float(self.SerialNo) - umsg.Short(self.ChipType) - umsg.End() - end) - end + if model then + for i=6,11 do + if model == (2^i).."k" then + self.RAMSize = (2^i)*1024 + self.ChipType = 0 + elseif model == (2^i).."kc" then + self.RAMSize = (2^i)*1024 + self.ChipType = 1 + end + end + end + + if not initial then + timer.Simple(0.1+math.random()*0.3, + function() + if not self:IsValid() then return end + + umsg.Start("wire_spu_memorymodel") + umsg.Long(self:EntIndex()) + umsg.Long (self.RAMSize) + umsg.Float(self.SerialNo) + umsg.Short(self.ChipType) + umsg.End() + end) + 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) + 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 -------------------------------------------------------------------------------- function ENT:ResendCache(player) - timer.Simple(0.4+math.random()*1.2, - function() - if not self:IsValid() then return end - if not IsValid(player) then return end - - self.Cache:Flush() - for address,value in pairs(self.Memory) do - self:WriteCell(address,value,player) - end - self.Cache:Flush(player) - - self:WriteCell(65534,1,player) -- Reset SPU - self:WriteCell(65535,self.Clk,player) -- Update Clk - end) + timer.Simple(0.4+math.random()*1.2, + function() + if not self:IsValid() then return end + if not IsValid(player) then return end + + self.Cache:Flush() + for address,value in pairs(self.Memory) do + self:WriteCell(address,value,player) + end + self.Cache:Flush(player) + + self:WriteCell(65534,1,player) -- Reset SPU + self:WriteCell(65535,self.Clk,player) -- Update Clk + end) end local function SPU_PlayerRespawn(player) - for _,Entity in ipairs(ents.FindByClass("gmod_wire_spu")) do - Entity:ResendCache(player) - end + for _,Entity in ipairs(ents.FindByClass("gmod_wire_spu")) do + Entity:ResendCache(player) + end end hook.Add("PlayerInitialSpawn", "SPUPlayerRespawn", SPU_PlayerRespawn) concommand.Add("wire_spu_resendcache", SPU_PlayerRespawn) @@ -149,16 +149,16 @@ concommand.Add("wire_spu_resendcache", SPU_PlayerRespawn) -- Read cell from SPU memory -------------------------------------------------------------------------------- function ENT:ReadCell(Address) - Address = math.floor(Address) - if (Address < 0) or (Address >= self.RAMSize) then - return nil - else - if self.Memory[Address] then - return self.Memory[Address] - else - return 0 - end - end + Address = math.floor(Address) + if (Address < 0) or (Address >= self.RAMSize) then + return nil + else + if self.Memory[Address] then + return self.Memory[Address] + else + return 0 + end + end end @@ -166,23 +166,23 @@ end -- Write cell to SPU memory -------------------------------------------------------------------------------- function ENT:WriteCell(Address, Value, Player) - Address = math.floor(Address) - if (Address < 0) or (Address >= self.RAMSize) then - return false - else - if (Address ~= 65535) and (Address ~= 65534) then - -- Write to internal memory - self.Memory[Address] = Value - - -- Add address to cache if cache is not big enough yet - self.Cache:Write(Address,Value,Player) - return true - else - self.Cache:Flush(Player) - self.Cache:WriteNow(Address,Value,Player) - end - return true - end + Address = math.floor(Address) + if (Address < 0) or (Address >= self.RAMSize) then + return false + else + if (Address ~= 65535) and (Address ~= 65534) then + -- Write to internal memory + self.Memory[Address] = Value + + -- Add address to cache if cache is not big enough yet + self.Cache:Write(Address,Value,Player) + return true + else + self.Cache:Flush(Player) + self.Cache:WriteNow(Address,Value,Player) + end + return true + end end @@ -190,20 +190,20 @@ end -- Write advanced dupe -------------------------------------------------------------------------------- function ENT:BuildDupeInfo() - local info = BaseClass.BuildDupeInfo(self) or {} + local info = BaseClass.BuildDupeInfo(self) or {} - info.SerialNo = self.SerialNo - info.RAMSize = self.RAMSize - info.ChipType = self.ChipType - info.Memory = {} - info.ZVMExtensions = self.ZVMExtensions - self:SetExtensionLoadOrder(self.ZVMExtensions) + info.SerialNo = self.SerialNo + 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 - end + for address = 0,self.RAMSize-1 do + if self.Memory[address] and (self.Memory[address] ~= 0) then info.Memory[address] = self.Memory[address] end + end - return info + return info end @@ -211,20 +211,20 @@ end -- Read from advanced dupe -------------------------------------------------------------------------------- function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) - BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) - self.SerialNo = info.SerialNo or 999999 - self.RAMSize = math.Clamp(info.RAMSize or 65536, 0, 2097152) - self.ChipType = info.ChipType or 0 - self.Memory = {} - self.ZVMExtensions = info.ZVMExtensions + self.SerialNo = info.SerialNo or 999999 + 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 - end + for address = 0,self.RAMSize-1 do + if info.Memory[address] then self.Memory[address] = tonumber(info.Memory[address]) or 0 end + end - self:SetMemoryModel() - self:ResendCache(nil) + self:SetMemoryModel() + self:ResendCache(nil) end @@ -232,12 +232,12 @@ end -- Handle external input -------------------------------------------------------------------------------- function ENT:TriggerInput(iname, value) - if iname == "Clk" then - self.Clk = (value >= 1 and 1 or 0) - self:WriteCell(65535,self.Clk) - elseif iname == "Reset" then - if value >= 1.0 then self:WriteCell(65534,1) end - end + if iname == "Clk" then + self.Clk = (value >= 1 and 1 or 0) + self:WriteCell(65535,self.Clk) + elseif iname == "Reset" then + if value >= 1.0 then self:WriteCell(65534,1) end + end end @@ -245,33 +245,33 @@ end -- Find out all sound emitters connected to the SPU -------------------------------------------------------------------------------- function ENT:QuerySoundEmitters(entity) - self.QueryRecurseCounter = self.QueryRecurseCounter + 1 - if self.QueryRecurseCounter > 128 then return end - if (not entity) or (not entity:IsValid()) then return end - - if entity:GetClass() == "gmod_wire_spu" then -- VideoOut connected to a GPU - table.insert(self.QueryResult,entity:EntIndex()) - elseif entity.Socket then -- VideoOut connected to a plug - self:QuerySoundEmitters(entity.Socket.Inputs.Memory.Src) - elseif entity.Plug then -- VideoOut connected to a socket - self:QuerySoundEmitters(entity.Plug.Inputs.Memory.Src) - elseif entity.Ply and entity.Ply:IsValid() then -- VideoOut connected to pod - table.insert(self.QueryResult,entity.Ply:EntIndex()) - elseif entity:GetClass() == "gmod_wire_addressbus" then -- VideoOut connected to address bus - self:QuerySoundEmitters(entity.Inputs.Memory1.Src) - self:QuerySoundEmitters(entity.Inputs.Memory2.Src) - self:QuerySoundEmitters(entity.Inputs.Memory3.Src) - self:QuerySoundEmitters(entity.Inputs.Memory4.Src) - elseif entity:GetClass() == "gmod_wire_extbus" then -- VideoOut connected to ext bus - self:QuerySoundEmitters(entity.Inputs.Memory1.Src) - self:QuerySoundEmitters(entity.Inputs.Memory2.Src) - self:QuerySoundEmitters(entity.Inputs.Memory3.Src) - self:QuerySoundEmitters(entity.Inputs.Memory4.Src) - self:QuerySoundEmitters(entity.Inputs.Memory5.Src) - self:QuerySoundEmitters(entity.Inputs.Memory6.Src) - self:QuerySoundEmitters(entity.Inputs.Memory7.Src) - self:QuerySoundEmitters(entity.Inputs.Memory8.Src) - end + self.QueryRecurseCounter = self.QueryRecurseCounter + 1 + if self.QueryRecurseCounter > 128 then return end + if (not entity) or (not entity:IsValid()) then return end + + if entity:GetClass() == "gmod_wire_spu" then -- VideoOut connected to a GPU + table.insert(self.QueryResult,entity:EntIndex()) + elseif entity.Socket then -- VideoOut connected to a plug + self:QuerySoundEmitters(entity.Socket.Inputs.Memory.Src) + elseif entity.Plug then -- VideoOut connected to a socket + self:QuerySoundEmitters(entity.Plug.Inputs.Memory.Src) + elseif entity.Ply and entity.Ply:IsValid() then -- VideoOut connected to pod + table.insert(self.QueryResult,entity.Ply:EntIndex()) + elseif entity:GetClass() == "gmod_wire_addressbus" then -- VideoOut connected to address bus + self:QuerySoundEmitters(entity.Inputs.Memory1.Src) + self:QuerySoundEmitters(entity.Inputs.Memory2.Src) + self:QuerySoundEmitters(entity.Inputs.Memory3.Src) + self:QuerySoundEmitters(entity.Inputs.Memory4.Src) + elseif entity:GetClass() == "gmod_wire_extbus" then -- VideoOut connected to ext bus + self:QuerySoundEmitters(entity.Inputs.Memory1.Src) + self:QuerySoundEmitters(entity.Inputs.Memory2.Src) + self:QuerySoundEmitters(entity.Inputs.Memory3.Src) + self:QuerySoundEmitters(entity.Inputs.Memory4.Src) + self:QuerySoundEmitters(entity.Inputs.Memory5.Src) + self:QuerySoundEmitters(entity.Inputs.Memory6.Src) + self:QuerySoundEmitters(entity.Inputs.Memory7.Src) + self:QuerySoundEmitters(entity.Inputs.Memory8.Src) + end end @@ -279,75 +279,75 @@ end -- Update cache and external connections -------------------------------------------------------------------------------- function ENT:Think() - -- Update IOBus - if self.Inputs.IOBus.Src then - -- Was there any update in that that would require flushing - local DataUpdated = false - - -- Update any cells that must be updated - for port = 0,1023 do - if self.Inputs.IOBus.Src.ReadCell then - local var = self.Inputs.IOBus.Src:ReadCell(port) - if var then - if self:ReadCell(port+63488) ~= var then - self:WriteCell(port+63488,var) - DataUpdated = true - end - end - end - end - - -- Flush updated data - if DataUpdated then self.Cache:Flush() end - end - - -- Flush any data in cache - self.Cache:Flush() - - -- Update video output, and send any changes to client - if self.Inputs.SoundOut.Src then - self.QueryRecurseCounter = 0 - self.QueryResult = { } - self:QuerySoundEmitters(self.Inputs.SoundOut.Src) - - -- Check if sound emitters setup has changed - local soundEmittersChanged = false - for k,v in pairs(self.QueryResult) do - if self.SoundEmitters[k] ~= v then - soundEmittersChanged = true - break - end - end - - if not soundEmittersChanged then - for k,v in pairs(self.SoundEmitters) do - if self.QueryResult[k] ~= v then - soundEmittersChanged = true - break - end - end - end - - if #self.QueryResult ~= #self.SoundEmitters then soundEmittersChanged = true end - - if soundEmittersChanged then - self.SoundEmitters = self.QueryResult - end - - -- Send update to all clients - if soundEmittersChanged then - umsg.Start("wire_spu_soundstate") - umsg.Long(self:EntIndex()) - umsg.Short(#self.SoundEmitters) - for idx=1,#self.SoundEmitters do - umsg.Long(self.SoundEmitters[idx]) - end - umsg.End() - end - end - - self:NextThink(CurTime()+0.05) - return true + -- Update IOBus + if self.Inputs.IOBus.Src then + -- Was there any update in that that would require flushing + local DataUpdated = false + + -- Update any cells that must be updated + for port = 0,1023 do + if self.Inputs.IOBus.Src.ReadCell then + local var = self.Inputs.IOBus.Src:ReadCell(port) + if var then + if self:ReadCell(port+63488) ~= var then + self:WriteCell(port+63488,var) + DataUpdated = true + end + end + end + end + + -- Flush updated data + if DataUpdated then self.Cache:Flush() end + end + + -- Flush any data in cache + self.Cache:Flush() + + -- Update video output, and send any changes to client + if self.Inputs.SoundOut.Src then + self.QueryRecurseCounter = 0 + self.QueryResult = { } + self:QuerySoundEmitters(self.Inputs.SoundOut.Src) + + -- Check if sound emitters setup has changed + local soundEmittersChanged = false + for k,v in pairs(self.QueryResult) do + if self.SoundEmitters[k] ~= v then + soundEmittersChanged = true + break + end + end + + if not soundEmittersChanged then + for k,v in pairs(self.SoundEmitters) do + if self.QueryResult[k] ~= v then + soundEmittersChanged = true + break + end + end + end + + if #self.QueryResult ~= #self.SoundEmitters then soundEmittersChanged = true end + + if soundEmittersChanged then + self.SoundEmitters = self.QueryResult + end + + -- Send update to all clients + if soundEmittersChanged then + umsg.Start("wire_spu_soundstate") + umsg.Long(self:EntIndex()) + umsg.Short(#self.SoundEmitters) + for idx=1,#self.SoundEmitters do + umsg.Long(self.SoundEmitters[idx]) + end + umsg.End() + end + end + + self:NextThink(CurTime()+0.05) + return true end duplicator.RegisterEntityClass("gmod_wire_spu", WireLib.MakeWireEnt, "Data") diff --git a/lua/wire/client/hlzasm/hc_codetree.lua b/lua/wire/client/hlzasm/hc_codetree.lua index dbf4a92ff4..fe9c837436 100644 --- a/lua/wire/client/hlzasm/hc_codetree.lua +++ b/lua/wire/client/hlzasm/hc_codetree.lua @@ -1,26 +1,26 @@ -------------------------------------------------------------------------------- -- Creates new code tree leaf function HCOMP:NewLeaf(parentLeaf) - local leaf = { - Opcode = "INVALID", -- Opcode number in this leaf - Operands = {}, - ParentLabel = self.CurrentParentLabel, - } - - if parentLeaf then - leaf.CurrentPosition = parentLeaf.CurrentPosition - else - leaf.CurrentPosition = self:CurrentSourcePosition() - end - return leaf + local leaf = { + Opcode = "INVALID", -- Opcode number in this leaf + Operands = {}, + ParentLabel = self.CurrentParentLabel, + } + + if parentLeaf then + leaf.CurrentPosition = parentLeaf.CurrentPosition + else + leaf.CurrentPosition = self:CurrentSourcePosition() + end + return leaf end function HCOMP:NewOpcode(opcode,op1,op2) - local leaf = self:NewLeaf() - leaf.Opcode = opcode - leaf.Operands[1] = op1 - leaf.Operands[2] = op2 - return leaf + local leaf = self:NewLeaf() + leaf.Opcode = opcode + leaf.Operands[1] = op1 + leaf.Operands[2] = op2 + return leaf end -- Each operand can contain the following entries: @@ -74,27 +74,27 @@ end -------------------------------------------------------------------------------- -- Adds leaf to the tail function HCOMP:AddLeafToTail(leaf) - if self.BusyRegisters then - leaf.BusyRegisters = self.BusyRegisters - end - - if self.GenerateInlineFunction then - table.insert(self.InlineFunctionCode,leaf) - else - table.insert(self.CodeTree,leaf) - end + if self.BusyRegisters then + leaf.BusyRegisters = self.BusyRegisters + end + + if self.GenerateInlineFunction then + table.insert(self.InlineFunctionCode,leaf) + else + table.insert(self.CodeTree,leaf) + end end -------------------------------------------------------------------------------- -- Returns free (non-busy) register. Does not check EBP and ESP function HCOMP:FreeRegister() - -- Try to find a free register - for i=1,#self.RegisterBusy do - if not self.RegisterBusy[i] then return i end - end + -- Try to find a free register + for i=1,#self.RegisterBusy do + if not self.RegisterBusy[i] then return i end + end - -- Try to find a register that wasnt pushed to stack yet + -- Try to find a register that wasnt pushed to stack yet -- for i=1,6 do -- if not self.RegisterStackOffset[i] then -- local pushLeaf = self:NewLeaf() @@ -103,16 +103,16 @@ function HCOMP:FreeRegister() -- self.RegisterStackOffset[i] = 0 -- end -- end - -- FIXME: non-busy register must always exist? + -- FIXME: non-busy register must always exist? - self:Error("Out of free registers",self.ErrorReportLeaf) - return 1 + self:Error("Out of free registers",self.ErrorReportLeaf) + return 1 end -- Gets current list of registers used by users. This can be either the global -- list, or list inside current code block function HCOMP:GetUserRegisters() - return self.UserRegisters + return self.UserRegisters end @@ -121,135 +121,135 @@ end -- Makes first operand temporary -- Returns the register number used, changes the first operand to a temp register function HCOMP:MakeFirstOperandTemporary(operands) - local freeReg = self:FreeRegister() - - -- Generate MOV - local movLeaf = self:NewLeaf(self.ErrorReportLeaf) - movLeaf.Opcode = "mov" - movLeaf.Operands[1] = { - Register = freeReg - } - movLeaf.Operands[2] = operands[1] - self:GenerateLeaf(movLeaf) - - -- Sets operand to be free register, and marks it as temporary and busy - operands[1] = movLeaf.Operands[1] - operands[1].Temporary = true - self.RegisterBusy[freeReg] = true - - return freeReg + local freeReg = self:FreeRegister() + + -- Generate MOV + local movLeaf = self:NewLeaf(self.ErrorReportLeaf) + movLeaf.Opcode = "mov" + movLeaf.Operands[1] = { + Register = freeReg + } + movLeaf.Operands[2] = operands[1] + self:GenerateLeaf(movLeaf) + + -- Sets operand to be free register, and marks it as temporary and busy + operands[1] = movLeaf.Operands[1] + operands[1].Temporary = true + self.RegisterBusy[freeReg] = true + + return freeReg end -- Reads operand from stack into a temp register (index: which operand is the stack index) function HCOMP:ReadOperandFromStack(operands,index,forceRead) - local stackOffset = operands[index].Stack - local freeReg = self:FreeRegister() - - -- Generate RSTACK opcode to read value from stack - local rstackLeaf = self:NewLeaf(self.ErrorReportLeaf) - rstackLeaf.Opcode = "rstack" - rstackLeaf.Operands[1] = { - Register = freeReg, - } - if tonumber(stackOffset) then -- Stack offset is a constant value - rstackLeaf.Operands[2] = { - Constant = stackOffset, - Segment = 16 - } - else -- Stack offset is a leaf that returns a constant value - -- Register must be marked used up so its not used in next gen step - self.RegisterBusy[freeReg] = true - - -- Request result of this leaf into a register - local offsetReg,isTemp = self:GenerateLeaf(operands[index].Stack,true) - rstackLeaf.Operands[2] = { - Register = offsetReg, - Segment = 16, - Temporary = isTemp - } - self.RegisterBusy[offsetReg] = isTemp - - self.RegisterBusy[freeReg] = false - end - - -- Generate "RSTACK" leaf - self:GenerateLeaf(rstackLeaf) - - -- Mark register as used (couldn't do before or else code generator would - -- mess up the RSTACK instruction) - self.RegisterBusy[freeReg] = true - - -- Change the operand (and make sure it retains stack offset) - operands[index] = rstackLeaf.Operands[1] - operands[index].Temporary = true - operands[index].Stack = stackOffset + local stackOffset = operands[index].Stack + local freeReg = self:FreeRegister() + + -- Generate RSTACK opcode to read value from stack + local rstackLeaf = self:NewLeaf(self.ErrorReportLeaf) + rstackLeaf.Opcode = "rstack" + rstackLeaf.Operands[1] = { + Register = freeReg, + } + if tonumber(stackOffset) then -- Stack offset is a constant value + rstackLeaf.Operands[2] = { + Constant = stackOffset, + Segment = 16 + } + else -- Stack offset is a leaf that returns a constant value + -- Register must be marked used up so its not used in next gen step + self.RegisterBusy[freeReg] = true + + -- Request result of this leaf into a register + local offsetReg,isTemp = self:GenerateLeaf(operands[index].Stack,true) + rstackLeaf.Operands[2] = { + Register = offsetReg, + Segment = 16, + Temporary = isTemp + } + self.RegisterBusy[offsetReg] = isTemp + + self.RegisterBusy[freeReg] = false + end + + -- Generate "RSTACK" leaf + self:GenerateLeaf(rstackLeaf) + + -- Mark register as used (couldn't do before or else code generator would + -- mess up the RSTACK instruction) + self.RegisterBusy[freeReg] = true + + -- Change the operand (and make sure it retains stack offset) + operands[index] = rstackLeaf.Operands[1] + operands[index].Temporary = true + operands[index].Stack = stackOffset end -- Reads operand from memory (index: which operand is the memory pointer) -- Replaces operand with a temporary register function HCOMP:ReadOperandFromMemory(operands,index) - if operands[index].MemoryPointer.Opcode then -- Parse complex expression - local addrReg,isTemp = self:GenerateLeaf(operands[index].MemoryPointer,true) - operands[index] = { MemoryRegister = addrReg, Segment = operands[index].Segment, Temporary = isTemp } - self.RegisterBusy[addrReg] = isTemp - return addrReg - else -- Parse an operand - local freeReg = self:FreeRegister() - if operands[index].MemoryPointer.Stack then -- Generate stack read - if not tonumber(operands[index].MemoryPointer.Stack) then self:Error("Internal error 186") end - local rstackLeaf = self:NewLeaf(self.ErrorReportLeaf) - rstackLeaf.Opcode = "rstack" - rstackLeaf.Operands[1] = { Register = freeReg } - rstackLeaf.Operands[2] = { Constant = operands[index].MemoryPointer.Stack, Segment = 16 } - self:GenerateLeaf(rstackLeaf) - operands[index] = { MemoryRegister = freeReg, Segment = operands[index].Segment, Temporary = true } - self.RegisterBusy[freeReg] = true - return addrReg - else -- Generate more than just a stack read - if operands[index].MemoryPointer.Register then - operands[index] = { MemoryRegister = operands[index].MemoryPointer.Register, Temporary = operands[index].MemoryPointer.Temporary } - return operands[index].Register - elseif operands[index].MemoryPointer.Constant then - if istable(operands[index].MemoryPointer.Constant) then - -- Don't decay a label constant expression into an unusable memory address if possible - for _,item in pairs(operands[index].MemoryPointer.Constant) do - if item.Type == self.TOKEN.IDENT then - operands[index] = { MemoryPointer = operands[index].MemoryPointer.Constant } - return nil - end - end - end - operands[index] = { Memory = operands[index].MemoryPointer.Constant } - return nil - else - local movLeaf = self:NewLeaf(self.ErrorReportLeaf) - movLeaf.Opcode = "mov" - movLeaf.Operands[1] = { Register = freeReg, Temporary = true } - movLeaf.Operands[2] = operands[index].MemoryPointer - self.RegisterBusy[freeReg] = true - - local addrReg,isTemp = self:GenerateLeaf(movLeaf,true) - operands[index] = { MemoryRegister = addrReg, Temporary = isTemp } - self.RegisterBusy[addrReg] = isTemp - return addrReg - end - end - end + if operands[index].MemoryPointer.Opcode then -- Parse complex expression + local addrReg,isTemp = self:GenerateLeaf(operands[index].MemoryPointer,true) + operands[index] = { MemoryRegister = addrReg, Segment = operands[index].Segment, Temporary = isTemp } + self.RegisterBusy[addrReg] = isTemp + return addrReg + else -- Parse an operand + local freeReg = self:FreeRegister() + if operands[index].MemoryPointer.Stack then -- Generate stack read + if not tonumber(operands[index].MemoryPointer.Stack) then self:Error("Internal error 186") end + local rstackLeaf = self:NewLeaf(self.ErrorReportLeaf) + rstackLeaf.Opcode = "rstack" + rstackLeaf.Operands[1] = { Register = freeReg } + rstackLeaf.Operands[2] = { Constant = operands[index].MemoryPointer.Stack, Segment = 16 } + self:GenerateLeaf(rstackLeaf) + operands[index] = { MemoryRegister = freeReg, Segment = operands[index].Segment, Temporary = true } + self.RegisterBusy[freeReg] = true + return addrReg + else -- Generate more than just a stack read + if operands[index].MemoryPointer.Register then + operands[index] = { MemoryRegister = operands[index].MemoryPointer.Register, Temporary = operands[index].MemoryPointer.Temporary } + return operands[index].Register + elseif operands[index].MemoryPointer.Constant then + if istable(operands[index].MemoryPointer.Constant) then + -- Don't decay a label constant expression into an unusable memory address if possible + for _,item in pairs(operands[index].MemoryPointer.Constant) do + if item.Type == self.TOKEN.IDENT then + operands[index] = { MemoryPointer = operands[index].MemoryPointer.Constant } + return nil + end + end + end + operands[index] = { Memory = operands[index].MemoryPointer.Constant } + return nil + else + local movLeaf = self:NewLeaf(self.ErrorReportLeaf) + movLeaf.Opcode = "mov" + movLeaf.Operands[1] = { Register = freeReg, Temporary = true } + movLeaf.Operands[2] = operands[index].MemoryPointer + self.RegisterBusy[freeReg] = true + + local addrReg,isTemp = self:GenerateLeaf(movLeaf,true) + operands[index] = { MemoryRegister = addrReg, Temporary = isTemp } + self.RegisterBusy[addrReg] = isTemp + return addrReg + end + end + end end -- Turns known label operation into a know one function HCOMP:TurnUnknownLabelIntoKnown(operand) - local label = operand.UnknownOperationByLabel - operand.UnknownOperationByLabel = nil - --print("UNKLABEL",label.Name,label.Type) - if label.Type == "Variable" then - operand.Memory = label - elseif label.Type == "Pointer" then - operand.Constant = label.Expression or {{ Type = self.TOKEN.IDENT, Data = label.Name, Position = self.ErrorReportLeaf.CurrentPosition }} - else - if not label.Name then self:Error("Internal error 033") end - self:Error("Undefined label: "..label.Name,self.ErrorReportLeaf) - end + local label = operand.UnknownOperationByLabel + operand.UnknownOperationByLabel = nil + --print("UNKLABEL",label.Name,label.Type) + if label.Type == "Variable" then + operand.Memory = label + elseif label.Type == "Pointer" then + operand.Constant = label.Expression or {{ Type = self.TOKEN.IDENT, Data = label.Name, Position = self.ErrorReportLeaf.CurrentPosition }} + else + if not label.Name then self:Error("Internal error 033") end + self:Error("Undefined label: "..label.Name,self.ErrorReportLeaf) + end end @@ -270,33 +270,33 @@ end -- not that it's actually temporary! -- function HCOMP:GenerateLeaf(leaf,needResult) - -- Set this leaf for error reporting (small hack) - self.ErrorReportLeaf = leaf + -- Set this leaf for error reporting (small hack) + self.ErrorReportLeaf = leaf -- local initTempRegisters = {} -- for k,v in pairs(self.RegisterBusy) do -- initTempRegisters[k] = v -- end - -- If we have previous leaf, generate it - if leaf.PreviousLeaf then - self:GenerateLeaf(leaf.PreviousLeaf,false) - leaf.PreviousLeaf = nil - end - - -- Do not generate invalid tree leaves - if not leaf.Opcode then - if not leaf.PreviousLeaf then --not leaf.Register then - if istable(leaf.Constant) then - self:Warning("Trying to generate invalid code ("..self:PrintTokens(leaf.Constant)..")",leaf) - elseif not leaf.ForceTemporary then - self:Warning("Trying to generate invalid code",leaf) - end - end - return - end - - -- If operand has a previous leaf assigned, generate it + -- If we have previous leaf, generate it + if leaf.PreviousLeaf then + self:GenerateLeaf(leaf.PreviousLeaf,false) + leaf.PreviousLeaf = nil + end + + -- Do not generate invalid tree leaves + if not leaf.Opcode then + if not leaf.PreviousLeaf then --not leaf.Register then + if istable(leaf.Constant) then + self:Warning("Trying to generate invalid code ("..self:PrintTokens(leaf.Constant)..")",leaf) + elseif not leaf.ForceTemporary then + self:Warning("Trying to generate invalid code",leaf) + end + end + return + end + + -- If operand has a previous leaf assigned, generate it -- for i=1,#leaf.Operands do -- if leaf.Operands[i].PreviousLeaf then -- self:GenerateLeaf(leaf.Operands[i].PreviousLeaf,false) @@ -304,322 +304,322 @@ function HCOMP:GenerateLeaf(leaf,needResult) -- end -- end - -- Check if this opcode writes to its first argument - local opcodeWritesFirstOperand = (#leaf.Operands == 2) or - ((#leaf.Operands == 1) and (self.OpcodeWritesOperand[leaf.Opcode])) - - -- Generate explict operands for this leaf - local genOperands = {} - local i = #leaf.Operands - while i >= 1 do - if leaf.Operands[i].PreviousLeaf then - self:GenerateLeaf(leaf.Operands[i].PreviousLeaf,false) - leaf.Operands[i].PreviousLeaf = nil - end - - -- Turn unknown label into known one - if leaf.Operands[i].UnknownOperationByLabel then - --self:TurnUnknownLabelIntoKnown(leaf.Operands[i]) - local label = leaf.Operands[i].UnknownOperationByLabel - leaf.Operands[i].UnknownOperationByLabel = nil - if label.Type == "Variable" then - leaf.Operands[i].Memory = label - elseif label.Type == "Pointer" then - leaf.Operands[i].Constant = label.Expression or {{ Type = self.TOKEN.IDENT, Data = label.Name, Position = self.ErrorReportLeaf.CurrentPosition }} - else - if not label.Name then self:Error("Internal error 033") end - self:Error("Undefined label: "..label.Name,self.ErrorReportLeaf) - end - end - if leaf.Operands[i].MemoryPointer and - istable(leaf.Operands[i].MemoryPointer) and - leaf.Operands[i].MemoryPointer.UnknownOperationByLabel then + -- Check if this opcode writes to its first argument + local opcodeWritesFirstOperand = (#leaf.Operands == 2) or + ((#leaf.Operands == 1) and (self.OpcodeWritesOperand[leaf.Opcode])) + + -- Generate explict operands for this leaf + local genOperands = {} + local i = #leaf.Operands + while i >= 1 do + if leaf.Operands[i].PreviousLeaf then + self:GenerateLeaf(leaf.Operands[i].PreviousLeaf,false) + leaf.Operands[i].PreviousLeaf = nil + end + + -- Turn unknown label into known one + if leaf.Operands[i].UnknownOperationByLabel then + --self:TurnUnknownLabelIntoKnown(leaf.Operands[i]) + local label = leaf.Operands[i].UnknownOperationByLabel + leaf.Operands[i].UnknownOperationByLabel = nil + if label.Type == "Variable" then + leaf.Operands[i].Memory = label + elseif label.Type == "Pointer" then + leaf.Operands[i].Constant = label.Expression or {{ Type = self.TOKEN.IDENT, Data = label.Name, Position = self.ErrorReportLeaf.CurrentPosition }} + else + if not label.Name then self:Error("Internal error 033") end + self:Error("Undefined label: "..label.Name,self.ErrorReportLeaf) + end + end + if leaf.Operands[i].MemoryPointer and + istable(leaf.Operands[i].MemoryPointer) and + leaf.Operands[i].MemoryPointer.UnknownOperationByLabel then -- self:TurnUnknownLabelIntoKnown(leaf.Operands[i].MemoryPointer) - local label = leaf.Operands[i].MemoryPointer.UnknownOperationByLabel - leaf.Operands[i].MemoryPointer.UnknownOperationByLabel = nil - if label.Type == "Variable" then - leaf.Operands[i].MemoryPointer = { Memory = label } - elseif label.Type == "Pointer" then - leaf.Operands[i].MemoryPointer = label.Expression or {{ Type = self.TOKEN.IDENT, Data = label.Name, Position = self.ErrorReportLeaf.CurrentPosition }, TokenList = true } - elseif label.Type == "Register" then - leaf.Operands[i].MemoryPointer = { MemoryRegister = label.Value } - else - if not label.Name then self:Error("Internal error 033") end - self:Error("Undefined label: "..label.Name,self.ErrorReportLeaf) - end - end - - if leaf.Operands[i].Opcode then -- It's an opcode, not an operand we can write - -- Try to calculate the leaf into some temporary register - local resultReg,isTemp = self:GenerateLeaf(leaf.Operands[i],true) - if resultReg then - self.RegisterBusy[resultReg] = isTemp - genOperands[i] = { - Register = resultReg, - Temporary = isTemp, - } - else - -- Generate invalid constant value - genOperands[i] = { - Constant = self.Settings.MagicValue, - } - self:Error("Expression messed up",leaf) - end - else - genOperands[i] = {} - for k,v in pairs(leaf.Operands[i]) do genOperands[i][k] = v end - - -- Need a real explict value if its laying on stack, and we want to write into it - -- Do not gen explict value if our opcode is MOV though (we can turn it into sstack later) - -- Also do not generate explict value if its an explict assign - if genOperands[i].Stack then - if ((i == 1) and (leaf.Opcode ~= "mov")) or -- Force value out of stack if we are about to perform an instruction on it - (i == 2) then -- Or if we are about to read from it - self:ReadOperandFromStack(genOperands,i) - end - end - - -- Need a real explict value if its a non-constant address to memory - if istable(genOperands[i].MemoryPointer) and not genOperands[i].MemoryPointer.TokenList then - self:ReadOperandFromMemory(genOperands,i) - end - - -- Calculate pointer to the label if required - -- was "(i > 1) and genOperands[i].PointerToLabel" - if genOperands[i].PointerToLabel then - local label = genOperands[i].PointerToLabel - if (label.Type == "Variable") or (label.Type == "Unknown") then -- Read from a variable - - genOperands[i] = - { Constant = - { { Type = self.TOKEN.AND, Position = leaf.CurrentPosition }, - { Type = self.TOKEN.IDENT, Data = genOperands[i].PointerToLabel.Name, Position = leaf.CurrentPosition }, - } - } - elseif label.Type == "Stack" then -- Read from stack - genOperands[i] = { Constant = genOperands[i].PointerToLabel.StackOffset } - elseif label.Type == "Pointer" then -- Pointer value - genOperands[i] = { Constant = {{ Type = self.TOKEN.IDENT, Data = genOperands[i].PointerToLabel.Name, Position = leaf.CurrentPosition }} } - end - end - - -- Make register operand temporary if requested - if genOperands[i].ForceTemporary then - local initReg - if genOperands[i].Register then - initReg = genOperands[i].Register - elseif genOperands[i].MemoryRegister then - initReg = genOperands[i].MemoryRegister - end - genOperands[i].ForceTemporary = false - - if self.RegisterBusy[initReg] then - local freeReg = self:FreeRegister() - self.RegisterBusy[initReg] = false - - local pushLeaf = self:NewLeaf(leaf) - pushLeaf.Opcode = "push" - pushLeaf.Operands[1] = { Register = initReg } - self:GenerateLeaf(pushLeaf) - - local movLeaf = self:NewLeaf(leaf) - movLeaf.Opcode = "mov" - movLeaf.Operands[1] = { Register = freeReg, Temporary = true } - movLeaf.Operands[2] = { Register = initReg } - self.RegisterBusy[freeReg] = true - - local addrReg,isTemp = self:GenerateLeaf(movLeaf,true) - genOperands[i] = { Register = addrReg, Temporary = isTemp } - self.RegisterBusy[addrReg] = isTemp - - local popLeaf = self:NewLeaf(leaf) - popLeaf.Opcode = "pop" - popLeaf.Operands[1] = { Register = initReg } - self:GenerateLeaf(popLeaf) - - self.RegisterBusy[initReg] = true - else - genOperands[i].Temporary = true - self.RegisterBusy[initReg] = true - end - end - end - - i = i - 1 - end - - -- Result to return (if needResult is true) - local destRegister,isDestTemp - - if opcodeWritesFirstOperand then - -- Apply hack for trigonometric operations which look like "FSIN EAX,EAX" instead of "FSIN EAX" - if (#leaf.Operands > 1) and (genOperands[2].TrigonometryHack) then - genOperands[2] = genOperands[1] - end - - -- Are we trying to operate on a value which is busy or must not be changed? (MOV busyReg,<...>) - -- But if register is temporary, lets just re-assign it - if (not leaf.ExplictAssign) and - (genOperands[1].Register) and (not genOperands[1].Temporary) and - ((self.RegisterBusy[genOperands[1].Register] == true) or - (self.RegisterBusy[genOperands[1].Register] == nil)) then - self:MakeFirstOperandTemporary(genOperands) - end - - -- Check if we are trying to do "MOV VAR,<...>" when VAR is a stack one (change to SSTACK instead) - if (genOperands[1].Stack) and (leaf.Opcode == "mov") then - -- MOV STK(10),123 -> SSTACK EBP:10,123 - leaf.Opcode = "sstack" - if tonumber(genOperands[1].Stack) then - genOperands[1].Constant = genOperands[1].Stack - genOperands[1].Register = nil - else - local offsetReg,isTemp = self:GenerateLeaf(genOperands[1].Stack,true) - genOperands[1] = { Register = offsetReg, Temporary = isTemp } - self.RegisterBusy[offsetReg] = isTemp - end - genOperands[1].Segment = 16 - genOperands[1].Stack = nil - end - - -- Check if we are trying to do "INC VAR" when VAR is a stack, explict one - if leaf.ExplictAssign and (genOperands[1].Stack) then - -- INC STK(10) -> INC STK(10); SSTACK EBP:10,VAR - local sstackLeaf = self:NewLeaf(leaf) - sstackLeaf.Opcode = "sstack" - if tonumber(genOperands[1].Stack) then - sstackLeaf.Operands[1] = { Constant = genOperands[1].Stack } - else - local offsetReg,isTemp = self:GenerateLeaf(genOperands[1].Stack,true) - sstackLeaf.Operands[1] = { Register = offsetReg, Temporary = isTemp } - self.RegisterBusy[offsetReg] = isTemp - end - - genOperands[1].Stack = nil - sstackLeaf.Operands[1].Segment = 16 - sstackLeaf.Operands[2] = genOperands[1] - leaf.NextLeaf = sstackLeaf - end - - -- Are we trying to do "ADD VAR,<...>" when VAR is a stack one? - -- At this point "VAR" is already read into a register, so generate this leaf, and then - -- change it to sstack - if (genOperands[1].Stack) and (genOperands[1].Register) then - local tempReg = genOperands[1].Register - local stackOffset = genOperands[1].Stack - - -- Generate proper opcode for the opepration - local operationLeaf = self:NewLeaf(leaf) - operationLeaf.Opcode = leaf.Opcode - operationLeaf.Operands[1] = { - Register = tempReg, - Temporary = true - } - self.RegisterBusy[tempReg] = true - if #leaf.Operands > 1 then - -- Generate second operand. It's already read from stack into a temp register, - -- so remove the "stack" marker - operationLeaf.Operands[2] = genOperands[2] - operationLeaf.Operands[2].Stack = nil - end - self:GenerateLeaf(operationLeaf) - - -- Turn this operation into SSTACK - leaf.Opcode = "sstack" - genOperands[2] = { - Register = tempReg, - Temporary = true - } - genOperands[1] = { - Constant = stackOffset, - Segment = 16 - } - self.RegisterBusy[tempReg] = true - end - - -- If we really need result, then make sure it lies in some register - if needResult then - if genOperands[1].Register and (not leaf.ExplictAssign) then - -- If operand is already a register, just return it - -- (that is, unless we want an explict assign operation on it) - destRegister = genOperands[1].Register - isDestTemp = genOperands[1].Temporary - else - -- Otherwise we will need to copy this result into a register - if leaf.Opcode == "sstack" then - -- turn SSTACK into MOV - destRegister = self:FreeRegister() - leaf.Opcode = "mov" - genOperands[1] = { Register = destRegister, Temporary = true } --FIXME - isDestTemp = true - - -- This happens when there is a code tree like this: - -- cmp - -- 0 - -- add - -- stack 0 - -- stack 1 - else - if leaf.ExplictAssign then -- Perform explict assign and copy to temp register anyway - if leaf.ReturnAfterAssign then - -- HACK: generate opcode before the MOV - self:GenerateOpcode(leaf,genOperands) - leaf.Opcode = nil - - local movLeaf = self:NewLeaf(leaf) - destRegister = self:FreeRegister() - movLeaf.Opcode = "mov" - movLeaf.Operands[1] = { - Register = destRegister - } - movLeaf.Operands[2] = genOperands[1] - self:GenerateLeaf(movLeaf) - self.RegisterBusy[destRegister] = true - isDestTemp = true - else - local movLeaf = self:NewLeaf(leaf) - destRegister = self:FreeRegister() - movLeaf.Opcode = "mov" - movLeaf.Operands[1] = { - Register = destRegister - } - movLeaf.Operands[2] = genOperands[1] - self:GenerateLeaf(movLeaf) - self.RegisterBusy[destRegister] = true - isDestTemp = true - end - else -- Just make it temporary - destRegister = self:MakeFirstOperandTemporary(genOperands) - isDestTemp = true - end - end - end - end - end - - -- Generate the opcode, unless it's a "MOV REG,REG" (happens with RETURN token) - if (not ((leaf.Opcode == "mov") and genOperands[1].Register and (genOperands[1].Register == genOperands[2].Register))) and - (leaf.Opcode) then - self:GenerateOpcode(leaf,genOperands) - end - - -- Generate next leaf too, if required - if leaf.NextLeaf then - self:GenerateLeaf(leaf.NextLeaf,false) - end - - -- Reset all temporary registers used in the expression - for i=1,#genOperands do - if genOperands[i].Temporary then - if genOperands[i].Register then - self.RegisterBusy[genOperands[i].Register] = false - elseif genOperands[i].MemoryRegister then - self.RegisterBusy[genOperands[i].MemoryRegister] = false - else - self:Error("Internal error 379",leaf) - end - end - end + local label = leaf.Operands[i].MemoryPointer.UnknownOperationByLabel + leaf.Operands[i].MemoryPointer.UnknownOperationByLabel = nil + if label.Type == "Variable" then + leaf.Operands[i].MemoryPointer = { Memory = label } + elseif label.Type == "Pointer" then + leaf.Operands[i].MemoryPointer = label.Expression or {{ Type = self.TOKEN.IDENT, Data = label.Name, Position = self.ErrorReportLeaf.CurrentPosition }, TokenList = true } + elseif label.Type == "Register" then + leaf.Operands[i].MemoryPointer = { MemoryRegister = label.Value } + else + if not label.Name then self:Error("Internal error 033") end + self:Error("Undefined label: "..label.Name,self.ErrorReportLeaf) + end + end + + if leaf.Operands[i].Opcode then -- It's an opcode, not an operand we can write + -- Try to calculate the leaf into some temporary register + local resultReg,isTemp = self:GenerateLeaf(leaf.Operands[i],true) + if resultReg then + self.RegisterBusy[resultReg] = isTemp + genOperands[i] = { + Register = resultReg, + Temporary = isTemp, + } + else + -- Generate invalid constant value + genOperands[i] = { + Constant = self.Settings.MagicValue, + } + self:Error("Expression messed up",leaf) + end + else + genOperands[i] = {} + for k,v in pairs(leaf.Operands[i]) do genOperands[i][k] = v end + + -- Need a real explict value if its laying on stack, and we want to write into it + -- Do not gen explict value if our opcode is MOV though (we can turn it into sstack later) + -- Also do not generate explict value if its an explict assign + if genOperands[i].Stack then + if ((i == 1) and (leaf.Opcode ~= "mov")) or -- Force value out of stack if we are about to perform an instruction on it + (i == 2) then -- Or if we are about to read from it + self:ReadOperandFromStack(genOperands,i) + end + end + + -- Need a real explict value if its a non-constant address to memory + if istable(genOperands[i].MemoryPointer) and not genOperands[i].MemoryPointer.TokenList then + self:ReadOperandFromMemory(genOperands,i) + end + + -- Calculate pointer to the label if required + -- was "(i > 1) and genOperands[i].PointerToLabel" + if genOperands[i].PointerToLabel then + local label = genOperands[i].PointerToLabel + if (label.Type == "Variable") or (label.Type == "Unknown") then -- Read from a variable + + genOperands[i] = + { Constant = + { { Type = self.TOKEN.AND, Position = leaf.CurrentPosition }, + { Type = self.TOKEN.IDENT, Data = genOperands[i].PointerToLabel.Name, Position = leaf.CurrentPosition }, + } + } + elseif label.Type == "Stack" then -- Read from stack + genOperands[i] = { Constant = genOperands[i].PointerToLabel.StackOffset } + elseif label.Type == "Pointer" then -- Pointer value + genOperands[i] = { Constant = {{ Type = self.TOKEN.IDENT, Data = genOperands[i].PointerToLabel.Name, Position = leaf.CurrentPosition }} } + end + end + + -- Make register operand temporary if requested + if genOperands[i].ForceTemporary then + local initReg + if genOperands[i].Register then + initReg = genOperands[i].Register + elseif genOperands[i].MemoryRegister then + initReg = genOperands[i].MemoryRegister + end + genOperands[i].ForceTemporary = false + + if self.RegisterBusy[initReg] then + local freeReg = self:FreeRegister() + self.RegisterBusy[initReg] = false + + local pushLeaf = self:NewLeaf(leaf) + pushLeaf.Opcode = "push" + pushLeaf.Operands[1] = { Register = initReg } + self:GenerateLeaf(pushLeaf) + + local movLeaf = self:NewLeaf(leaf) + movLeaf.Opcode = "mov" + movLeaf.Operands[1] = { Register = freeReg, Temporary = true } + movLeaf.Operands[2] = { Register = initReg } + self.RegisterBusy[freeReg] = true + + local addrReg,isTemp = self:GenerateLeaf(movLeaf,true) + genOperands[i] = { Register = addrReg, Temporary = isTemp } + self.RegisterBusy[addrReg] = isTemp + + local popLeaf = self:NewLeaf(leaf) + popLeaf.Opcode = "pop" + popLeaf.Operands[1] = { Register = initReg } + self:GenerateLeaf(popLeaf) + + self.RegisterBusy[initReg] = true + else + genOperands[i].Temporary = true + self.RegisterBusy[initReg] = true + end + end + end + + i = i - 1 + end + + -- Result to return (if needResult is true) + local destRegister,isDestTemp + + if opcodeWritesFirstOperand then + -- Apply hack for trigonometric operations which look like "FSIN EAX,EAX" instead of "FSIN EAX" + if (#leaf.Operands > 1) and (genOperands[2].TrigonometryHack) then + genOperands[2] = genOperands[1] + end + + -- Are we trying to operate on a value which is busy or must not be changed? (MOV busyReg,<...>) + -- But if register is temporary, lets just re-assign it + if (not leaf.ExplictAssign) and + (genOperands[1].Register) and (not genOperands[1].Temporary) and + ((self.RegisterBusy[genOperands[1].Register] == true) or + (self.RegisterBusy[genOperands[1].Register] == nil)) then + self:MakeFirstOperandTemporary(genOperands) + end + + -- Check if we are trying to do "MOV VAR,<...>" when VAR is a stack one (change to SSTACK instead) + if (genOperands[1].Stack) and (leaf.Opcode == "mov") then + -- MOV STK(10),123 -> SSTACK EBP:10,123 + leaf.Opcode = "sstack" + if tonumber(genOperands[1].Stack) then + genOperands[1].Constant = genOperands[1].Stack + genOperands[1].Register = nil + else + local offsetReg,isTemp = self:GenerateLeaf(genOperands[1].Stack,true) + genOperands[1] = { Register = offsetReg, Temporary = isTemp } + self.RegisterBusy[offsetReg] = isTemp + end + genOperands[1].Segment = 16 + genOperands[1].Stack = nil + end + + -- Check if we are trying to do "INC VAR" when VAR is a stack, explict one + if leaf.ExplictAssign and (genOperands[1].Stack) then + -- INC STK(10) -> INC STK(10); SSTACK EBP:10,VAR + local sstackLeaf = self:NewLeaf(leaf) + sstackLeaf.Opcode = "sstack" + if tonumber(genOperands[1].Stack) then + sstackLeaf.Operands[1] = { Constant = genOperands[1].Stack } + else + local offsetReg,isTemp = self:GenerateLeaf(genOperands[1].Stack,true) + sstackLeaf.Operands[1] = { Register = offsetReg, Temporary = isTemp } + self.RegisterBusy[offsetReg] = isTemp + end + + genOperands[1].Stack = nil + sstackLeaf.Operands[1].Segment = 16 + sstackLeaf.Operands[2] = genOperands[1] + leaf.NextLeaf = sstackLeaf + end + + -- Are we trying to do "ADD VAR,<...>" when VAR is a stack one? + -- At this point "VAR" is already read into a register, so generate this leaf, and then + -- change it to sstack + if (genOperands[1].Stack) and (genOperands[1].Register) then + local tempReg = genOperands[1].Register + local stackOffset = genOperands[1].Stack + + -- Generate proper opcode for the opepration + local operationLeaf = self:NewLeaf(leaf) + operationLeaf.Opcode = leaf.Opcode + operationLeaf.Operands[1] = { + Register = tempReg, + Temporary = true + } + self.RegisterBusy[tempReg] = true + if #leaf.Operands > 1 then + -- Generate second operand. It's already read from stack into a temp register, + -- so remove the "stack" marker + operationLeaf.Operands[2] = genOperands[2] + operationLeaf.Operands[2].Stack = nil + end + self:GenerateLeaf(operationLeaf) + + -- Turn this operation into SSTACK + leaf.Opcode = "sstack" + genOperands[2] = { + Register = tempReg, + Temporary = true + } + genOperands[1] = { + Constant = stackOffset, + Segment = 16 + } + self.RegisterBusy[tempReg] = true + end + + -- If we really need result, then make sure it lies in some register + if needResult then + if genOperands[1].Register and (not leaf.ExplictAssign) then + -- If operand is already a register, just return it + -- (that is, unless we want an explict assign operation on it) + destRegister = genOperands[1].Register + isDestTemp = genOperands[1].Temporary + else + -- Otherwise we will need to copy this result into a register + if leaf.Opcode == "sstack" then + -- turn SSTACK into MOV + destRegister = self:FreeRegister() + leaf.Opcode = "mov" + genOperands[1] = { Register = destRegister, Temporary = true } --FIXME + isDestTemp = true + + -- This happens when there is a code tree like this: + -- cmp + -- 0 + -- add + -- stack 0 + -- stack 1 + else + if leaf.ExplictAssign then -- Perform explict assign and copy to temp register anyway + if leaf.ReturnAfterAssign then + -- HACK: generate opcode before the MOV + self:GenerateOpcode(leaf,genOperands) + leaf.Opcode = nil + + local movLeaf = self:NewLeaf(leaf) + destRegister = self:FreeRegister() + movLeaf.Opcode = "mov" + movLeaf.Operands[1] = { + Register = destRegister + } + movLeaf.Operands[2] = genOperands[1] + self:GenerateLeaf(movLeaf) + self.RegisterBusy[destRegister] = true + isDestTemp = true + else + local movLeaf = self:NewLeaf(leaf) + destRegister = self:FreeRegister() + movLeaf.Opcode = "mov" + movLeaf.Operands[1] = { + Register = destRegister + } + movLeaf.Operands[2] = genOperands[1] + self:GenerateLeaf(movLeaf) + self.RegisterBusy[destRegister] = true + isDestTemp = true + end + else -- Just make it temporary + destRegister = self:MakeFirstOperandTemporary(genOperands) + isDestTemp = true + end + end + end + end + end + + -- Generate the opcode, unless it's a "MOV REG,REG" (happens with RETURN token) + if (not ((leaf.Opcode == "mov") and genOperands[1].Register and (genOperands[1].Register == genOperands[2].Register))) and + (leaf.Opcode) then + self:GenerateOpcode(leaf,genOperands) + end + + -- Generate next leaf too, if required + if leaf.NextLeaf then + self:GenerateLeaf(leaf.NextLeaf,false) + end + + -- Reset all temporary registers used in the expression + for i=1,#genOperands do + if genOperands[i].Temporary then + if genOperands[i].Register then + self.RegisterBusy[genOperands[i].Register] = false + elseif genOperands[i].MemoryRegister then + self.RegisterBusy[genOperands[i].MemoryRegister] = false + else + self:Error("Internal error 379",leaf) + end + end + end -- for k,v in pairs(initTempRegisters) do -- self.RegisterBusy[k] = v @@ -630,7 +630,7 @@ function HCOMP:GenerateLeaf(leaf,needResult) -- end -- end - return destRegister,isDestTemp + return destRegister,isDestTemp end @@ -645,59 +645,59 @@ function HCOMP:GenerateOpcode(leaf,operands) -- for i=1,8 do if self.RegisterBusy[i] then rstr = rstr.."1" else rstr = rstr.."0" end end -- print(leaf.Opcode,rstr,leaf.CurrentPosition.Line) - table.insert(self.GeneratedCode,{ - Opcode = leaf.Opcode, - Operands = operands, - Comment = leaf.Comment, - CurrentPosition = leaf.CurrentPosition, - }) + table.insert(self.GeneratedCode,{ + Opcode = leaf.Opcode, + Operands = operands, + Comment = leaf.Comment, + CurrentPosition = leaf.CurrentPosition, + }) end -- Generate other marker function HCOMP:GenerateMarker(leaf) - table.insert(self.GeneratedCode,{ - ZeroPadding = leaf.ZeroPadding, - Data = leaf.Data, - Label = leaf.Label, - Comment = leaf.Comment, - - SetWritePointer = leaf.SetWritePointer, - SetPointerOffset = leaf.SetPointerOffset, - CurrentPosition = leaf.CurrentPosition, - }) + table.insert(self.GeneratedCode,{ + ZeroPadding = leaf.ZeroPadding, + Data = leaf.Data, + Label = leaf.Label, + Comment = leaf.Comment, + + SetWritePointer = leaf.SetWritePointer, + SetPointerOffset = leaf.SetPointerOffset, + CurrentPosition = leaf.CurrentPosition, + }) end -------------------------------------------------------------------------------- -- Generate leaf (called by the stage, it generates special leaves too) function HCOMP:StageGenerateLeaf(leaf) - if self.Settings.OutputCodeTree then self:PrintLeaf(leaf) end - - if self.Settings.NoUnreferencedLeaves == true then - if leaf.ParentLabel and (leaf.ParentLabel.Referenced == false) then - -- Do not generate leafs that are parented to unreferenced labels - return false - end - end - - if (leaf.Opcode == "DATA") or - (leaf.Opcode == "LABEL") or - (leaf.Opcode == "MARKER") then - self:GenerateMarker(leaf) - return false - else - -- Make sure it never attempts to use ESP/EBP, mark them always busy - self.RegisterBusy = { false,false,false,false,false,false,true,true } - self.RegisterStackOffset = {} - - if leaf.BusyRegisters then - for k,v in pairs(leaf.BusyRegisters) do - self.RegisterBusy[k] = v - end - end - - self:GenerateLeaf(leaf) - - return false - end + if self.Settings.OutputCodeTree then self:PrintLeaf(leaf) end + + if self.Settings.NoUnreferencedLeaves == true then + if leaf.ParentLabel and (leaf.ParentLabel.Referenced == false) then + -- Do not generate leafs that are parented to unreferenced labels + return false + end + end + + if (leaf.Opcode == "DATA") or + (leaf.Opcode == "LABEL") or + (leaf.Opcode == "MARKER") then + self:GenerateMarker(leaf) + return false + else + -- Make sure it never attempts to use ESP/EBP, mark them always busy + self.RegisterBusy = { false,false,false,false,false,false,true,true } + self.RegisterStackOffset = {} + + if leaf.BusyRegisters then + for k,v in pairs(leaf.BusyRegisters) do + self.RegisterBusy[k] = v + end + end + + self:GenerateLeaf(leaf) + + return false + end end diff --git a/lua/wire/client/hlzasm/hc_compiler.lua b/lua/wire/client/hlzasm/hc_compiler.lua index ce457bbb11..b8a9e7cae6 100644 --- a/lua/wire/client/hlzasm/hc_compiler.lua +++ b/lua/wire/client/hlzasm/hc_compiler.lua @@ -42,30 +42,30 @@ include("wire/client/hlzasm/hc_tokenizer.lua") -------------------------------------------------------------------------------- -- Formats the prefix according to one of the three possible ways to raise error function HCOMP:formatPrefix(param1,param2,param3) - local line,col,file - if param2 then -- Specify line/col/file directly - line = param1 - col = param2 - file = param3 - elseif param1 then -- Specify parameter by block - if param1.CurrentPosition then - line = param1.CurrentPosition.Line - col = param1.CurrentPosition.Col - file = param1.CurrentPosition.File - end - else -- Get position from parser - local currentPosition = self:CurrentSourcePosition() - line = currentPosition.Line - col = currentPosition.Col - file = currentPosition.File - end - - if (not file) or (not line) or (not col) then - error("[global 1:1] Internal error 048") - end - - -- Format prefix for reporting warnings/errors - return "["..file.." "..line..":"..col.."]",file,line,col + local line,col,file + if param2 then -- Specify line/col/file directly + line = param1 + col = param2 + file = param3 + elseif param1 then -- Specify parameter by block + if param1.CurrentPosition then + line = param1.CurrentPosition.Line + col = param1.CurrentPosition.Col + file = param1.CurrentPosition.File + end + else -- Get position from parser + local currentPosition = self:CurrentSourcePosition() + line = currentPosition.Line + col = currentPosition.Col + file = currentPosition.File + end + + if (not file) or (not line) or (not col) then + error("[global 1:1] Internal error 048") + end + + -- Format prefix for reporting warnings/errors + return "["..file.." "..line..":"..col.."]",file,line,col end @@ -80,10 +80,10 @@ end -- Error(msg,line,col,filename) -- Raise an error when preprocessing function HCOMP:Error(msg,param1,param2,param3) - local prefix,file,line,col = self:formatPrefix(param1,param2,param3) - self.ErrorMessage = prefix..": "..msg - self.ErrorPosition = { Line = line, Col = col, File = file } - error(self.ErrorMessage) + local prefix,file,line,col = self:formatPrefix(param1,param2,param3) + self.ErrorMessage = prefix..": "..msg + self.ErrorPosition = { Line = line, Col = col, File = file } + error(self.ErrorMessage) end @@ -93,23 +93,23 @@ end -- -- Same rules for calling as for Error() function HCOMP:Warning(msg,param1,param2,param3) - local prefixstr,file,line,col = self:formatPrefix(param1,param2,param3) - local parent = self:CurrentSourcePosition().ParentFile - print(prefixstr..": Warning: "..msg) -- This is the original console print, we should only mute the editor warnings - if self.SilencedFiles[file] then - if self.SilencedFiles[file].Silenced or self.SilencedParents[parent] then - return - end - elseif self.SilencedParents[parent] then - return - end - local warning = {} - warning.trace = {start_line = line, start_col = col} - warning.message = msg - if file ~= self.FileName then -- self.FileName is the file that's open in the editor at the start of compile - warning.message = "(in file: '"..file.."') " .. warning.message - end - self.Warnings[#self.Warnings+1] = warning + local prefixstr,file,line,col = self:formatPrefix(param1,param2,param3) + local parent = self:CurrentSourcePosition().ParentFile + print(prefixstr..": Warning: "..msg) -- This is the original console print, we should only mute the editor warnings + if self.SilencedFiles[file] then + if self.SilencedFiles[file].Silenced or self.SilencedParents[parent] then + return + end + elseif self.SilencedParents[parent] then + return + end + local warning = {} + warning.trace = {start_line = line, start_col = col} + warning.message = msg + if file ~= self.FileName then -- self.FileName is the file that's open in the editor at the start of compile + warning.message = "(in file: '"..file.."') " .. warning.message + end + self.Warnings[#self.Warnings+1] = warning end @@ -117,27 +117,27 @@ end -- Print a line to specific file function HCOMP:PrintLine(file,...) - if self.Settings.OutputToFile then - local ffile = self.Settings[file] or file - local outputString = "" - local argc = select("#",...) - for i=1,argc do - if i < argc then - outputString = outputString..select(i,...).."\t" - else - outputString = outputString..select(i,...) - end - end - outputString = outputString.."\n" - self.OutputText[ffile] = (self.OutputText[ffile] or "") .. outputString - - -- Forced garbage collection for very large output - if #self.OutputText[ffile] > 96000 then - collectgarbage("step") - end - else - print(...) - end + if self.Settings.OutputToFile then + local ffile = self.Settings[file] or file + local outputString = "" + local argc = select("#",...) + for i=1,argc do + if i < argc then + outputString = outputString..select(i,...).."\t" + else + outputString = outputString..select(i,...) + end + end + outputString = outputString.."\n" + self.OutputText[ffile] = (self.OutputText[ffile] or "") .. outputString + + -- Forced garbage collection for very large output + if #self.OutputText[ffile] > 96000 then + collectgarbage("step") + end + else + print(...) + end end @@ -146,42 +146,42 @@ end -------------------------------------------------------------------------------- -- Emit a code byte to the output stream 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 - - if not byte then error("[global 1:1] Internal error 108") end - - -- Remember debug data - if block.CurrentPosition then - local currentPositionKey = block.CurrentPosition.Line..":"..block.CurrentPosition.File - self.DebugInfo.PositionByPointer[self.WritePointer] = block.CurrentPosition - if self.DebugInfo.PointersByLine[currentPositionKey] then - self.DebugInfo.PointersByLine[currentPositionKey][2] = self.WritePointer - else - self.DebugInfo.PointersByLine[currentPositionKey] = { self.WritePointer, self.WritePointer } - end - else - self.DebugInfo.PositionByPointer[self.WritePointer] = { Line = 0, Col = 0, File = "undefined" } - end - - -- Output binary listing - if self.Settings.OutputBinaryListing then - if not self.CurrentBinaryListingLine then - self.CurrentBinaryListingLine = "db " - end - self.CurrentBinaryListingLine = self.CurrentBinaryListingLine .. byte .. "," - if #self.CurrentBinaryListingLine > 60 then - self:PrintLine("binlist",string.sub(self.CurrentBinaryListingLine,1,#self.CurrentBinaryListingLine-1)) - self.CurrentBinaryListingLine = nil - end - end - self.WritePointer = self.WritePointer + 1 + -- 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 + + if not byte then error("[global 1:1] Internal error 108") end + + -- Remember debug data + if block.CurrentPosition then + local currentPositionKey = block.CurrentPosition.Line..":"..block.CurrentPosition.File + self.DebugInfo.PositionByPointer[self.WritePointer] = block.CurrentPosition + if self.DebugInfo.PointersByLine[currentPositionKey] then + self.DebugInfo.PointersByLine[currentPositionKey][2] = self.WritePointer + else + self.DebugInfo.PointersByLine[currentPositionKey] = { self.WritePointer, self.WritePointer } + end + else + self.DebugInfo.PositionByPointer[self.WritePointer] = { Line = 0, Col = 0, File = "undefined" } + end + + -- Output binary listing + if self.Settings.OutputBinaryListing then + if not self.CurrentBinaryListingLine then + self.CurrentBinaryListingLine = "db " + end + self.CurrentBinaryListingLine = self.CurrentBinaryListingLine .. byte .. "," + if #self.CurrentBinaryListingLine > 60 then + self:PrintLine("binlist",string.sub(self.CurrentBinaryListingLine,1,#self.CurrentBinaryListingLine-1)) + self.CurrentBinaryListingLine = nil + end + end + self.WritePointer = self.WritePointer + 1 end @@ -192,105 +192,105 @@ end -- -- Will call writeByteCallback(writeByteCaller,Address,Value) to output a byte function HCOMP:StartCompile(sourceCode,fileName,writeByteCallback,writeByteCaller) - -- Remember callbacks for the writing functions - self.WriteByteCallback = writeByteCallback - self.WriteByteCaller = writeByteCaller - - -- Refresh the keywords table. - local keywordsTable = WireTextEditor.Modes.ZCPU.keywordsTable - local keywordsTableOriginal = WireTextEditor.Modes.ZCPU.keywordsTableOriginal - - for k,v in pairs(keywordsTable) do - if keywordsTableOriginal[k] == nil then - keywordsTable[k] = nil - end - end - - -- Set the working directory - self.FileName = string.sub(fileName,string.find(fileName,"\\$") or 1) - if string.GetPathFromFilename then - local filePath = string.GetPathFromFilename(fileName) - self.WorkingDir = string.sub(filePath,(string.find(string.lower(filePath),"chip") or -4)+5) - else - self.WorkingDir = "" - end - - -- Initialize compiler settings - self.Settings = {} - - -- Internal settings - self.Settings.CurrentLanguage = "HLZASM" -- ZASM2 - self.Settings.CurrentPlatform = "CPU" - self.Settings.MagicValue = -700500 -- This magic value will appear in invalid output code - self.Settings.OptimizeLevel = 0 -- 0: none, 1: low, 2: high; high optimize level might mangle code for perfomance - - -- Verbosity settings - self.Settings.OutputCodeTree = false -- Output code tree for the source - self.Settings.OutputResolveListing = false -- Output code listing for resolve stage - self.Settings.OutputFinalListing = false -- Output code listing for final stage - self.Settings.OutputTokenListing = false -- Output tokenized code - self.Settings.OutputBinaryListing = false -- Output final binary as listing - self.Settings.OutputDebugListing = false -- Output debug data as listing - self.Settings.OutputToFile = false -- Output listings to files instead of console - self.Settings.OutputOffsetsInListing = true -- Output binary offsets in listings - self.Settings.OutputLabelsInListing = true -- Output labels in final listing - self.Settings.GenerateComments = true -- Generates comments in output listing - - -- Code generation settings - self.Settings.AutoBusyRegisters = false -- Automatically preserves or zaps a range of registers for all code leaves that are generated while this is enabled. - self.Settings.AutoBusyRegisterRanges = {} - self.Settings.FixedSizeOutput = false -- Output fixed-size instructions - self.Settings.SeparateDataSegment = false -- Puts all variables into separate data segment - self.Settings.GenerateLibrary = false -- Generate precompiled library - self.Settings.AlwaysEnterLeave = false -- Always generate the enter/leave blocks - self.Settings.NoUnreferencedLeaves = false -- Dont generate functions, variables that are not referenced - self.Settings.DataSegmentOffset = 0 -- Data segment offset for separate data segment - - self.Settings.NewIfDefs = true -- Changes ifdef handling to skip all tokens inside until endif - - -- Search paths - self.SearchPaths = { - "lib", - "inc" - } - - -- Prepare parser - self.Stage = 1 - self.Tokens = {} - self.Code = {{ Text = sourceCode, Line = 1, Col = 1, File = self.FileName, NextCharPos = 1 }} - - -- Structs - self.Structs = {} - self.StructSize = {} - - -- Prepare debug information - self.DebugInfo = {} - self.DebugInfo.Labels = {} - self.DebugInfo.PositionByPointer = {} - self.DebugInfo.PointersByLine = {} - self.Warnings = {} -- Warnings to pass to the editor - self.SilencedFiles = {} -- Will not push warnings from silenced files - self.SilencedParents = {} -- Includes from these files will get silenced - -- Exported function list (library generation) - self.ExportedSymbols = {} - self.LabelLookup = {} - self.LabelLookupCounter = 0 - - -- All functions defined so far - self.Functions = {} - -- Details about the current function that we're building - self.CurFunction = {} - -- All macros defined so far - self.Defines = {} - self.Defines["__LINE__"] = 0 - self.Defines["__FILE__"] = "" - self.IFDEFLevel = {} - self.SkipToEndIf = false - self.EndIfsToSkip = 0 - - -- Output text - self.OutputText = {} - return self.Warnings + -- Remember callbacks for the writing functions + self.WriteByteCallback = writeByteCallback + self.WriteByteCaller = writeByteCaller + + -- Refresh the keywords table. + local keywordsTable = WireTextEditor.Modes.ZCPU.keywordsTable + local keywordsTableOriginal = WireTextEditor.Modes.ZCPU.keywordsTableOriginal + + for k,v in pairs(keywordsTable) do + if keywordsTableOriginal[k] == nil then + keywordsTable[k] = nil + end + end + + -- Set the working directory + self.FileName = string.sub(fileName,string.find(fileName,"\\$") or 1) + if string.GetPathFromFilename then + local filePath = string.GetPathFromFilename(fileName) + self.WorkingDir = string.sub(filePath,(string.find(string.lower(filePath),"chip") or -4)+5) + else + self.WorkingDir = "" + end + + -- Initialize compiler settings + self.Settings = {} + + -- Internal settings + self.Settings.CurrentLanguage = "HLZASM" -- ZASM2 + self.Settings.CurrentPlatform = "CPU" + self.Settings.MagicValue = -700500 -- This magic value will appear in invalid output code + self.Settings.OptimizeLevel = 0 -- 0: none, 1: low, 2: high; high optimize level might mangle code for perfomance + + -- Verbosity settings + self.Settings.OutputCodeTree = false -- Output code tree for the source + self.Settings.OutputResolveListing = false -- Output code listing for resolve stage + self.Settings.OutputFinalListing = false -- Output code listing for final stage + self.Settings.OutputTokenListing = false -- Output tokenized code + self.Settings.OutputBinaryListing = false -- Output final binary as listing + self.Settings.OutputDebugListing = false -- Output debug data as listing + self.Settings.OutputToFile = false -- Output listings to files instead of console + self.Settings.OutputOffsetsInListing = true -- Output binary offsets in listings + self.Settings.OutputLabelsInListing = true -- Output labels in final listing + self.Settings.GenerateComments = true -- Generates comments in output listing + + -- Code generation settings + self.Settings.AutoBusyRegisters = false -- Automatically preserves or zaps a range of registers for all code leaves that are generated while this is enabled. + self.Settings.AutoBusyRegisterRanges = {} + self.Settings.FixedSizeOutput = false -- Output fixed-size instructions + self.Settings.SeparateDataSegment = false -- Puts all variables into separate data segment + self.Settings.GenerateLibrary = false -- Generate precompiled library + self.Settings.AlwaysEnterLeave = false -- Always generate the enter/leave blocks + self.Settings.NoUnreferencedLeaves = false -- Dont generate functions, variables that are not referenced + self.Settings.DataSegmentOffset = 0 -- Data segment offset for separate data segment + + self.Settings.NewIfDefs = true -- Changes ifdef handling to skip all tokens inside until endif + + -- Search paths + self.SearchPaths = { + "lib", + "inc" + } + + -- Prepare parser + self.Stage = 1 + self.Tokens = {} + self.Code = {{ Text = sourceCode, Line = 1, Col = 1, File = self.FileName, NextCharPos = 1 }} + + -- Structs + self.Structs = {} + self.StructSize = {} + + -- Prepare debug information + self.DebugInfo = {} + self.DebugInfo.Labels = {} + self.DebugInfo.PositionByPointer = {} + self.DebugInfo.PointersByLine = {} + self.Warnings = {} -- Warnings to pass to the editor + self.SilencedFiles = {} -- Will not push warnings from silenced files + self.SilencedParents = {} -- Includes from these files will get silenced + -- Exported function list (library generation) + self.ExportedSymbols = {} + self.LabelLookup = {} + self.LabelLookupCounter = 0 + + -- All functions defined so far + self.Functions = {} + -- Details about the current function that we're building + self.CurFunction = {} + -- All macros defined so far + self.Defines = {} + self.Defines["__LINE__"] = 0 + self.Defines["__FILE__"] = "" + self.IFDEFLevel = {} + self.SkipToEndIf = false + self.EndIfsToSkip = 0 + + -- Output text + self.OutputText = {} + return self.Warnings end @@ -300,7 +300,7 @@ end -- Call this until the function returns false (it returns false when there -- is nothing more to do) function HCOMP:Compile() - return self:UnprotectedCompile() + return self:UnprotectedCompile() -- local status,result = pcall(self.UnprotectedCompile,self) -- if not status then -- print("ERROR: "..result) @@ -314,247 +314,247 @@ end -- Unprotected function that does the actual compiling function HCOMP:UnprotectedCompile() - if self.Stage == 1 then - -- Tokenize stage - -- - -- At this stage sourcecode is converted to list of tokens - - local stageResult = self:Tokenize() - if not stageResult then - -- Output tokens if required - if self.Settings.OutputTokenListing then - for k,v in pairs(self.Tokens) do - self:PrintLine("toklist",k,self.TOKEN_NAME[v.Type],v.Data,v.Position.File.." "..v.Position.Line..":"..v.Position.Col) - end - end - - -- Clean up preprocessor variables - self.Code = nil - self.SourceCode = nil - self.Defines = nil - - -- Go to the first token - self.CurrentToken = 1 - - -- Sest up variables for code parsing - self.CodeTree = {} -- Code tree that will be built (see hc_codetree.lua) - self.GlobalLabels = {} -- Table of globally defined labels - self.LabelCounter = 0 -- Counter for internal labels (for IF, CASE, etc) - self.UserRegisters = {} -- Registers used by user in global scope - self.BlockDepth = 0 -- Nesting depth of the {..} block - self.GlobalStringTable = {} -- Global table for string leaves - - -- Reset parsing the blocks - self.SpecialLeaf = nil - self.LocalLabels = nil - self.StackPointer = nil - self.TokenData = nil - self.StringsTable = nil - self.ParameterPointer = nil - self.BlockType = nil - - -- Set special labels - self:SetSpecialLabels() - - self.Stage = 2 - end - return true - elseif self.Stage == 2 then - -- Parse code stage - -- - -- At this stage code is parsed, and code tree is built - - local stageResult = self:Statement() - if not stageResult then - -- Index for code tree leaves - self.CurrentLeafIndex = 1 - - -- Create storage for generated code - self.GeneratedCode = {} - self.Stage = 3 - end - return true - elseif self.Stage == 3 then - -- Generate stage - -- - -- This will generate code based on the code tree that was created by the - -- parser - - local stageResult = false - if self.CodeTree[self.CurrentLeafIndex] and self:StageGenerateLeaf(self.CodeTree[self.CurrentLeafIndex]) then - stageResult = true - end - - if not stageResult then - self.CurrentLeafIndex = self.CurrentLeafIndex + 1 - if not self.CodeTree[self.CurrentLeafIndex] then - self.Stage = 4 - end - end - return true - elseif self.Stage == 4 then - -- Code optimize stage - -- - -- At this stage code is optimized for the known patterns - - local stageResult - - -- Do not perform this stage if optimization is set to 0 - if self.Settings.OptimizeLevel == 0 then - stageResult = false - else - stageResult = self:OptimizeCode() - end - if not stageResult then - -- Initialize iteration through generated code - self.CurrentBlockIndex = 1 - self.Stage = 5 - - -- Set write pointers - self.PointerOffset = 0 - self.WritePointer = 0 - self.DataPointer = 0 - end - return true - elseif self.Stage == 5 then - -- Resolve stage - -- - -- This will attempt to output the code without actually writing it. - -- All the labels will be resolved at this stage - - local stageResult = false - if self.GeneratedCode[self.CurrentBlockIndex] and self:Resolve(self.GeneratedCode[self.CurrentBlockIndex]) then - stageResult = true - end - if not stageResult then - self.CurrentBlockIndex = self.CurrentBlockIndex + 1 - if not self.GeneratedCode[self.CurrentBlockIndex] then - -- Set special labels - self:SetSpecialLabels() - - -- Initialize iteration through generated code - self.CurrentBlockIndex = 1 - self.Stage = 6 - - -- Set write pointers - self.PointerOffset = 0 - self.WritePointer = 0 - self.DataPointer = 0 - end - end - - return true - elseif self.Stage == 6 then - -- Output stage - -- - -- The code will be output as binary at this stage - - local stageResult = false - if self.GeneratedCode[self.CurrentBlockIndex] and self:Output(self.GeneratedCode[self.CurrentBlockIndex]) then - stageResult = true - end - if not stageResult then - self.CurrentBlockIndex = self.CurrentBlockIndex + 1 - if not self.GeneratedCode[self.CurrentBlockIndex] then - self.Stage = 7 - - -- Generate labels for the debugger - for labelName,labelData in pairs(self.GlobalLabels) do - if string.sub(labelName,1,2) ~= "__" then - if labelData.DebugAsVector then - self.DebugInfo.Labels[string.upper(labelData.Name)] = { - Vector = labelData.Value, - Size = labelData.DebugAsVector -- vector size - } - elseif (labelData.Type == "Variable") or (labelData.DebugAsVariable) then - self.DebugInfo.Labels[string.upper(labelData.Name)] = { - Offset = labelData.Value - } - elseif labelData.Type == "Pointer" then - self.DebugInfo.Labels[string.upper(labelData.Name)] = { - Pointer = labelData.Value - } - end - end - end - - -- Write binary output - if self.Settings.OutputBinaryListing then - if self.CurrentBinaryListingLine then - self:PrintLine("binlist",string.sub(self.CurrentBinaryListingLine,1,#self.CurrentBinaryListingLine-1)) - self.CurrentBinaryListingLine = nil - end - end - - -- Write the debug data - if self.Settings.OutputDebugListing then - self:PrintLine("dbglist","Labels:") - self:PrintLine("dbglist","Name","Offset","Type") - for k,v in pairs(self.DebugInfo.Labels) do - if v.Offset then self:PrintLine("dbglist",k,v.Offset,"MEMORY") - elseif v.Pointer then self:PrintLine("dbglist",k,v.Pointer,"POINTER") - elseif v.StackOffset then self:PrintLine("dbglist",k,v.StackOffset,"STACK") - end - end - - self:PrintLine("dbglist","Position by pointer:") - self:PrintLine("dbglist","Pointer","Line","Column","File") - for k,v in pairs(self.DebugInfo.PositionByPointer) do - self:PrintLine("dbglist",k,v.Line,v.Col,v.File) - end - - self:PrintLine("dbglist","Pointers by line:") - self:PrintLine("dbglist","Line","Start","End") - for k,v in pairs(self.DebugInfo.PointersByLine) do - self:PrintLine("dbglist",k,v[1],v[2]) - end - end - - -- Write header file for library - if self.Settings.GenerateLibrary then - if self.DBString then - self:PrintLine("lib",self.DBString) - self.DBString = nil - end - - self:PrintLine("lib","") - - for symName,symData in pairs(self.ExportedSymbols) do - local printText = "#pragma export " - if symData.FunctionName then - printText = printText .. string.lower(self.TOKEN_TEXT["TYPE"][2][symData.ReturnType]) - printText = printText .. string.rep("*",symData.ReturnPtrLevel) - - printText = printText .. " " .. symData.FunctionName .. "(" - for varIdx,varData in pairs(symData.Parameters) do - printText = printText .. string.lower(self.TOKEN_TEXT["TYPE"][2][varData.Type]) - printText = printText .. string.rep("*",varData.PtrLevel) - printText = printText .. " " .. varData.Name - if varIdx < #symData.Parameters then - printText = printText .. ", " - end - end - printText = printText .. ")" - end - self:PrintLine("lib",printText) - end - end - - -- Clean up - self.LabelLookup = nil - self.LabelLookupCounter = nil - - -- Close all output files - for k,v in pairs(self.OutputText) do self:SaveFile(self.WorkingDir..k..".txt",v) end - end - end - - return true - else - -- Reset compiler to start point and return false when work is done - self.Stage = 1 - return false - end + if self.Stage == 1 then + -- Tokenize stage + -- + -- At this stage sourcecode is converted to list of tokens + + local stageResult = self:Tokenize() + if not stageResult then + -- Output tokens if required + if self.Settings.OutputTokenListing then + for k,v in pairs(self.Tokens) do + self:PrintLine("toklist",k,self.TOKEN_NAME[v.Type],v.Data,v.Position.File.." "..v.Position.Line..":"..v.Position.Col) + end + end + + -- Clean up preprocessor variables + self.Code = nil + self.SourceCode = nil + self.Defines = nil + + -- Go to the first token + self.CurrentToken = 1 + + -- Sest up variables for code parsing + self.CodeTree = {} -- Code tree that will be built (see hc_codetree.lua) + self.GlobalLabels = {} -- Table of globally defined labels + self.LabelCounter = 0 -- Counter for internal labels (for IF, CASE, etc) + self.UserRegisters = {} -- Registers used by user in global scope + self.BlockDepth = 0 -- Nesting depth of the {..} block + self.GlobalStringTable = {} -- Global table for string leaves + + -- Reset parsing the blocks + self.SpecialLeaf = nil + self.LocalLabels = nil + self.StackPointer = nil + self.TokenData = nil + self.StringsTable = nil + self.ParameterPointer = nil + self.BlockType = nil + + -- Set special labels + self:SetSpecialLabels() + + self.Stage = 2 + end + return true + elseif self.Stage == 2 then + -- Parse code stage + -- + -- At this stage code is parsed, and code tree is built + + local stageResult = self:Statement() + if not stageResult then + -- Index for code tree leaves + self.CurrentLeafIndex = 1 + + -- Create storage for generated code + self.GeneratedCode = {} + self.Stage = 3 + end + return true + elseif self.Stage == 3 then + -- Generate stage + -- + -- This will generate code based on the code tree that was created by the + -- parser + + local stageResult = false + if self.CodeTree[self.CurrentLeafIndex] and self:StageGenerateLeaf(self.CodeTree[self.CurrentLeafIndex]) then + stageResult = true + end + + if not stageResult then + self.CurrentLeafIndex = self.CurrentLeafIndex + 1 + if not self.CodeTree[self.CurrentLeafIndex] then + self.Stage = 4 + end + end + return true + elseif self.Stage == 4 then + -- Code optimize stage + -- + -- At this stage code is optimized for the known patterns + + local stageResult + + -- Do not perform this stage if optimization is set to 0 + if self.Settings.OptimizeLevel == 0 then + stageResult = false + else + stageResult = self:OptimizeCode() + end + if not stageResult then + -- Initialize iteration through generated code + self.CurrentBlockIndex = 1 + self.Stage = 5 + + -- Set write pointers + self.PointerOffset = 0 + self.WritePointer = 0 + self.DataPointer = 0 + end + return true + elseif self.Stage == 5 then + -- Resolve stage + -- + -- This will attempt to output the code without actually writing it. + -- All the labels will be resolved at this stage + + local stageResult = false + if self.GeneratedCode[self.CurrentBlockIndex] and self:Resolve(self.GeneratedCode[self.CurrentBlockIndex]) then + stageResult = true + end + if not stageResult then + self.CurrentBlockIndex = self.CurrentBlockIndex + 1 + if not self.GeneratedCode[self.CurrentBlockIndex] then + -- Set special labels + self:SetSpecialLabels() + + -- Initialize iteration through generated code + self.CurrentBlockIndex = 1 + self.Stage = 6 + + -- Set write pointers + self.PointerOffset = 0 + self.WritePointer = 0 + self.DataPointer = 0 + end + end + + return true + elseif self.Stage == 6 then + -- Output stage + -- + -- The code will be output as binary at this stage + + local stageResult = false + if self.GeneratedCode[self.CurrentBlockIndex] and self:Output(self.GeneratedCode[self.CurrentBlockIndex]) then + stageResult = true + end + if not stageResult then + self.CurrentBlockIndex = self.CurrentBlockIndex + 1 + if not self.GeneratedCode[self.CurrentBlockIndex] then + self.Stage = 7 + + -- Generate labels for the debugger + for labelName,labelData in pairs(self.GlobalLabels) do + if string.sub(labelName,1,2) ~= "__" then + if labelData.DebugAsVector then + self.DebugInfo.Labels[string.upper(labelData.Name)] = { + Vector = labelData.Value, + Size = labelData.DebugAsVector -- vector size + } + elseif (labelData.Type == "Variable") or (labelData.DebugAsVariable) then + self.DebugInfo.Labels[string.upper(labelData.Name)] = { + Offset = labelData.Value + } + elseif labelData.Type == "Pointer" then + self.DebugInfo.Labels[string.upper(labelData.Name)] = { + Pointer = labelData.Value + } + end + end + end + + -- Write binary output + if self.Settings.OutputBinaryListing then + if self.CurrentBinaryListingLine then + self:PrintLine("binlist",string.sub(self.CurrentBinaryListingLine,1,#self.CurrentBinaryListingLine-1)) + self.CurrentBinaryListingLine = nil + end + end + + -- Write the debug data + if self.Settings.OutputDebugListing then + self:PrintLine("dbglist","Labels:") + self:PrintLine("dbglist","Name","Offset","Type") + for k,v in pairs(self.DebugInfo.Labels) do + if v.Offset then self:PrintLine("dbglist",k,v.Offset,"MEMORY") + elseif v.Pointer then self:PrintLine("dbglist",k,v.Pointer,"POINTER") + elseif v.StackOffset then self:PrintLine("dbglist",k,v.StackOffset,"STACK") + end + end + + self:PrintLine("dbglist","Position by pointer:") + self:PrintLine("dbglist","Pointer","Line","Column","File") + for k,v in pairs(self.DebugInfo.PositionByPointer) do + self:PrintLine("dbglist",k,v.Line,v.Col,v.File) + end + + self:PrintLine("dbglist","Pointers by line:") + self:PrintLine("dbglist","Line","Start","End") + for k,v in pairs(self.DebugInfo.PointersByLine) do + self:PrintLine("dbglist",k,v[1],v[2]) + end + end + + -- Write header file for library + if self.Settings.GenerateLibrary then + if self.DBString then + self:PrintLine("lib",self.DBString) + self.DBString = nil + end + + self:PrintLine("lib","") + + for symName,symData in pairs(self.ExportedSymbols) do + local printText = "#pragma export " + if symData.FunctionName then + printText = printText .. string.lower(self.TOKEN_TEXT["TYPE"][2][symData.ReturnType]) + printText = printText .. string.rep("*",symData.ReturnPtrLevel) + + printText = printText .. " " .. symData.FunctionName .. "(" + for varIdx,varData in pairs(symData.Parameters) do + printText = printText .. string.lower(self.TOKEN_TEXT["TYPE"][2][varData.Type]) + printText = printText .. string.rep("*",varData.PtrLevel) + printText = printText .. " " .. varData.Name + if varIdx < #symData.Parameters then + printText = printText .. ", " + end + end + printText = printText .. ")" + end + self:PrintLine("lib",printText) + end + end + + -- Clean up + self.LabelLookup = nil + self.LabelLookupCounter = nil + + -- Close all output files + for k,v in pairs(self.OutputText) do self:SaveFile(self.WorkingDir..k..".txt",v) end + end + end + + return true + else + -- Reset compiler to start point and return false when work is done + self.Stage = 1 + return false + end end @@ -564,79 +564,79 @@ end -- Get label (local or global one). Second result returns true if label is new -- Third result returns if label was referenced before function HCOMP:GetLabel(name,declareLocalVariable) - local trueName = string.upper(name) - - -- Should we treat unknown variables as local label definition - -- This assumes self.LocalLabel is defined at this point - if declareLocalVariable then - if self.LocalLabels[trueName] then - return self.LocalLabels[trueName],not self.LocalLabels[trueName].Defined - else - self.LocalLabels[trueName] = { - Type = "Unknown", - Name = name, - Position = self:CurrentSourcePosition(), - } - return self.LocalLabels[trueName],true - end - else - -- If in local mode then try to resolve label amongst the local ones first - if self.LocalLabels and self.LocalLabels[trueName] then - return self.LocalLabels[trueName],not self.LocalLabels[trueName].Defined - elseif self.GlobalLabels[trueName] then - local wasReferenced = self.GlobalLabels[trueName].Referenced - self.GlobalLabels[trueName].Referenced = true - return self.GlobalLabels[trueName],not self.GlobalLabels[trueName].Defined,wasReferenced - else - self.GlobalLabels[trueName] = { - Type = "Unknown", - Name = name, - Position = self:CurrentSourcePosition(), - Referenced = true, - } - return self.GlobalLabels[trueName],true,false - end - end + local trueName = string.upper(name) + + -- Should we treat unknown variables as local label definition + -- This assumes self.LocalLabel is defined at this point + if declareLocalVariable then + if self.LocalLabels[trueName] then + return self.LocalLabels[trueName],not self.LocalLabels[trueName].Defined + else + self.LocalLabels[trueName] = { + Type = "Unknown", + Name = name, + Position = self:CurrentSourcePosition(), + } + return self.LocalLabels[trueName],true + end + else + -- If in local mode then try to resolve label amongst the local ones first + if self.LocalLabels and self.LocalLabels[trueName] then + return self.LocalLabels[trueName],not self.LocalLabels[trueName].Defined + elseif self.GlobalLabels[trueName] then + local wasReferenced = self.GlobalLabels[trueName].Referenced + self.GlobalLabels[trueName].Referenced = true + return self.GlobalLabels[trueName],not self.GlobalLabels[trueName].Defined,wasReferenced + else + self.GlobalLabels[trueName] = { + Type = "Unknown", + Name = name, + Position = self:CurrentSourcePosition(), + Referenced = true, + } + return self.GlobalLabels[trueName],true,false + end + end end -- Define a new label function HCOMP:DefineLabel(name,declareLocalVariable) - local label,isNew,wasReferenced = self:GetLabel(name,declareLocalVariable) - if not isNew then -- - if label.Position then - self:Error("Variable redefined: \""..name.."\", previously defined at ".. - self:formatPrefix(label.Position.Line,label.Position.Col,label.Position.File)) - else - self:Error("Variable redefined: \""..name.."\"") - end - end - - -- Clear referenced flag if required - label.Referenced = false or wasReferenced - return label + local label,isNew,wasReferenced = self:GetLabel(name,declareLocalVariable) + if not isNew then -- + if label.Position then + self:Error("Variable redefined: \""..name.."\", previously defined at ".. + self:formatPrefix(label.Position.Line,label.Position.Col,label.Position.File)) + else + self:Error("Variable redefined: \""..name.."\"") + end + end + + -- Clear referenced flag if required + label.Referenced = false or wasReferenced + return label end -- Redefine label under new name function HCOMP:RedefineLabel(oldName,newName) - local label = self:GetLabel(oldName) - self.GlobalLabels[string.upper(label.Name)] = nil - - label.Name = newName - local prevLabel = self.GlobalLabels[string.upper(label.Name)] - if prevLabel then - if prevLabel.Position then - self:Error("Variable redefined: \""..newName.."\", previously defined at ".. - self:formatPrefix(prevLabel.Position.Line,prevLabel.Position.Col,prevLabel.Position.File)) - else - self:Error("Variable redefined: \""..newName.."\"") - end - else - self.GlobalLabels[string.upper(label.Name)] = label - end + local label = self:GetLabel(oldName) + self.GlobalLabels[string.upper(label.Name)] = nil + + label.Name = newName + local prevLabel = self.GlobalLabels[string.upper(label.Name)] + if prevLabel then + if prevLabel.Position then + self:Error("Variable redefined: \""..newName.."\", previously defined at ".. + self:formatPrefix(prevLabel.Position.Line,prevLabel.Position.Col,prevLabel.Position.File)) + else + self:Error("Variable redefined: \""..newName.."\"") + end + else + self.GlobalLabels[string.upper(label.Name)] = label + end end @@ -644,14 +644,14 @@ end -- Get a new temporary/internal label for use in complex language structures function HCOMP:GetTempLabel() - local labelName = "__"..self.LabelCounter - self.GlobalLabels[labelName] = { - Type = "Unknown", - Name = labelName, - } - - self.LabelCounter = self.LabelCounter + 1 - return self.GlobalLabels[labelName] + local labelName = "__"..self.LabelCounter + self.GlobalLabels[labelName] = { + Type = "Unknown", + Name = labelName, + } + + self.LabelCounter = self.LabelCounter + 1 + return self.GlobalLabels[labelName] end @@ -659,9 +659,9 @@ end -- Set a label to specific value (used for special labels) function HCOMP:SetLabel(name,value) - local label = self:GetLabel(name) - label.Type = "Pointer" - label.Value = value + local label = self:GetLabel(name) + label.Type = "Pointer" + label.Value = value end @@ -669,86 +669,86 @@ end -- Set special labels function HCOMP:SetSpecialLabels() - -- Set special labels - self:DefineLabel("__PTR__").Type = "Pointer" - self:SetLabel("programsize",self.WritePointer) - self:SetLabel("__PROGRAMSIZE__",self.WritePointer) - self:SetLabel("__DATE_YEAR__", tonumber(os.date("%Y"))) - self:SetLabel("__DATE_MONTH__", tonumber(os.date("%m"))) - self:SetLabel("__DATE_DAY__", tonumber(os.date("%d"))) - self:SetLabel("__DATE_HOUR__", tonumber(os.date("%H"))) - self:SetLabel("__DATE_MINUTE__",tonumber(os.date("%M"))) - self:SetLabel("__DATE_SECOND__",tonumber(os.date("%S"))) - - if string.match(self.Settings.CurrentPlatform, "GPU$") then - self:SetLabel("regClk", 65535) - self:SetLabel("regReset", 65534) - self:SetLabel("regHWClear", 65533) - self:SetLabel("regVertexMode", 65532) - self:SetLabel("regHalt", 65531) - self:SetLabel("regRAMReset", 65530) - self:SetLabel("regAsyncReset", 65529) - self:SetLabel("regAsyncClk", 65528) - self:SetLabel("regAsyncFreq", 65527) - self:SetLabel("regIndex", 65526) - - self:SetLabel("regHScale", 65525) - self:SetLabel("regVScale", 65524) - self:SetLabel("regHWScale", 65523) - self:SetLabel("regRotation", 65522) - self:SetLabel("regTexSize", 65521) - self:SetLabel("regTexDataPtr", 65520) - self:SetLabel("regTexDataSz", 65519) - self:SetLabel("regRasterQ", 65518) - self:SetLabel("regTexBuffer", 65517) - - - self:SetLabel("regWidth", 65515) - self:SetLabel("regHeight", 65514) - self:SetLabel("regRatio", 65513) - self:SetLabel("regParamList", 65512) - - self:SetLabel("regCursorX", 65505) - self:SetLabel("regCursorY", 65504) - self:SetLabel("regCursor", 65503) - self:SetLabel("regCursorButtons", 65502) - - self:SetLabel("regBrightnessW", 65495) - self:SetLabel("regBrightnessR", 65494) - self:SetLabel("regBrightnessG", 65493) - self:SetLabel("regBrightnessB", 65492) - self:SetLabel("regContrastW", 65491) - self:SetLabel("regContrastR", 65490) - self:SetLabel("regContrastG", 65489) - self:SetLabel("regContrastB", 65488) - - self:SetLabel("regCircleQuality", 65485) - self:SetLabel("regOffsetX", 65484) - self:SetLabel("regOffsetY", 65483) - self:SetLabel("regRotation", 65482) - self:SetLabel("regScale", 65481) - self:SetLabel("regCenterX", 65480) - self:SetLabel("regCenterY", 65479) - self:SetLabel("regCircleStart", 65478) - self:SetLabel("regCircleEnd", 65477) - self:SetLabel("regLineWidth", 65476) - self:SetLabel("regScaleX", 65475) - self:SetLabel("regScaleY", 65474) - self:SetLabel("regFontAlign", 65473) - self:SetLabel("regFontHalign", 65473) - self:SetLabel("regZOffset", 65472) - self:SetLabel("regFontValign", 65471) - self:SetLabel("regCullDistance", 65470) - self:SetLabel("regCullMode", 65469) - self:SetLabel("regLightMode", 65468) - self:SetLabel("regVertexArray", 65467) - self:SetLabel("regTexRotation", 65466) - self:SetLabel("regTexScale", 65465) - self:SetLabel("regTexCenterU", 65464) - self:SetLabel("regTexCenterV", 65463) - self:SetLabel("regTexOffsetU", 65462) - self:SetLabel("regTexOffsetV", 65461) - end + -- Set special labels + self:DefineLabel("__PTR__").Type = "Pointer" + self:SetLabel("programsize",self.WritePointer) + self:SetLabel("__PROGRAMSIZE__",self.WritePointer) + self:SetLabel("__DATE_YEAR__", tonumber(os.date("%Y"))) + self:SetLabel("__DATE_MONTH__", tonumber(os.date("%m"))) + self:SetLabel("__DATE_DAY__", tonumber(os.date("%d"))) + self:SetLabel("__DATE_HOUR__", tonumber(os.date("%H"))) + self:SetLabel("__DATE_MINUTE__",tonumber(os.date("%M"))) + self:SetLabel("__DATE_SECOND__",tonumber(os.date("%S"))) + + if string.match(self.Settings.CurrentPlatform, "GPU$") then + self:SetLabel("regClk", 65535) + self:SetLabel("regReset", 65534) + self:SetLabel("regHWClear", 65533) + self:SetLabel("regVertexMode", 65532) + self:SetLabel("regHalt", 65531) + self:SetLabel("regRAMReset", 65530) + self:SetLabel("regAsyncReset", 65529) + self:SetLabel("regAsyncClk", 65528) + self:SetLabel("regAsyncFreq", 65527) + self:SetLabel("regIndex", 65526) + + self:SetLabel("regHScale", 65525) + self:SetLabel("regVScale", 65524) + self:SetLabel("regHWScale", 65523) + self:SetLabel("regRotation", 65522) + self:SetLabel("regTexSize", 65521) + self:SetLabel("regTexDataPtr", 65520) + self:SetLabel("regTexDataSz", 65519) + self:SetLabel("regRasterQ", 65518) + self:SetLabel("regTexBuffer", 65517) + + + self:SetLabel("regWidth", 65515) + self:SetLabel("regHeight", 65514) + self:SetLabel("regRatio", 65513) + self:SetLabel("regParamList", 65512) + + self:SetLabel("regCursorX", 65505) + self:SetLabel("regCursorY", 65504) + self:SetLabel("regCursor", 65503) + self:SetLabel("regCursorButtons", 65502) + + self:SetLabel("regBrightnessW", 65495) + self:SetLabel("regBrightnessR", 65494) + self:SetLabel("regBrightnessG", 65493) + self:SetLabel("regBrightnessB", 65492) + self:SetLabel("regContrastW", 65491) + self:SetLabel("regContrastR", 65490) + self:SetLabel("regContrastG", 65489) + self:SetLabel("regContrastB", 65488) + + self:SetLabel("regCircleQuality", 65485) + self:SetLabel("regOffsetX", 65484) + self:SetLabel("regOffsetY", 65483) + self:SetLabel("regRotation", 65482) + self:SetLabel("regScale", 65481) + self:SetLabel("regCenterX", 65480) + self:SetLabel("regCenterY", 65479) + self:SetLabel("regCircleStart", 65478) + self:SetLabel("regCircleEnd", 65477) + self:SetLabel("regLineWidth", 65476) + self:SetLabel("regScaleX", 65475) + self:SetLabel("regScaleY", 65474) + self:SetLabel("regFontAlign", 65473) + self:SetLabel("regFontHalign", 65473) + self:SetLabel("regZOffset", 65472) + self:SetLabel("regFontValign", 65471) + self:SetLabel("regCullDistance", 65470) + self:SetLabel("regCullMode", 65469) + self:SetLabel("regLightMode", 65468) + self:SetLabel("regVertexArray", 65467) + self:SetLabel("regTexRotation", 65466) + self:SetLabel("regTexScale", 65465) + self:SetLabel("regTexCenterU", 65464) + self:SetLabel("regTexCenterV", 65463) + self:SetLabel("regTexOffsetU", 65462) + self:SetLabel("regTexOffsetV", 65461) + end end @@ -757,32 +757,32 @@ end -------------------------------------------------------------------------------- -- Converts integer to binary representation function HCOMP:IntegerToBinary(n) - -- Check sign - n = math.floor(n or 0) - if n < 0 then - local bits = self:IntegerToBinary(2^48 + n) - bits[48-1] = 1 - return bits - end - - -- Convert to binary - local bits = {} - local cnt = 0 - while (n > 0) and (cnt < 48) do - local bit = n % 2 - bits[cnt] = bit - - n = (n-bit)/2 - cnt = cnt + 1 - end - - -- Fill in missing zero bits - while cnt < 48 do - bits[cnt] = 0 - cnt = cnt + 1 - end - - return bits + -- Check sign + n = math.floor(n or 0) + if n < 0 then + local bits = self:IntegerToBinary(2^48 + n) + bits[48-1] = 1 + return bits + end + + -- Convert to binary + local bits = {} + local cnt = 0 + while (n > 0) and (cnt < 48) do + local bit = n % 2 + bits[cnt] = bit + + n = (n-bit)/2 + cnt = cnt + 1 + end + + -- Fill in missing zero bits + while cnt < 48 do + bits[cnt] = 0 + cnt = cnt + 1 + end + + return bits end @@ -791,20 +791,20 @@ end -------------------------------------------------------------------------------- -- Converts binary representation back to integer function HCOMP:BinaryToInteger(bits) - local n = #bits - local result = 0 - - -- Convert to integer - for i = 0, 48-2 do - result = result + (bits[i] or 0) * (2 ^ i) - end - - -- Add sign - if bits[48-1] == 1 then - return -2^(48-1)+result - else - return result - end + local n = #bits + local result = 0 + + -- Convert to integer + for i = 0, 48-2 do + result = result + (bits[i] or 0) * (2 ^ i) + end + + -- Add sign + if bits[48-1] == 1 then + return -2^(48-1)+result + else + return result + end end @@ -813,15 +813,15 @@ end -------------------------------------------------------------------------------- -- Binary OR function HCOMP:BinaryOr(m,n) - local bits_m = self:IntegerToBinary(m) - local bits_n = self:IntegerToBinary(n) - local bits = {} + local bits_m = self:IntegerToBinary(m) + local bits_n = self:IntegerToBinary(n) + local bits = {} - for i = 0, 48-1 do - bits[i] = math.min(1,bits_m[i]+bits_n[i]) - end + for i = 0, 48-1 do + bits[i] = math.min(1,bits_m[i]+bits_n[i]) + end - return self:BinaryToInteger(bits) + return self:BinaryToInteger(bits) end @@ -830,15 +830,15 @@ end -------------------------------------------------------------------------------- -- Binary AND function HCOMP:BinaryAnd(m,n) - local bits_m = self:IntegerToBinary(m) - local bits_n = self:IntegerToBinary(n) - local bits = {} + local bits_m = self:IntegerToBinary(m) + local bits_n = self:IntegerToBinary(n) + local bits = {} - for i = 0, 48-1 do - bits[i] = bits_m[i]*bits_n[i] - end + for i = 0, 48-1 do + bits[i] = bits_m[i]*bits_n[i] + end - return self:BinaryToInteger(bits) + return self:BinaryToInteger(bits) end @@ -847,13 +847,13 @@ end -------------------------------------------------------------------------------- -- Binary NOT function HCOMP:BinaryNot(n) - local bits_n = self:IntegerToBinary(n) - local bits = {} + local bits_n = self:IntegerToBinary(n) + local bits = {} - for i = 0, 48-1 do - bits[i] = 1-bits_n[i] - end - return self:BinaryToInteger(bits) + for i = 0, 48-1 do + bits[i] = 1-bits_n[i] + end + return self:BinaryToInteger(bits) end @@ -862,15 +862,15 @@ end -------------------------------------------------------------------------------- -- Binary XOR function HCOMP:BinaryXor(m,n) - local bits_m = self:IntegerToBinary(m) - local bits_n = self:IntegerToBinary(n) - local bits = {} + local bits_m = self:IntegerToBinary(m) + local bits_n = self:IntegerToBinary(n) + local bits = {} - for i = 0, 48-1 do - bits[i] = (bits_m[i]+bits_n[i]) % 2 - end + for i = 0, 48-1 do + bits[i] = (bits_m[i]+bits_n[i]) % 2 + end - return self:BinaryToInteger(bits) + return self:BinaryToInteger(bits) end @@ -879,18 +879,18 @@ end -------------------------------------------------------------------------------- -- Binary shift right function HCOMP:BinarySHR(n,cnt) - local bits_n = self:IntegerToBinary(n) - local bits = {} - - local rslt = #bits_n - for i = 0, 48-cnt-1 do - bits[i] = bits_n[i+cnt] - end - for i = 48-cnt,rslt-1 do - bits[i] = 0 - end - - return self:BinaryToInteger(bits) + local bits_n = self:IntegerToBinary(n) + local bits = {} + + local rslt = #bits_n + for i = 0, 48-cnt-1 do + bits[i] = bits_n[i+cnt] + end + for i = 48-cnt,rslt-1 do + bits[i] = 0 + end + + return self:BinaryToInteger(bits) end @@ -899,15 +899,15 @@ end -------------------------------------------------------------------------------- -- Binary shift left function HCOMP:BinarySHL(n,cnt) - local bits_n = self:IntegerToBinary(n) - local bits = {} + local bits_n = self:IntegerToBinary(n) + local bits = {} - for i = cnt,48-1 do - bits[i] = bits_n[i-cnt] - end - for i = 0,cnt-1 do - bits[i] = 0 - end + for i = cnt,48-1 do + bits[i] = bits_n[i-cnt] + end + for i = 0,cnt-1 do + bits[i] = 0 + end - return self:BinaryToInteger(bits) + return self:BinaryToInteger(bits) end diff --git a/lua/wire/client/hlzasm/hc_expression.lua b/lua/wire/client/hlzasm/hc_expression.lua index cc05da1d03..c655d9320f 100644 --- a/lua/wire/client/hlzasm/hc_expression.lua +++ b/lua/wire/client/hlzasm/hc_expression.lua @@ -11,722 +11,722 @@ -- Returns leaf for an expression on specific level -- This will also test whether the expression is constant or not function HCOMP:Expression_LevelLeaf(level) - local levelLeaf - local levelConst,levelValue,levelExpr = self:ConstantExpression(false,level) - - if levelConst then - if levelExpr - then levelLeaf = levelExpr -- Expression that has to be recalculated later - else levelLeaf = levelValue -- Numeric value - end - - return { Constant = levelLeaf, CurrentPosition = self:CurrentSourcePosition() } - else - return self["Expression_Level"..level](self) - end + local levelLeaf + local levelConst,levelValue,levelExpr = self:ConstantExpression(false,level) + + if levelConst then + if levelExpr + then levelLeaf = levelExpr -- Expression that has to be recalculated later + else levelLeaf = levelValue -- Numeric value + end + + return { Constant = levelLeaf, CurrentPosition = self:CurrentSourcePosition() } + else + return self["Expression_Level"..level](self) + end end -- generate explict increment/decrement opcode function HCOMP:Expression_ExplictIncDec(opcode,label,returnAfter) - local operationLeaf = self:NewLeaf() - operationLeaf.Opcode = opcode - - if tonumber(label) then --returnBefore - operationLeaf.Operands[1] = { Register = label } - elseif not label.Type then - operationLeaf.Operands[1] = label - else - if label.Type == "Variable" then - operationLeaf.Operands[1] = { Memory = label } - elseif label.Type == "Unknown" then - operationLeaf.Operands[1] = { UnknownOperationByLabel = label } - elseif label.Type == "Stack" then - operationLeaf.Operands[1] = { Stack = label.StackOffset } - elseif label.Type == "Register" then - if self.RegisterIdentities[label.Name] == -1 then - self:Error("Attempting to use register variable after zapping it") - end - operationLeaf.Operands[1] = { Register = label.Value } - end - end - - -- Mark this leaf as an explict assign operation - operationLeaf.ExplictAssign = true - operationLeaf.ReturnAfterAssign = returnAfter - - return operationLeaf + local operationLeaf = self:NewLeaf() + operationLeaf.Opcode = opcode + + if tonumber(label) then --returnBefore + operationLeaf.Operands[1] = { Register = label } + elseif not label.Type then + operationLeaf.Operands[1] = label + else + if label.Type == "Variable" then + operationLeaf.Operands[1] = { Memory = label } + elseif label.Type == "Unknown" then + operationLeaf.Operands[1] = { UnknownOperationByLabel = label } + elseif label.Type == "Stack" then + operationLeaf.Operands[1] = { Stack = label.StackOffset } + elseif label.Type == "Register" then + if self.RegisterIdentities[label.Name] == -1 then + self:Error("Attempting to use register variable after zapping it") + end + operationLeaf.Operands[1] = { Register = label.Value } + end + end + + -- Mark this leaf as an explict assign operation + operationLeaf.ExplictAssign = true + operationLeaf.ReturnAfterAssign = returnAfter + + return operationLeaf end -- generate code for function call function HCOMP:Expression_FunctionCall(label) local TOKEN = self.TOKEN - -- Parse arguments and push them to stack - local argumentCount = 0 - local argumentExpression = {} - while not (self:PeekToken() == TOKEN.RPAREN) do - -- Parse argument - argumentExpression[#argumentExpression+1] = self:Expression() - - -- Go to next one - argumentCount = argumentCount + 1 - self:MatchToken(TOKEN.COMMA) - end - self:ExpectToken(TOKEN.RPAREN) - - -- Find the function definition - local functionEntry = self.Functions[label.Name] - - -- All leaves that must be generated previously to knowing correct result - local genLeaves = {} - - -- Push arguments to stack in reverse order - for argNo = #argumentExpression,1,-1 do - local pushLeaf - if argumentExpression[argNo].Memory then - if argumentExpression[argNo].Memory.CopySize then - for i=argumentExpression[argNo].Memory.CopySize-1,0,-1 do - pushLeaf = self:NewLeaf() - pushLeaf.Comment = " passing large variable, byte "..i - pushLeaf.Opcode = "push" - local a = argumentExpression[argNo] - local copiedArg = { - CurrentPosition = argumentExpression[argNo].CurrentPosition, - Memory = a.Memory, - MemAddrOffset = i - } - -- Memory will update globally when it receives the final value - -- so we wrap it into a new copy of the table with a constant num - -- because setting the const in memory would update the same table multiple times - pushLeaf.Operands[1] = copiedArg - table.insert(genLeaves,pushLeaf) - end - argumentCount = argumentCount + argumentExpression[argNo].Memory.CopySize-1 - else - pushLeaf = self:NewLeaf() - pushLeaf.Opcode = "push" - pushLeaf.Operands[1] = argumentExpression[argNo] - table.insert(genLeaves,pushLeaf) - end - else - pushLeaf = self:NewLeaf() - pushLeaf.Opcode = "push" - pushLeaf.Operands[1] = argumentExpression[argNo] - table.insert(genLeaves,pushLeaf) - end - - if functionEntry then - if functionEntry.Parameters[argNo] then - pushLeaf.Comment = label.Name.." arg #"..argNo.." (".. - string.lower( - self.TOKEN_TEXT["TYPE"][2][functionEntry.Parameters[argNo].Type] or - functionEntry.Parameters[argNo].Type).. - string.rep("*",functionEntry.Parameters[argNo].PtrLevel).. - " ".. - functionEntry.Parameters[argNo].Name..")" - else - pushLeaf.Comment = label.Name.." arg #"..argNo.." (unknown)" - end - end - end - - -- Call function - if functionEntry and functionEntry.InlineCode then - for i=1,#functionEntry.InlineCode do + -- Parse arguments and push them to stack + local argumentCount = 0 + local argumentExpression = {} + while not (self:PeekToken() == TOKEN.RPAREN) do + -- Parse argument + argumentExpression[#argumentExpression+1] = self:Expression() + + -- Go to next one + argumentCount = argumentCount + 1 + self:MatchToken(TOKEN.COMMA) + end + self:ExpectToken(TOKEN.RPAREN) + + -- Find the function definition + local functionEntry = self.Functions[label.Name] + + -- All leaves that must be generated previously to knowing correct result + local genLeaves = {} + + -- Push arguments to stack in reverse order + for argNo = #argumentExpression,1,-1 do + local pushLeaf + if argumentExpression[argNo].Memory then + if argumentExpression[argNo].Memory.CopySize then + for i=argumentExpression[argNo].Memory.CopySize-1,0,-1 do + pushLeaf = self:NewLeaf() + pushLeaf.Comment = " passing large variable, byte "..i + pushLeaf.Opcode = "push" + local a = argumentExpression[argNo] + local copiedArg = { + CurrentPosition = argumentExpression[argNo].CurrentPosition, + Memory = a.Memory, + MemAddrOffset = i + } + -- Memory will update globally when it receives the final value + -- so we wrap it into a new copy of the table with a constant num + -- because setting the const in memory would update the same table multiple times + pushLeaf.Operands[1] = copiedArg + table.insert(genLeaves,pushLeaf) + end + argumentCount = argumentCount + argumentExpression[argNo].Memory.CopySize-1 + else + pushLeaf = self:NewLeaf() + pushLeaf.Opcode = "push" + pushLeaf.Operands[1] = argumentExpression[argNo] + table.insert(genLeaves,pushLeaf) + end + else + pushLeaf = self:NewLeaf() + pushLeaf.Opcode = "push" + pushLeaf.Operands[1] = argumentExpression[argNo] + table.insert(genLeaves,pushLeaf) + end + + if functionEntry then + if functionEntry.Parameters[argNo] then + pushLeaf.Comment = label.Name.." arg #"..argNo.." (".. + string.lower( + self.TOKEN_TEXT["TYPE"][2][functionEntry.Parameters[argNo].Type] or + functionEntry.Parameters[argNo].Type).. + string.rep("*",functionEntry.Parameters[argNo].PtrLevel).. + " ".. + functionEntry.Parameters[argNo].Name..")" + else + pushLeaf.Comment = label.Name.." arg #"..argNo.." (unknown)" + end + end + end + + -- Call function + if functionEntry and functionEntry.InlineCode then + for i=1,#functionEntry.InlineCode do -- self:AddLeafToTail(functionEntry.InlineCode[i]) - if functionEntry.InlineCode[i].Opcode ~= "LABEL" then - table.insert(genLeaves,functionEntry.InlineCode[i]) - end - end - else - -- Push argument count to stack - local argCountLeaf = self:NewLeaf() - argCountLeaf.Opcode = "mov" - argCountLeaf.ExplictAssign = true - argCountLeaf.Operands[1] = { Register = 3 } -- ECX is the argument count register - argCountLeaf.Operands[2] = { Constant = argumentCount } - table.insert(genLeaves,argCountLeaf) - - local callLeaf = self:NewLeaf() - callLeaf.Opcode = "call" - callLeaf.Comment = label.Name.."(...)" - if label.Type == "Stack" then - callLeaf.Operands[1] = { Stack = label.StackOffset } - else --{ PointerToLabel = label } - callLeaf.Operands[1] = { UnknownOperationByLabel = label } - end - table.insert(genLeaves,callLeaf) - end - - -- Stack cleanup - if argumentCount > 0 then - local stackCleanupLeaf = self:NewLeaf() - stackCleanupLeaf.Opcode = "add" - stackCleanupLeaf.ExplictAssign = true - stackCleanupLeaf.Operands[1] = { Register = 7 } - stackCleanupLeaf.Operands[2] = { Constant = argumentCount } - table.insert(genLeaves,stackCleanupLeaf) - end - - -- Create correct leaf tree - for i=2,#genLeaves do - genLeaves[i].PreviousLeaf = genLeaves[i-1] - end - - -- Return EAX as the return value - if self.Functions[label] then - if self.StructSize[self.Functions[label].ReturnType] then - -- Return as memory register that requests copy of x size - return { MemoryRegister = 1, ForceTemporary = true, CopySize = self.StructSize[self.Functions[label].ReturnType], PreviousLeaf = genLeaves[#genLeaves]} - end - elseif self.CurFunction then - if self.CurFunction.ReturnType then - if self.StructSize[self.CurFunction.ReturnType] then - return { MemoryRegister = 1, ForceTemporary = true, CopySize = self.StructSize[self.CurFunction.ReturnType], PreviousLeaf = genLeaves[#genLeaves]} - end - end - end - return { Register = 1, ForceTemporary = true, PreviousLeaf = genLeaves[#genLeaves] } + if functionEntry.InlineCode[i].Opcode ~= "LABEL" then + table.insert(genLeaves,functionEntry.InlineCode[i]) + end + end + else + -- Push argument count to stack + local argCountLeaf = self:NewLeaf() + argCountLeaf.Opcode = "mov" + argCountLeaf.ExplictAssign = true + argCountLeaf.Operands[1] = { Register = 3 } -- ECX is the argument count register + argCountLeaf.Operands[2] = { Constant = argumentCount } + table.insert(genLeaves,argCountLeaf) + + local callLeaf = self:NewLeaf() + callLeaf.Opcode = "call" + callLeaf.Comment = label.Name.."(...)" + if label.Type == "Stack" then + callLeaf.Operands[1] = { Stack = label.StackOffset } + else --{ PointerToLabel = label } + callLeaf.Operands[1] = { UnknownOperationByLabel = label } + end + table.insert(genLeaves,callLeaf) + end + + -- Stack cleanup + if argumentCount > 0 then + local stackCleanupLeaf = self:NewLeaf() + stackCleanupLeaf.Opcode = "add" + stackCleanupLeaf.ExplictAssign = true + stackCleanupLeaf.Operands[1] = { Register = 7 } + stackCleanupLeaf.Operands[2] = { Constant = argumentCount } + table.insert(genLeaves,stackCleanupLeaf) + end + + -- Create correct leaf tree + for i=2,#genLeaves do + genLeaves[i].PreviousLeaf = genLeaves[i-1] + end + + -- Return EAX as the return value + if self.Functions[label] then + if self.StructSize[self.Functions[label].ReturnType] then + -- Return as memory register that requests copy of x size + return { MemoryRegister = 1, ForceTemporary = true, CopySize = self.StructSize[self.Functions[label].ReturnType], PreviousLeaf = genLeaves[#genLeaves]} + end + elseif self.CurFunction then + if self.CurFunction.ReturnType then + if self.StructSize[self.CurFunction.ReturnType] then + return { MemoryRegister = 1, ForceTemporary = true, CopySize = self.StructSize[self.CurFunction.ReturnType], PreviousLeaf = genLeaves[#genLeaves]} + end + end + end + return { Register = 1, ForceTemporary = true, PreviousLeaf = genLeaves[#genLeaves] } end -- generate code for array access function HCOMP:Expression_ArrayAccess(label) local TOKEN = self.TOKEN - local operationLeaf - local arrayOffsetLeaf = self:Expression() - self:ExpectToken(TOKEN.RSUBSCR) - - -- Create leaf for calculating address - local addressLeaf = self:NewLeaf() - - if label.Array then -- Parse array access treating label as pointer to array - if label.Type == "Stack" then - if arrayOffsetLeaf.Constant then - addressLeaf = { Constant = label.StackOffset+arrayOffsetLeaf.Constant } - operationLeaf = { Stack = label.StackOffset+arrayOffsetLeaf.Constant } - else - addressLeaf.Opcode = "add" - addressLeaf.Operands[1] = { Constant = label.StackOffset } - addressLeaf.Operands[2] = arrayOffsetLeaf - operationLeaf = { Stack = addressLeaf } - end - else - addressLeaf.Opcode = "add" - addressLeaf.Operands[1] = arrayOffsetLeaf - addressLeaf.Operands[2] = { PointerToLabel = label } - operationLeaf = { MemoryPointer = addressLeaf } - end - else -- Parse array access treating variable as pointer - if label.Type == "Stack" then - addressLeaf.Opcode = "add" - addressLeaf.Operands[1] = { Stack = label.StackOffset } - addressLeaf.Operands[2] = arrayOffsetLeaf - operationLeaf = { MemoryPointer = addressLeaf } - elseif label.Type == "Variable" then - addressLeaf.Opcode = "add" - addressLeaf.Operands[1] = arrayOffsetLeaf - addressLeaf.Operands[2] = { Memory = label } - operationLeaf = { MemoryPointer = addressLeaf } - elseif label.Type == "Pointer" then - addressLeaf.Opcode = "add" - addressLeaf.Operands[1] = arrayOffsetLeaf - addressLeaf.Operands[2] = { Constant = {{ Type = TOKEN.IDENT, Data = label.Name, Position = self:CurrentSourcePosition() }} } - operationLeaf = { MemoryPointer = addressLeaf } - elseif label.Type == "Register" then - if self.RegisterIdentities[label.Name] == -1 then - self:Error("Attempting to use register variable after zapping it") - end - addressLeaf.Opcode = "add" - addressLeaf.Operands[1] = arrayOffsetLeaf - addressLeaf.Operands[2] = { Register = label.Value } - operationLeaf = { MemoryPointer = addressLeaf } - else - addressLeaf.Opcode = "add" - addressLeaf.Operands[1] = arrayOffsetLeaf - addressLeaf.Operands[2] = { UnknownOperationByLabel = label } - operationLeaf = { MemoryPointer = addressLeaf } - end - end - - if self:MatchToken(TOKEN.INC) then -- reg++ - operationLeaf = self:Expression_ExplictIncDec("inc",operationLeaf) - elseif self:MatchToken(TOKEN.DEC) then -- reg-- - operationLeaf = self:Expression_ExplictIncDec("dec",operationLeaf) - end - - return operationLeaf,addressLeaf + local operationLeaf + local arrayOffsetLeaf = self:Expression() + self:ExpectToken(TOKEN.RSUBSCR) + + -- Create leaf for calculating address + local addressLeaf = self:NewLeaf() + + if label.Array then -- Parse array access treating label as pointer to array + if label.Type == "Stack" then + if arrayOffsetLeaf.Constant then + addressLeaf = { Constant = label.StackOffset+arrayOffsetLeaf.Constant } + operationLeaf = { Stack = label.StackOffset+arrayOffsetLeaf.Constant } + else + addressLeaf.Opcode = "add" + addressLeaf.Operands[1] = { Constant = label.StackOffset } + addressLeaf.Operands[2] = arrayOffsetLeaf + operationLeaf = { Stack = addressLeaf } + end + else + addressLeaf.Opcode = "add" + addressLeaf.Operands[1] = arrayOffsetLeaf + addressLeaf.Operands[2] = { PointerToLabel = label } + operationLeaf = { MemoryPointer = addressLeaf } + end + else -- Parse array access treating variable as pointer + if label.Type == "Stack" then + addressLeaf.Opcode = "add" + addressLeaf.Operands[1] = { Stack = label.StackOffset } + addressLeaf.Operands[2] = arrayOffsetLeaf + operationLeaf = { MemoryPointer = addressLeaf } + elseif label.Type == "Variable" then + addressLeaf.Opcode = "add" + addressLeaf.Operands[1] = arrayOffsetLeaf + addressLeaf.Operands[2] = { Memory = label } + operationLeaf = { MemoryPointer = addressLeaf } + elseif label.Type == "Pointer" then + addressLeaf.Opcode = "add" + addressLeaf.Operands[1] = arrayOffsetLeaf + addressLeaf.Operands[2] = { Constant = {{ Type = TOKEN.IDENT, Data = label.Name, Position = self:CurrentSourcePosition() }} } + operationLeaf = { MemoryPointer = addressLeaf } + elseif label.Type == "Register" then + if self.RegisterIdentities[label.Name] == -1 then + self:Error("Attempting to use register variable after zapping it") + end + addressLeaf.Opcode = "add" + addressLeaf.Operands[1] = arrayOffsetLeaf + addressLeaf.Operands[2] = { Register = label.Value } + operationLeaf = { MemoryPointer = addressLeaf } + else + addressLeaf.Opcode = "add" + addressLeaf.Operands[1] = arrayOffsetLeaf + addressLeaf.Operands[2] = { UnknownOperationByLabel = label } + operationLeaf = { MemoryPointer = addressLeaf } + end + end + + if self:MatchToken(TOKEN.INC) then -- reg++ + operationLeaf = self:Expression_ExplictIncDec("inc",operationLeaf) + elseif self:MatchToken(TOKEN.DEC) then -- reg-- + operationLeaf = self:Expression_ExplictIncDec("dec",operationLeaf) + end + + return operationLeaf,addressLeaf end -- level3: () or function HCOMP:Expression_Level3() local TOKEN = self.TOKEN - local negateLeaf,operationLeaf - - - -- Negate value if required - if self:MatchToken(TOKEN.MINUS) then -- "-" - negateLeaf = self:NewLeaf() - negateLeaf.Opcode = "neg" - end - -- Logically negate value if required - if self:MatchToken(TOKEN.NOT) then -- "!" - negateLeaf = self:NewLeaf() - negateLeaf.Opcode = "lneg" - end - - - if self:MatchToken(TOKEN.AND) then -- Parse retrieve pointer operation (&var) - if self:MatchToken(TOKEN.IDENT) then - local label = self:GetLabel(self.TokenData) - - if self:MatchToken(TOKEN.LSUBSCR) then -- Parse array access - local _,addressLeaf = self:Expression_ArrayAccess(label) - if label.Type == "Stack" then - operationLeaf = self:NewLeaf() - operationLeaf.Opcode = "add" - operationLeaf.Operands[1] = { Register = 7, Segment = 2 } -- EBP:SS - operationLeaf.Operands[2] = addressLeaf - else - operationLeaf = addressLeaf - end - else - if label.Type == "Stack" then - if self:MatchToken(TOKEN.LSUBSCR) then -- Pointer to element of an array on stack - local arrayOffsetLeaf = self:Expression() - self:ExpectToken(TOKEN.RSUBSCR) - - -- Create leaf for calculating address - local addressLeaf - if arrayOffsetLeaf.Constant then - addressLeaf = { Constant = label.StackOffset+arrayOffsetLeaf.Constant } - else - addressLeaf = self:NewLeaf() - addressLeaf.Opcode = "add" - addressLeaf.Operands[1] = { Constant = label.StackOffset } - addressLeaf.Operands[2] = arrayOffsetLeaf - end - - -- Create leaf that returns pointer to stack - operationLeaf = self:NewLeaf() - operationLeaf.Opcode = "add" - operationLeaf.Operands[1] = { Register = 7, Segment = 2 } -- EBP:SS - operationLeaf.Operands[2] = addressLeaf - else -- Pointer to a stack variable - -- FIXME: check if var is an array - - -- Create leaf that returns pointer to stack - operationLeaf = self:NewLeaf() - operationLeaf.Opcode = "add" - operationLeaf.Operands[1] = { Register = 7, Segment = 2 } -- EBP:SS - operationLeaf.Operands[2] = { Constant = label.StackOffset } - end - else - -- All other pointers must be resolved by constant expression parser - -- If they are not, it's a bug - self:Error("Internal error 085") - end - end - else - self:Error("Identifier expected") - return - end - elseif self:MatchToken(TOKEN.TIMES) then -- Parse memory read operation - local pointerLeaf = self:Expression_LevelLeaf(3) - operationLeaf = { MemoryPointer = pointerLeaf } - elseif self:MatchToken(TOKEN.INC) then -- Parse ++X - local operandLeaf = self:Expression_LevelLeaf(3) - operationLeaf = self:Expression_ExplictIncDec("inc",operandLeaf,true) - elseif self:MatchToken(TOKEN.DEC) then -- Parse --X - local operandLeaf = self:Expression_LevelLeaf(3) - operationLeaf = self:Expression_ExplictIncDec("dec",operandLeaf,true) - elseif self:MatchToken(TOKEN.REGISTER) or self:MatchToken(TOKEN.SEGMENT) then - local register = self.TokenData - if self.TokenType == TOKEN.SEGMENT then register = register + 15 end - - if self:MatchToken(TOKEN.INC) then -- reg++ - operationLeaf = self:Expression_ExplictIncDec("inc",register) - elseif self:MatchToken(TOKEN.DEC) then -- reg-- - operationLeaf = self:Expression_ExplictIncDec("dec",register) - else - operationLeaf = { Register = register } - end - elseif self:MatchToken(TOKEN.IDENT) then - -- Check if variable lies inside structure - local dotPos = string.find(self.TokenData,"[.]") - if dotPos then - local structName = string.sub(self.TokenData,1,dotPos-1) - local memberName = string.sub(self.TokenData,dotPos+1) - local structLabel = self:GetLabel(structName) - if structLabel.Struct then -- Fetch structure member - local structData = self.Structs[structLabel.Struct] - - -- Some error checks - if not structData[memberName] then - self:Error("Undefined structure member: "..memberName) - end - - -- Generate leaf - local addressLeaf = self:NewLeaf() - if structLabel.Type == "Stack" then - if structLabel.PointerToStruct then - addressLeaf.Opcode = "add" - addressLeaf.Operands[1] = { Stack = structLabel.StackOffset } - addressLeaf.Operands[2] = { Constant = structData[memberName].Offset } - operationLeaf = { MemoryPointer = addressLeaf } - else - operationLeaf = { Stack = structLabel.StackOffset+structData[memberName].Offset-1 } - end - elseif structLabel.Type == "Variable" then - if structLabel.PointerToStruct then - addressLeaf.Opcode = "add" - addressLeaf.Operands[1] = { Constant = structData[memberName].Offset } - addressLeaf.Operands[2] = { Memory = structLabel } - operationLeaf = { MemoryPointer = addressLeaf } - else - addressLeaf.Opcode = "add" - addressLeaf.Operands[1] = { Constant = structData[memberName].Offset } - addressLeaf.Operands[2] = { Constant = - { { Type = self.TOKEN.AND, Position = self:CurrentSourcePosition() }, - { Type = self.TOKEN.IDENT, Data = structLabel.Name, Position = self:CurrentSourcePosition() }, - } - } - operationLeaf = { MemoryPointer = addressLeaf } - end - else - self:Error("Internal error 164") - end - - return operationLeaf - end - end - - -- Try to fetch it normal way - local label = self:GetLabel(self.TokenData) - local forceType = label.ForceType - - if self:MatchToken(TOKEN.INC) then -- Parse var++ - operationLeaf = self:Expression_ExplictIncDec("inc",label) - elseif self:MatchToken(TOKEN.DEC) then -- Parse var-- - operationLeaf = self:Expression_ExplictIncDec("dec",label) - elseif self:MatchToken(TOKEN.LPAREN) then -- Parse a function call - operationLeaf = self:Expression_FunctionCall(label) - elseif self:MatchToken(TOKEN.LSUBSCR) then -- Parse array access - operationLeaf = self:Expression_ArrayAccess(label) - else -- Parse variable access - if label.Type == "Variable" then -- Read from a variable - -- Array variables are resolved as pointers at constant expression stage - if label.Struct then - operationLeaf = { Memory = label, ForceType = forceType } - operationLeaf.Memory.CopySize = self.StructSize[label.Struct] - else - operationLeaf = { Memory = label, ForceType = forceType } - end - elseif label.Type == "Unknown" then -- Read from an unknown variable - operationLeaf = { UnknownOperationByLabel = label, ForceType = forceType } - elseif label.Type == "Stack" then -- Read from stack - if label.Array then - -- Array on stack - return pointer - operationLeaf = self:NewLeaf() - operationLeaf.Opcode = "add" - operationLeaf.Operands[1] = { Register = 7, Segment = 2 } -- EBP:SS - operationLeaf.Operands[2] = { Constant = label.StackOffset } - else - -- Stack variable - operationLeaf = { Stack = label.StackOffset, ForceType = forceType } - end - elseif label.Type == "Register" then - -- Register variable - if self.RegisterIdentities[label.Name] == -1 then - self:Error("Attempting to use register variable after zapping it") - end - operationLeaf = { Register = label.Value } - end - end - elseif self:MatchToken(TOKEN.LPAREN) then -- (...) - if self:MatchToken(TOKEN.TYPE) then - local forceType = self.TokenData - operationLeaf = self:Expression_LevelLeaf(3) - operationLeaf.ForceType = forceType - else - operationLeaf = self:Expression_LevelLeaf(0) - end - self:ExpectToken(TOKEN.RPAREN) - end - - if not operationLeaf then - self:Error("Expression expected, got \""..self:PrintTokens(self:GetSavedTokens()).."\"") - return - else - -- Assign sourcecode position to leaf - if not operationLeaf.CurrentPosition then - operationLeaf.CurrentPosition = self:CurrentSourcePosition() - end - - -- Negate the result if required - if negateLeaf then - negateLeaf.Operands[1] = operationLeaf - return negateLeaf - else - return operationLeaf - end - end + local negateLeaf,operationLeaf + + + -- Negate value if required + if self:MatchToken(TOKEN.MINUS) then -- "-" + negateLeaf = self:NewLeaf() + negateLeaf.Opcode = "neg" + end + -- Logically negate value if required + if self:MatchToken(TOKEN.NOT) then -- "!" + negateLeaf = self:NewLeaf() + negateLeaf.Opcode = "lneg" + end + + + if self:MatchToken(TOKEN.AND) then -- Parse retrieve pointer operation (&var) + if self:MatchToken(TOKEN.IDENT) then + local label = self:GetLabel(self.TokenData) + + if self:MatchToken(TOKEN.LSUBSCR) then -- Parse array access + local _,addressLeaf = self:Expression_ArrayAccess(label) + if label.Type == "Stack" then + operationLeaf = self:NewLeaf() + operationLeaf.Opcode = "add" + operationLeaf.Operands[1] = { Register = 7, Segment = 2 } -- EBP:SS + operationLeaf.Operands[2] = addressLeaf + else + operationLeaf = addressLeaf + end + else + if label.Type == "Stack" then + if self:MatchToken(TOKEN.LSUBSCR) then -- Pointer to element of an array on stack + local arrayOffsetLeaf = self:Expression() + self:ExpectToken(TOKEN.RSUBSCR) + + -- Create leaf for calculating address + local addressLeaf + if arrayOffsetLeaf.Constant then + addressLeaf = { Constant = label.StackOffset+arrayOffsetLeaf.Constant } + else + addressLeaf = self:NewLeaf() + addressLeaf.Opcode = "add" + addressLeaf.Operands[1] = { Constant = label.StackOffset } + addressLeaf.Operands[2] = arrayOffsetLeaf + end + + -- Create leaf that returns pointer to stack + operationLeaf = self:NewLeaf() + operationLeaf.Opcode = "add" + operationLeaf.Operands[1] = { Register = 7, Segment = 2 } -- EBP:SS + operationLeaf.Operands[2] = addressLeaf + else -- Pointer to a stack variable + -- FIXME: check if var is an array + + -- Create leaf that returns pointer to stack + operationLeaf = self:NewLeaf() + operationLeaf.Opcode = "add" + operationLeaf.Operands[1] = { Register = 7, Segment = 2 } -- EBP:SS + operationLeaf.Operands[2] = { Constant = label.StackOffset } + end + else + -- All other pointers must be resolved by constant expression parser + -- If they are not, it's a bug + self:Error("Internal error 085") + end + end + else + self:Error("Identifier expected") + return + end + elseif self:MatchToken(TOKEN.TIMES) then -- Parse memory read operation + local pointerLeaf = self:Expression_LevelLeaf(3) + operationLeaf = { MemoryPointer = pointerLeaf } + elseif self:MatchToken(TOKEN.INC) then -- Parse ++X + local operandLeaf = self:Expression_LevelLeaf(3) + operationLeaf = self:Expression_ExplictIncDec("inc",operandLeaf,true) + elseif self:MatchToken(TOKEN.DEC) then -- Parse --X + local operandLeaf = self:Expression_LevelLeaf(3) + operationLeaf = self:Expression_ExplictIncDec("dec",operandLeaf,true) + elseif self:MatchToken(TOKEN.REGISTER) or self:MatchToken(TOKEN.SEGMENT) then + local register = self.TokenData + if self.TokenType == TOKEN.SEGMENT then register = register + 15 end + + if self:MatchToken(TOKEN.INC) then -- reg++ + operationLeaf = self:Expression_ExplictIncDec("inc",register) + elseif self:MatchToken(TOKEN.DEC) then -- reg-- + operationLeaf = self:Expression_ExplictIncDec("dec",register) + else + operationLeaf = { Register = register } + end + elseif self:MatchToken(TOKEN.IDENT) then + -- Check if variable lies inside structure + local dotPos = string.find(self.TokenData,"[.]") + if dotPos then + local structName = string.sub(self.TokenData,1,dotPos-1) + local memberName = string.sub(self.TokenData,dotPos+1) + local structLabel = self:GetLabel(structName) + if structLabel.Struct then -- Fetch structure member + local structData = self.Structs[structLabel.Struct] + + -- Some error checks + if not structData[memberName] then + self:Error("Undefined structure member: "..memberName) + end + + -- Generate leaf + local addressLeaf = self:NewLeaf() + if structLabel.Type == "Stack" then + if structLabel.PointerToStruct then + addressLeaf.Opcode = "add" + addressLeaf.Operands[1] = { Stack = structLabel.StackOffset } + addressLeaf.Operands[2] = { Constant = structData[memberName].Offset } + operationLeaf = { MemoryPointer = addressLeaf } + else + operationLeaf = { Stack = structLabel.StackOffset+structData[memberName].Offset-1 } + end + elseif structLabel.Type == "Variable" then + if structLabel.PointerToStruct then + addressLeaf.Opcode = "add" + addressLeaf.Operands[1] = { Constant = structData[memberName].Offset } + addressLeaf.Operands[2] = { Memory = structLabel } + operationLeaf = { MemoryPointer = addressLeaf } + else + addressLeaf.Opcode = "add" + addressLeaf.Operands[1] = { Constant = structData[memberName].Offset } + addressLeaf.Operands[2] = { Constant = + { { Type = self.TOKEN.AND, Position = self:CurrentSourcePosition() }, + { Type = self.TOKEN.IDENT, Data = structLabel.Name, Position = self:CurrentSourcePosition() }, + } + } + operationLeaf = { MemoryPointer = addressLeaf } + end + else + self:Error("Internal error 164") + end + + return operationLeaf + end + end + + -- Try to fetch it normal way + local label = self:GetLabel(self.TokenData) + local forceType = label.ForceType + + if self:MatchToken(TOKEN.INC) then -- Parse var++ + operationLeaf = self:Expression_ExplictIncDec("inc",label) + elseif self:MatchToken(TOKEN.DEC) then -- Parse var-- + operationLeaf = self:Expression_ExplictIncDec("dec",label) + elseif self:MatchToken(TOKEN.LPAREN) then -- Parse a function call + operationLeaf = self:Expression_FunctionCall(label) + elseif self:MatchToken(TOKEN.LSUBSCR) then -- Parse array access + operationLeaf = self:Expression_ArrayAccess(label) + else -- Parse variable access + if label.Type == "Variable" then -- Read from a variable + -- Array variables are resolved as pointers at constant expression stage + if label.Struct then + operationLeaf = { Memory = label, ForceType = forceType } + operationLeaf.Memory.CopySize = self.StructSize[label.Struct] + else + operationLeaf = { Memory = label, ForceType = forceType } + end + elseif label.Type == "Unknown" then -- Read from an unknown variable + operationLeaf = { UnknownOperationByLabel = label, ForceType = forceType } + elseif label.Type == "Stack" then -- Read from stack + if label.Array then + -- Array on stack - return pointer + operationLeaf = self:NewLeaf() + operationLeaf.Opcode = "add" + operationLeaf.Operands[1] = { Register = 7, Segment = 2 } -- EBP:SS + operationLeaf.Operands[2] = { Constant = label.StackOffset } + else + -- Stack variable + operationLeaf = { Stack = label.StackOffset, ForceType = forceType } + end + elseif label.Type == "Register" then + -- Register variable + if self.RegisterIdentities[label.Name] == -1 then + self:Error("Attempting to use register variable after zapping it") + end + operationLeaf = { Register = label.Value } + end + end + elseif self:MatchToken(TOKEN.LPAREN) then -- (...) + if self:MatchToken(TOKEN.TYPE) then + local forceType = self.TokenData + operationLeaf = self:Expression_LevelLeaf(3) + operationLeaf.ForceType = forceType + else + operationLeaf = self:Expression_LevelLeaf(0) + end + self:ExpectToken(TOKEN.RPAREN) + end + + if not operationLeaf then + self:Error("Expression expected, got \""..self:PrintTokens(self:GetSavedTokens()).."\"") + return + else + -- Assign sourcecode position to leaf + if not operationLeaf.CurrentPosition then + operationLeaf.CurrentPosition = self:CurrentSourcePosition() + end + + -- Negate the result if required + if negateLeaf then + negateLeaf.Operands[1] = operationLeaf + return negateLeaf + else + return operationLeaf + end + end end -- level2: * function HCOMP:Expression_Level2() - local leftLeaf = self:Expression_LevelLeaf(3) - - local token = self:PeekToken() - if (token == self.TOKEN.TIMES) or - (token == self.TOKEN.SLASH) or - (token == self.TOKEN.POWER) or - (token == self.TOKEN.MODULUS) then - self:NextToken() - local rightLeaf = self:Expression_LevelLeaf(2) - - if token == self.TOKEN.TIMES then return self:NewOpcode("mul", leftLeaf,rightLeaf) end - if token == self.TOKEN.SLASH then return self:NewOpcode("div", leftLeaf,rightLeaf) end - if token == self.TOKEN.POWER then return self:NewOpcode("fpwr",leftLeaf,rightLeaf) end - if token == self.TOKEN.MODULUS then return self:NewOpcode("mod",leftLeaf,rightLeaf) end - else - return leftLeaf - end + local leftLeaf = self:Expression_LevelLeaf(3) + + local token = self:PeekToken() + if (token == self.TOKEN.TIMES) or + (token == self.TOKEN.SLASH) or + (token == self.TOKEN.POWER) or + (token == self.TOKEN.MODULUS) then + self:NextToken() + local rightLeaf = self:Expression_LevelLeaf(2) + + if token == self.TOKEN.TIMES then return self:NewOpcode("mul", leftLeaf,rightLeaf) end + if token == self.TOKEN.SLASH then return self:NewOpcode("div", leftLeaf,rightLeaf) end + if token == self.TOKEN.POWER then return self:NewOpcode("fpwr",leftLeaf,rightLeaf) end + if token == self.TOKEN.MODULUS then return self:NewOpcode("mod",leftLeaf,rightLeaf) end + else + return leftLeaf + end end -- level1: + function HCOMP:Expression_Level1() - local leftLeaf = self:Expression_LevelLeaf(2) - - local token = self:PeekToken() - if (token == self.TOKEN.PLUS) or - (token == self.TOKEN.MINUS) then -- +- - -- Treat "-" as negate instead of subtraction FIXME - if token == self.TOKEN.PLUS then self:NextToken() end - - local rightLeaf = self:Expression_LevelLeaf(0) - return self:NewOpcode("add",leftLeaf,rightLeaf) - elseif (token == self.TOKEN.LAND) or - (token == self.TOKEN.LOR) then -- &&, || - self:NextToken() - local rightLeaf = self:Expression_LevelLeaf(0) - - if token == self.TOKEN.LAND then return self:NewOpcode("and",leftLeaf,rightLeaf) end - if token == self.TOKEN.LOR then return self:NewOpcode("or",leftLeaf,rightLeaf) end - elseif (token == self.TOKEN.AND) or - (token == self.TOKEN.OR) or - (token == self.TOKEN.XOR) then -- &, |, ^ - self:NextToken() - local rightLeaf = self:Expression_LevelLeaf(0) - - if token == self.TOKEN.AND then return self:NewOpcode("band",leftLeaf,rightLeaf) end - if token == self.TOKEN.OR then return self:NewOpcode("bor", leftLeaf,rightLeaf) end - if token == self.TOKEN.XOR then return self:NewOpcode("bxor",leftLeaf,rightLeaf) end - else - return leftLeaf - end + local leftLeaf = self:Expression_LevelLeaf(2) + + local token = self:PeekToken() + if (token == self.TOKEN.PLUS) or + (token == self.TOKEN.MINUS) then -- +- + -- Treat "-" as negate instead of subtraction FIXME + if token == self.TOKEN.PLUS then self:NextToken() end + + local rightLeaf = self:Expression_LevelLeaf(0) + return self:NewOpcode("add",leftLeaf,rightLeaf) + elseif (token == self.TOKEN.LAND) or + (token == self.TOKEN.LOR) then -- &&, || + self:NextToken() + local rightLeaf = self:Expression_LevelLeaf(0) + + if token == self.TOKEN.LAND then return self:NewOpcode("and",leftLeaf,rightLeaf) end + if token == self.TOKEN.LOR then return self:NewOpcode("or",leftLeaf,rightLeaf) end + elseif (token == self.TOKEN.AND) or + (token == self.TOKEN.OR) or + (token == self.TOKEN.XOR) then -- &, |, ^ + self:NextToken() + local rightLeaf = self:Expression_LevelLeaf(0) + + if token == self.TOKEN.AND then return self:NewOpcode("band",leftLeaf,rightLeaf) end + if token == self.TOKEN.OR then return self:NewOpcode("bor", leftLeaf,rightLeaf) end + if token == self.TOKEN.XOR then return self:NewOpcode("bxor",leftLeaf,rightLeaf) end + else + return leftLeaf + end end -- level0: = function HCOMP:Expression_Level0() - local leftLeaf = self:Expression_LevelLeaf(1) - - if self:MatchToken(self.TOKEN.EQUAL) then -- = - local rightLeaf = self:Expression_LevelLeaf(0) - if leftLeaf.Memory then - if rightLeaf.Memory then - if leftLeaf.Memory.CopySize and rightLeaf.Memory.CopySize then - local topLeaf = self:NewOpcode("mov",leftLeaf,rightLeaf) - local curLeaf = topLeaf - local tempLeftLeaf,tempRightLeaf - for i=1,math.min(leftLeaf.Memory.CopySize,rightLeaf.Memory.CopySize)-1 do - tempLeftLeaf = { - CurrentPosition = leftLeaf.CurrentPosition, - Memory = leftLeaf.Memory, - MemAddrOffset = i - } - tempRightLeaf = { - CurrentPosition = rightLeaf.CurrentPosition, - Memory = rightLeaf.Memory, - MemAddrOffset = i - } - curLeaf.PreviousLeaf = self:NewOpcode("mov",tempLeftLeaf,tempRightLeaf) - curLeaf = curLeaf.PreviousLeaf - end - -- example of generated output (because previousleaf is parsed before the leaf containing previousleaf) - -- MOV #0+2,#3+2 - -- MOV #0+1,#3+1 - -- MOV #0,#3 - -- * Mark these both with an offset so they won't warn - leftLeaf.MemAddrOffset = 0 - rightLeaf.MemAddrOffset = 0 - curLeaf.Comment = topLeaf.Comment - topLeaf.Comment = nil - return topLeaf - end - elseif rightLeaf.MemoryRegister then - if leftLeaf.Memory.CopySize and rightLeaf.CopySize then - local topLeaf = self:NewOpcode("mov",leftLeaf,rightLeaf) - local curLeaf = topLeaf - local tempLeftLeaf - for i=1,math.min(leftLeaf.Memory.CopySize,rightLeaf.CopySize)-1 do - tempLeftLeaf = { - CurrentPosition = leftLeaf.CurrentPosition, - Memory = leftLeaf.Memory, - MemAddrOffset = i - } - curLeaf.PreviousLeaf = self:NewOpcode("inc",{Register = rightLeaf.MemoryRegister}) - curLeaf = curLeaf.PreviousLeaf - curLeaf.PreviousLeaf = self:NewOpcode("mov",tempLeftLeaf,rightLeaf) - curLeaf = curLeaf.PreviousLeaf - end - -- example of generated output (because previousleaf is parsed before the leaf containing previousleaf) - -- MOV #0+2,#R0 - -- INC R0 - -- MOV #0+1,#R0 - -- INC R0 - -- MOV #0,#R0 - -- * Mark these both with an offset so they won't warn - leftLeaf.MemAddrOffset = 0 - rightLeaf.MemAddrOffset = 0 - curLeaf.Comment = topLeaf.Comment - topLeaf.Comment = nil - return topLeaf - end - end - end - -- Mark this leaf as an explict assign operation - local operationLeaf = self:NewOpcode("mov",leftLeaf,rightLeaf) - operationLeaf.ExplictAssign = true - operationLeaf.ReturnAfterAssign = true - return operationLeaf - elseif self:MatchToken(self.TOKEN.LSS) then -- < - local rightLeaf = self:Expression_LevelLeaf(0) - return self:NewOpcode("max", - self:NewOpcode("fsgn", - self:NewOpcode("sub",rightLeaf,leftLeaf), - { TrigonometryHack = true } - ), - { Constant = 0 } - ) - elseif self:MatchToken(self.TOKEN.GTR) then -- > - local rightLeaf = self:Expression_LevelLeaf(0) - return self:NewOpcode("max", - self:NewOpcode("fsgn", - self:NewOpcode("neg", - self:NewOpcode("sub",rightLeaf,leftLeaf) - ), - { TrigonometryHack = true } - ), - { Constant = 0 } - ) - elseif self:MatchToken(self.TOKEN.LEQ) then -- <= - -- FIXME: returns "0", "1", or "2" instead of just 1 or 0 - -- Does not alter comparsions, but might be annoying? - local rightLeaf = self:Expression_LevelLeaf(0) - return self:NewOpcode("max", - self:NewOpcode("inc", - self:NewOpcode("fsgn", - self:NewOpcode("sub",rightLeaf,leftLeaf), - { TrigonometryHack = true } - ) - ), - { Constant = 0 } - ) - elseif self:MatchToken(self.TOKEN.GEQ) then -- >= - -- FIXME: returns "0", "1", or "2" instead of just 1 or 0 - -- Does not alter comparsions, but might be annoying? - local rightLeaf = self:Expression_LevelLeaf(0) - return self:NewOpcode("max", - self:NewOpcode("inc", - self:NewOpcode("fsgn", - self:NewOpcode("neg", - self:NewOpcode("sub",rightLeaf,leftLeaf) - ), - { TrigonometryHack = true } - ) - ), - { Constant = 0 } - ) - elseif self:MatchToken(self.TOKEN.EQL) then -- == - local rightLeaf = self:Expression_LevelLeaf(0) - return self:NewOpcode("lneg", - self:NewOpcode("fsgn", - self:NewOpcode("fabs", - self:NewOpcode("sub",rightLeaf,leftLeaf), - { TrigonometryHack = true } - ), - { TrigonometryHack = true } - ) - ) - elseif self:MatchToken(self.TOKEN.NEQ) then -- != - local rightLeaf = self:Expression_LevelLeaf(0) - return self:NewOpcode("fsgn", - self:NewOpcode("fabs", - self:NewOpcode("sub",rightLeaf,leftLeaf), - { TrigonometryHack = true } - ), - { TrigonometryHack = true } - ) - elseif self:MatchToken(self.TOKEN.EQLADD) then -- += - local rightLeaf = self:Expression_LevelLeaf(0) - - local operationLeaf = self:NewOpcode("add",leftLeaf,rightLeaf) - operationLeaf.ExplictAssign = true - operationLeaf.ReturnAfterAssign = true - return operationLeaf - elseif self:MatchToken(self.TOKEN.EQLSUB) then -- -= - local rightLeaf = self:Expression_LevelLeaf(0) - - local operationLeaf = self:NewOpcode("sub",leftLeaf,rightLeaf) - operationLeaf.ExplictAssign = true - operationLeaf.ReturnAfterAssign = true - return operationLeaf - elseif self:MatchToken(self.TOKEN.EQLMUL) then -- *= - local rightLeaf = self:Expression_LevelLeaf(0) - - local operationLeaf = self:NewOpcode("mul",leftLeaf,rightLeaf) - operationLeaf.ExplictAssign = true - operationLeaf.ReturnAfterAssign = true - return operationLeaf - elseif self:MatchToken(self.TOKEN.EQLDIV) then -- /= - local rightLeaf = self:Expression_LevelLeaf(0) - - local operationLeaf = self:NewOpcode("div",leftLeaf,rightLeaf) - operationLeaf.ExplictAssign = true - operationLeaf.ReturnAfterAssign = true - return operationLeaf - elseif self:MatchToken(self.TOKEN.SHR) then -- >> - local rightLeaf = self:Expression_LevelLeaf(0) - - local operationLeaf = self:NewOpcode("bshr",leftLeaf,rightLeaf) - operationLeaf.ExplictAssign = true - operationLeaf.ReturnAfterAssign = true - return operationLeaf - elseif self:MatchToken(self.TOKEN.SHL) then -- << - local rightLeaf = self:Expression_LevelLeaf(0) - - local operationLeaf = self:NewOpcode("bshl",leftLeaf,rightLeaf) - operationLeaf.ExplictAssign = true - operationLeaf.ReturnAfterAssign = true - return operationLeaf - else - return leftLeaf - end + local leftLeaf = self:Expression_LevelLeaf(1) + + if self:MatchToken(self.TOKEN.EQUAL) then -- = + local rightLeaf = self:Expression_LevelLeaf(0) + if leftLeaf.Memory then + if rightLeaf.Memory then + if leftLeaf.Memory.CopySize and rightLeaf.Memory.CopySize then + local topLeaf = self:NewOpcode("mov",leftLeaf,rightLeaf) + local curLeaf = topLeaf + local tempLeftLeaf,tempRightLeaf + for i=1,math.min(leftLeaf.Memory.CopySize,rightLeaf.Memory.CopySize)-1 do + tempLeftLeaf = { + CurrentPosition = leftLeaf.CurrentPosition, + Memory = leftLeaf.Memory, + MemAddrOffset = i + } + tempRightLeaf = { + CurrentPosition = rightLeaf.CurrentPosition, + Memory = rightLeaf.Memory, + MemAddrOffset = i + } + curLeaf.PreviousLeaf = self:NewOpcode("mov",tempLeftLeaf,tempRightLeaf) + curLeaf = curLeaf.PreviousLeaf + end + -- example of generated output (because previousleaf is parsed before the leaf containing previousleaf) + -- MOV #0+2,#3+2 + -- MOV #0+1,#3+1 + -- MOV #0,#3 + -- * Mark these both with an offset so they won't warn + leftLeaf.MemAddrOffset = 0 + rightLeaf.MemAddrOffset = 0 + curLeaf.Comment = topLeaf.Comment + topLeaf.Comment = nil + return topLeaf + end + elseif rightLeaf.MemoryRegister then + if leftLeaf.Memory.CopySize and rightLeaf.CopySize then + local topLeaf = self:NewOpcode("mov",leftLeaf,rightLeaf) + local curLeaf = topLeaf + local tempLeftLeaf + for i=1,math.min(leftLeaf.Memory.CopySize,rightLeaf.CopySize)-1 do + tempLeftLeaf = { + CurrentPosition = leftLeaf.CurrentPosition, + Memory = leftLeaf.Memory, + MemAddrOffset = i + } + curLeaf.PreviousLeaf = self:NewOpcode("inc",{Register = rightLeaf.MemoryRegister}) + curLeaf = curLeaf.PreviousLeaf + curLeaf.PreviousLeaf = self:NewOpcode("mov",tempLeftLeaf,rightLeaf) + curLeaf = curLeaf.PreviousLeaf + end + -- example of generated output (because previousleaf is parsed before the leaf containing previousleaf) + -- MOV #0+2,#R0 + -- INC R0 + -- MOV #0+1,#R0 + -- INC R0 + -- MOV #0,#R0 + -- * Mark these both with an offset so they won't warn + leftLeaf.MemAddrOffset = 0 + rightLeaf.MemAddrOffset = 0 + curLeaf.Comment = topLeaf.Comment + topLeaf.Comment = nil + return topLeaf + end + end + end + -- Mark this leaf as an explict assign operation + local operationLeaf = self:NewOpcode("mov",leftLeaf,rightLeaf) + operationLeaf.ExplictAssign = true + operationLeaf.ReturnAfterAssign = true + return operationLeaf + elseif self:MatchToken(self.TOKEN.LSS) then -- < + local rightLeaf = self:Expression_LevelLeaf(0) + return self:NewOpcode("max", + self:NewOpcode("fsgn", + self:NewOpcode("sub",rightLeaf,leftLeaf), + { TrigonometryHack = true } + ), + { Constant = 0 } + ) + elseif self:MatchToken(self.TOKEN.GTR) then -- > + local rightLeaf = self:Expression_LevelLeaf(0) + return self:NewOpcode("max", + self:NewOpcode("fsgn", + self:NewOpcode("neg", + self:NewOpcode("sub",rightLeaf,leftLeaf) + ), + { TrigonometryHack = true } + ), + { Constant = 0 } + ) + elseif self:MatchToken(self.TOKEN.LEQ) then -- <= + -- FIXME: returns "0", "1", or "2" instead of just 1 or 0 + -- Does not alter comparsions, but might be annoying? + local rightLeaf = self:Expression_LevelLeaf(0) + return self:NewOpcode("max", + self:NewOpcode("inc", + self:NewOpcode("fsgn", + self:NewOpcode("sub",rightLeaf,leftLeaf), + { TrigonometryHack = true } + ) + ), + { Constant = 0 } + ) + elseif self:MatchToken(self.TOKEN.GEQ) then -- >= + -- FIXME: returns "0", "1", or "2" instead of just 1 or 0 + -- Does not alter comparsions, but might be annoying? + local rightLeaf = self:Expression_LevelLeaf(0) + return self:NewOpcode("max", + self:NewOpcode("inc", + self:NewOpcode("fsgn", + self:NewOpcode("neg", + self:NewOpcode("sub",rightLeaf,leftLeaf) + ), + { TrigonometryHack = true } + ) + ), + { Constant = 0 } + ) + elseif self:MatchToken(self.TOKEN.EQL) then -- == + local rightLeaf = self:Expression_LevelLeaf(0) + return self:NewOpcode("lneg", + self:NewOpcode("fsgn", + self:NewOpcode("fabs", + self:NewOpcode("sub",rightLeaf,leftLeaf), + { TrigonometryHack = true } + ), + { TrigonometryHack = true } + ) + ) + elseif self:MatchToken(self.TOKEN.NEQ) then -- != + local rightLeaf = self:Expression_LevelLeaf(0) + return self:NewOpcode("fsgn", + self:NewOpcode("fabs", + self:NewOpcode("sub",rightLeaf,leftLeaf), + { TrigonometryHack = true } + ), + { TrigonometryHack = true } + ) + elseif self:MatchToken(self.TOKEN.EQLADD) then -- += + local rightLeaf = self:Expression_LevelLeaf(0) + + local operationLeaf = self:NewOpcode("add",leftLeaf,rightLeaf) + operationLeaf.ExplictAssign = true + operationLeaf.ReturnAfterAssign = true + return operationLeaf + elseif self:MatchToken(self.TOKEN.EQLSUB) then -- -= + local rightLeaf = self:Expression_LevelLeaf(0) + + local operationLeaf = self:NewOpcode("sub",leftLeaf,rightLeaf) + operationLeaf.ExplictAssign = true + operationLeaf.ReturnAfterAssign = true + return operationLeaf + elseif self:MatchToken(self.TOKEN.EQLMUL) then -- *= + local rightLeaf = self:Expression_LevelLeaf(0) + + local operationLeaf = self:NewOpcode("mul",leftLeaf,rightLeaf) + operationLeaf.ExplictAssign = true + operationLeaf.ReturnAfterAssign = true + return operationLeaf + elseif self:MatchToken(self.TOKEN.EQLDIV) then -- /= + local rightLeaf = self:Expression_LevelLeaf(0) + + local operationLeaf = self:NewOpcode("div",leftLeaf,rightLeaf) + operationLeaf.ExplictAssign = true + operationLeaf.ReturnAfterAssign = true + return operationLeaf + elseif self:MatchToken(self.TOKEN.SHR) then -- >> + local rightLeaf = self:Expression_LevelLeaf(0) + + local operationLeaf = self:NewOpcode("bshr",leftLeaf,rightLeaf) + operationLeaf.ExplictAssign = true + operationLeaf.ReturnAfterAssign = true + return operationLeaf + elseif self:MatchToken(self.TOKEN.SHL) then -- << + local rightLeaf = self:Expression_LevelLeaf(0) + + local operationLeaf = self:NewOpcode("bshl",leftLeaf,rightLeaf) + operationLeaf.ExplictAssign = true + operationLeaf.ReturnAfterAssign = true + return operationLeaf + else + return leftLeaf + end end -- Compile a single expression (statement) and return corresponding leaf function HCOMP:Expression() - local leaf = self:Expression_Level0() - return leaf + local leaf = self:Expression_Level0() + return leaf end @@ -747,330 +747,330 @@ end --level3: () or function HCOMP:ConstantExpression_Level3() - local constSign = 1 - if self:MatchToken(self.TOKEN.MINUS) then constSign = -1 end - if self:MatchToken(self.TOKEN.PLUS) then constSign = 1 end - - if self:MatchToken(self.TOKEN.AND) then -- &pointer - if self:MatchToken(self.TOKEN.IDENT) then - local label = self:GetLabel(self.TokenData) - - -- Check if it's a pointer of array member (always dynamic) - if label.Array and self:MatchToken(self.TOKEN.LSUBSCR) then return false end - - -- Check if it's any of the known types - if label.Type == "Pointer" then - self:Error("Ident "..self.TokenData.." is not a variable") - elseif label.Type == "Variable" then - if label.Value and (not self.Settings.GenerateLibrary) - then return true,true,label.Value*constSign - else return true,false,self.Settings.MagicValue - end - elseif label.Type == "Stack" then - -- Pointer to stack value is not a constant - return false - elseif label.Type == "Register" then - if self.RegisterIdentities[label.Name] == -1 then - self:Error("Attempting to use register variable after zapping it") - end - -- Register variable is not a constant - return false - elseif label.Type == "Unknown" then - return true,false,self.Settings.MagicValue - else - self:Error("Ident "..self.TokenData.." is not a label/pointer") - end - else - return false - end - elseif self:MatchToken(self.TOKEN.NUMBER) then - return true,true,self.TokenData*constSign - elseif self:MatchToken(self.TOKEN.CHAR) then - return true,true,self.TokenData*constSign - elseif self:MatchToken(self.TOKEN.STRING) and (not self.IgnoreStringInExpression) then - local stringData = self.TokenData - while self:MatchToken(self.TOKEN.STRING) do - stringData = stringData .. self.TokenData - end - - if self.GlobalStringTable[stringData] then - if self.GlobalStringTable[stringData].Label.Value then - return true,true,self.GlobalStringTable[stringData].Label.Value*constSign - else - return true,false,self.Settings.MagicValue - end - else - if self.StringsTable then - if self.StringsTable[stringData] then - if self.StringsTable[stringData].Label.Value then - return true,true,self.StringsTable[stringData].Label.Value*constSign - else - return true,false,self.Settings.MagicValue - end - else - self.StringsTable[stringData] = self:NewLeaf() - self.StringsTable[stringData].Opcode = "DATA" - self.StringsTable[stringData].Data = { stringData, 0 } - - local stringLabel = self:GetTempLabel() - stringLabel.Leaf = self.StringsTable[stringData] - self.StringsTable[stringData].Label = stringLabel - self.GlobalStringTable[stringData] = self.StringsTable[stringData] - return true,false,self.Settings.MagicValue - end - else - return false - end - end - elseif self:MatchToken(self.TOKEN.IDENT) then - local label = self:GetLabel(self.TokenData) - if self:MatchToken(self.TOKEN.LSUBSCR) then - -- Array access is never constant - return false - end - if self:MatchToken(self.TOKEN.LPAREN) then - -- Function calls are never constant - return false - end - - if label.Type == "Pointer" then - -- Pointers are constant - if label.Value and (not self.Settings.GenerateLibrary) - then return true,true,label.Value*constSign - else return true,false,self.Settings.MagicValue - end - elseif label.Type == "Variable" then - if label.Array then - -- Array variables must be treated as pointers - if label.Value and (not self.Settings.GenerateLibrary) - then return true,true,label.Value*constSign - else return true,false,self.Settings.MagicValue - end - else - -- Variables are not constant - return false - end - elseif label.Type == "Stack" then - -- Stack variables are not constant - return false - elseif label.Type == "Register" then - if self.RegisterIdentities[label.Name] == -1 then - self:Error("Attempting to use register variable after zapping it") - end - -- Register variable is not a constant - return false - elseif label.Type == "Unknown" then - if self.MostLikelyConstantExpression then - -- Unknown variables are not constant, but they usually are. - return true,false,self.Settings.MagicValue - else - -- It's probably not a constant expression - return false - end - - -- If this variable wasn't really constant, the error will be caught - -- on the final output stage when all constant expressions are - -- recalculated - else - self:Error("Ident "..self.TokenData.." is not a label/pointer") - end - elseif self:MatchToken(self.TOKEN.LPAREN) then - local isConst,isPrecise,Value = self:ConstantExpression_Level0() - if not isConst then return false end - self:MatchToken(self.TOKEN.RPAREN) - --FIXME: this should be expect when you NEED constant value, and match when you TEST FOR constant value - - return true,isPrecise,Value*constSign - end - - return false + local constSign = 1 + if self:MatchToken(self.TOKEN.MINUS) then constSign = -1 end + if self:MatchToken(self.TOKEN.PLUS) then constSign = 1 end + + if self:MatchToken(self.TOKEN.AND) then -- &pointer + if self:MatchToken(self.TOKEN.IDENT) then + local label = self:GetLabel(self.TokenData) + + -- Check if it's a pointer of array member (always dynamic) + if label.Array and self:MatchToken(self.TOKEN.LSUBSCR) then return false end + + -- Check if it's any of the known types + if label.Type == "Pointer" then + self:Error("Ident "..self.TokenData.." is not a variable") + elseif label.Type == "Variable" then + if label.Value and (not self.Settings.GenerateLibrary) + then return true,true,label.Value*constSign + else return true,false,self.Settings.MagicValue + end + elseif label.Type == "Stack" then + -- Pointer to stack value is not a constant + return false + elseif label.Type == "Register" then + if self.RegisterIdentities[label.Name] == -1 then + self:Error("Attempting to use register variable after zapping it") + end + -- Register variable is not a constant + return false + elseif label.Type == "Unknown" then + return true,false,self.Settings.MagicValue + else + self:Error("Ident "..self.TokenData.." is not a label/pointer") + end + else + return false + end + elseif self:MatchToken(self.TOKEN.NUMBER) then + return true,true,self.TokenData*constSign + elseif self:MatchToken(self.TOKEN.CHAR) then + return true,true,self.TokenData*constSign + elseif self:MatchToken(self.TOKEN.STRING) and (not self.IgnoreStringInExpression) then + local stringData = self.TokenData + while self:MatchToken(self.TOKEN.STRING) do + stringData = stringData .. self.TokenData + end + + if self.GlobalStringTable[stringData] then + if self.GlobalStringTable[stringData].Label.Value then + return true,true,self.GlobalStringTable[stringData].Label.Value*constSign + else + return true,false,self.Settings.MagicValue + end + else + if self.StringsTable then + if self.StringsTable[stringData] then + if self.StringsTable[stringData].Label.Value then + return true,true,self.StringsTable[stringData].Label.Value*constSign + else + return true,false,self.Settings.MagicValue + end + else + self.StringsTable[stringData] = self:NewLeaf() + self.StringsTable[stringData].Opcode = "DATA" + self.StringsTable[stringData].Data = { stringData, 0 } + + local stringLabel = self:GetTempLabel() + stringLabel.Leaf = self.StringsTable[stringData] + self.StringsTable[stringData].Label = stringLabel + self.GlobalStringTable[stringData] = self.StringsTable[stringData] + return true,false,self.Settings.MagicValue + end + else + return false + end + end + elseif self:MatchToken(self.TOKEN.IDENT) then + local label = self:GetLabel(self.TokenData) + if self:MatchToken(self.TOKEN.LSUBSCR) then + -- Array access is never constant + return false + end + if self:MatchToken(self.TOKEN.LPAREN) then + -- Function calls are never constant + return false + end + + if label.Type == "Pointer" then + -- Pointers are constant + if label.Value and (not self.Settings.GenerateLibrary) + then return true,true,label.Value*constSign + else return true,false,self.Settings.MagicValue + end + elseif label.Type == "Variable" then + if label.Array then + -- Array variables must be treated as pointers + if label.Value and (not self.Settings.GenerateLibrary) + then return true,true,label.Value*constSign + else return true,false,self.Settings.MagicValue + end + else + -- Variables are not constant + return false + end + elseif label.Type == "Stack" then + -- Stack variables are not constant + return false + elseif label.Type == "Register" then + if self.RegisterIdentities[label.Name] == -1 then + self:Error("Attempting to use register variable after zapping it") + end + -- Register variable is not a constant + return false + elseif label.Type == "Unknown" then + if self.MostLikelyConstantExpression then + -- Unknown variables are not constant, but they usually are. + return true,false,self.Settings.MagicValue + else + -- It's probably not a constant expression + return false + end + + -- If this variable wasn't really constant, the error will be caught + -- on the final output stage when all constant expressions are + -- recalculated + else + self:Error("Ident "..self.TokenData.." is not a label/pointer") + end + elseif self:MatchToken(self.TOKEN.LPAREN) then + local isConst,isPrecise,Value = self:ConstantExpression_Level0() + if not isConst then return false end + self:MatchToken(self.TOKEN.RPAREN) + --FIXME: this should be expect when you NEED constant value, and match when you TEST FOR constant value + + return true,isPrecise,Value*constSign + end + + return false end --level2: * function HCOMP:ConstantExpression_Level2() - local leftConst,leftPrecise,leftValue = self:ConstantExpression_Level3() - if not leftConst then return false end - - local token = self:PeekToken() - if (token == self.TOKEN.TIMES) or - (token == self.TOKEN.SLASH) or - (token == self.TOKEN.POWER) or - (token == self.TOKEN.MODULUS) then - self:NextToken() - local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level2() - if not rightConst then return false end - - if token == self.TOKEN.TIMES then return true,(leftPrecise and rightPrecise),leftValue*rightValue end - if token == self.TOKEN.SLASH then return true,(leftPrecise and rightPrecise),leftValue/rightValue end - if token == self.TOKEN.POWER then return true,(leftPrecise and rightPrecise),leftValue^rightValue end - if token == self.TOKEN.MODULUS then return true,(leftPrecise and rightPrecise),leftValue%rightValue end - else - return true,leftPrecise,leftValue - end + local leftConst,leftPrecise,leftValue = self:ConstantExpression_Level3() + if not leftConst then return false end + + local token = self:PeekToken() + if (token == self.TOKEN.TIMES) or + (token == self.TOKEN.SLASH) or + (token == self.TOKEN.POWER) or + (token == self.TOKEN.MODULUS) then + self:NextToken() + local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level2() + if not rightConst then return false end + + if token == self.TOKEN.TIMES then return true,(leftPrecise and rightPrecise),leftValue*rightValue end + if token == self.TOKEN.SLASH then return true,(leftPrecise and rightPrecise),leftValue/rightValue end + if token == self.TOKEN.POWER then return true,(leftPrecise and rightPrecise),leftValue^rightValue end + if token == self.TOKEN.MODULUS then return true,(leftPrecise and rightPrecise),leftValue%rightValue end + else + return true,leftPrecise,leftValue + end end --level1: + function HCOMP:ConstantExpression_Level1() - local leftConst,leftPrecise,leftValue = self:ConstantExpression_Level2() - if not leftConst then return false end - - local token = self:PeekToken() - if (token == self.TOKEN.PLUS) or - (token == self.TOKEN.MINUS) then - if token == self.TOKEN.PLUS then self:NextToken() end - local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() - if not rightConst then return false end - - return true,(leftPrecise and rightPrecise),leftValue+rightValue - elseif (token == self.TOKEN.LAND) or - (token == self.TOKEN.LOR) then -- &&, || - self:NextToken() - local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() - if not rightConst then return false end - - if token == self.TOKEN.LAND then - if (leftValue > 0) and (rightValue > 0) then - return true,(leftPrecise and rightPrecise),1 - else - return true,(leftPrecise and rightPrecise),0 - end - end - if token == self.TOKEN.LOR then - if (leftValue > 0) or (rightValue > 0) then - return true,(leftPrecise and rightPrecise),1 - else - return true,(leftPrecise and rightPrecise),0 - end - end - elseif (token == self.TOKEN.AND) or - (token == self.TOKEN.OR) or - (token == self.TOKEN.XOR) then -- &, |, ^ - self:NextToken() - local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() - if not rightConst then return false end - - if token == self.TOKEN.AND then return true,(leftPrecise and rightPrecise),self:BinaryAnd(leftValue,rightValue) end - if token == self.TOKEN.OR then return true,(leftPrecise and rightPrecise),self:BinaryOr (leftValue,rightValue) end - if token == self.TOKEN.XOR then return true,(leftPrecise and rightPrecise),self:BinaryXor(leftValue,rightValue) end - else - return true,leftPrecise,leftValue - end + local leftConst,leftPrecise,leftValue = self:ConstantExpression_Level2() + if not leftConst then return false end + + local token = self:PeekToken() + if (token == self.TOKEN.PLUS) or + (token == self.TOKEN.MINUS) then + if token == self.TOKEN.PLUS then self:NextToken() end + local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() + if not rightConst then return false end + + return true,(leftPrecise and rightPrecise),leftValue+rightValue + elseif (token == self.TOKEN.LAND) or + (token == self.TOKEN.LOR) then -- &&, || + self:NextToken() + local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() + if not rightConst then return false end + + if token == self.TOKEN.LAND then + if (leftValue > 0) and (rightValue > 0) then + return true,(leftPrecise and rightPrecise),1 + else + return true,(leftPrecise and rightPrecise),0 + end + end + if token == self.TOKEN.LOR then + if (leftValue > 0) or (rightValue > 0) then + return true,(leftPrecise and rightPrecise),1 + else + return true,(leftPrecise and rightPrecise),0 + end + end + elseif (token == self.TOKEN.AND) or + (token == self.TOKEN.OR) or + (token == self.TOKEN.XOR) then -- &, |, ^ + self:NextToken() + local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() + if not rightConst then return false end + + if token == self.TOKEN.AND then return true,(leftPrecise and rightPrecise),self:BinaryAnd(leftValue,rightValue) end + if token == self.TOKEN.OR then return true,(leftPrecise and rightPrecise),self:BinaryOr (leftValue,rightValue) end + if token == self.TOKEN.XOR then return true,(leftPrecise and rightPrecise),self:BinaryXor(leftValue,rightValue) end + else + return true,leftPrecise,leftValue + end end --level0: = function HCOMP:ConstantExpression_Level0() - local leftConst,leftPrecise,leftValue = self:ConstantExpression_Level1() - if not leftConst then return false end - - if self:MatchToken(self.TOKEN.EQUAL) then -- = - return false - elseif self:MatchToken(self.TOKEN.LSS) then -- < - local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() - if not rightConst then return false end - - if leftValue < rightValue - then return true,(leftPrecise and rightPrecise),1 - else return true,(leftPrecise and rightPrecise),0 - end - elseif self:MatchToken(self.TOKEN.GTR) then -- > - local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() - if not rightConst then return false end - - if leftValue > rightValue - then return true,(leftPrecise and rightPrecise),1 - else return true,(leftPrecise and rightPrecise),0 - end - elseif self:MatchToken(self.TOKEN.LEQ) then -- <= - local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() - if not rightConst then return false end - - if leftValue <= rightValue - then return true,(leftPrecise and rightPrecise),1 - else return true,(leftPrecise and rightPrecise),0 - end - elseif self:MatchToken(self.TOKEN.GEQ) then -- >= - local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() - if not rightConst then return false end - - if leftValue >= rightValue - then return true,(leftPrecise and rightPrecise),1 - else return true,(leftPrecise and rightPrecise),0 - end - elseif self:MatchToken(self.TOKEN.EQL) then -- == - local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() - if not rightConst then return false end - - if leftValue == rightValue - then return true,(leftPrecise and rightPrecise),1 - else return true,(leftPrecise and rightPrecise),0 - end - elseif self:MatchToken(self.TOKEN.NEQ) then -- != - local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() - if not rightConst then return false end - - if leftValue ~= rightValue - then return true,(leftPrecise and rightPrecise),1 - else return true,(leftPrecise and rightPrecise),0 - end - elseif self:MatchToken(self.TOKEN.EQLADD) then -- += - local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() - if not rightConst then return false end - return true,(leftPrecise and rightPrecise),leftValue+rightValue - elseif self:MatchToken(self.TOKEN.EQLSUB) then -- -= - local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() - if not rightConst then return false end - return true,(leftPrecise and rightPrecise),leftValue-rightValue - elseif self:MatchToken(self.TOKEN.EQLMUL) then -- *= - local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() - if not rightConst then return false end - return true,(leftPrecise and rightPrecise),leftValue*rightValue - elseif self:MatchToken(self.TOKEN.EQLDIV) then -- /= - local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() - if not rightConst then return false end - return true,(leftPrecise and rightPrecise),leftValue/rightValue - elseif self:MatchToken(self.TOKEN.SHR) then -- >> - local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() - if not rightConst then return false end - return true,(leftPrecise and rightPrecise),self:BinarySHR(leftValue,rightValue) - elseif self:MatchToken(self.TOKEN.SHL) then -- << - local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() - if not rightConst then return false end - return true,(leftPrecise and rightPrecise),self:BinarySHL(leftValue,rightValue) - end - - return true,leftPrecise,leftValue + local leftConst,leftPrecise,leftValue = self:ConstantExpression_Level1() + if not leftConst then return false end + + if self:MatchToken(self.TOKEN.EQUAL) then -- = + return false + elseif self:MatchToken(self.TOKEN.LSS) then -- < + local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() + if not rightConst then return false end + + if leftValue < rightValue + then return true,(leftPrecise and rightPrecise),1 + else return true,(leftPrecise and rightPrecise),0 + end + elseif self:MatchToken(self.TOKEN.GTR) then -- > + local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() + if not rightConst then return false end + + if leftValue > rightValue + then return true,(leftPrecise and rightPrecise),1 + else return true,(leftPrecise and rightPrecise),0 + end + elseif self:MatchToken(self.TOKEN.LEQ) then -- <= + local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() + if not rightConst then return false end + + if leftValue <= rightValue + then return true,(leftPrecise and rightPrecise),1 + else return true,(leftPrecise and rightPrecise),0 + end + elseif self:MatchToken(self.TOKEN.GEQ) then -- >= + local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() + if not rightConst then return false end + + if leftValue >= rightValue + then return true,(leftPrecise and rightPrecise),1 + else return true,(leftPrecise and rightPrecise),0 + end + elseif self:MatchToken(self.TOKEN.EQL) then -- == + local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() + if not rightConst then return false end + + if leftValue == rightValue + then return true,(leftPrecise and rightPrecise),1 + else return true,(leftPrecise and rightPrecise),0 + end + elseif self:MatchToken(self.TOKEN.NEQ) then -- != + local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() + if not rightConst then return false end + + if leftValue ~= rightValue + then return true,(leftPrecise and rightPrecise),1 + else return true,(leftPrecise and rightPrecise),0 + end + elseif self:MatchToken(self.TOKEN.EQLADD) then -- += + local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() + if not rightConst then return false end + return true,(leftPrecise and rightPrecise),leftValue+rightValue + elseif self:MatchToken(self.TOKEN.EQLSUB) then -- -= + local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() + if not rightConst then return false end + return true,(leftPrecise and rightPrecise),leftValue-rightValue + elseif self:MatchToken(self.TOKEN.EQLMUL) then -- *= + local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() + if not rightConst then return false end + return true,(leftPrecise and rightPrecise),leftValue*rightValue + elseif self:MatchToken(self.TOKEN.EQLDIV) then -- /= + local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() + if not rightConst then return false end + return true,(leftPrecise and rightPrecise),leftValue/rightValue + elseif self:MatchToken(self.TOKEN.SHR) then -- >> + local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() + if not rightConst then return false end + return true,(leftPrecise and rightPrecise),self:BinarySHR(leftValue,rightValue) + elseif self:MatchToken(self.TOKEN.SHL) then -- << + local rightConst,rightPrecise,rightValue = self:ConstantExpression_Level0() + if not rightConst then return false end + return true,(leftPrecise and rightPrecise),self:BinarySHL(leftValue,rightValue) + end + + return true,leftPrecise,leftValue end -- Calculate constant expression and return expression function HCOMP:ConstantExpression(needResultNow,startLevel) - self:SaveParserState() - - local isConst,isPrecise,value - if startLevel == 3 then isConst,isPrecise,value = self:ConstantExpression_Level3() - elseif startLevel == 2 then isConst,isPrecise,value = self:ConstantExpression_Level2() - elseif startLevel == 1 then isConst,isPrecise,value = self:ConstantExpression_Level1() - else isConst,isPrecise,value = self:ConstantExpression_Level0() - end - - if isPrecise then - return true,value - else - if needResultNow then - self:RestoreParserState() - return false - end - - if isConst then - -- Return list of tokens that correspond to parsed expression - -- This is used to recalculate expression later - return true,nil,self:GetSavedTokens() - else - self:RestoreParserState() - return false - end - end + self:SaveParserState() + + local isConst,isPrecise,value + if startLevel == 3 then isConst,isPrecise,value = self:ConstantExpression_Level3() + elseif startLevel == 2 then isConst,isPrecise,value = self:ConstantExpression_Level2() + elseif startLevel == 1 then isConst,isPrecise,value = self:ConstantExpression_Level1() + else isConst,isPrecise,value = self:ConstantExpression_Level0() + end + + if isPrecise then + return true,value + else + if needResultNow then + self:RestoreParserState() + return false + end + + if isConst then + -- Return list of tokens that correspond to parsed expression + -- This is used to recalculate expression later + return true,nil,self:GetSavedTokens() + else + self:RestoreParserState() + return false + end + end end diff --git a/lua/wire/client/hlzasm/hc_opcodes.lua b/lua/wire/client/hlzasm/hc_opcodes.lua index b9d438926d..2af64cc9d0 100644 --- a/lua/wire/client/hlzasm/hc_opcodes.lua +++ b/lua/wire/client/hlzasm/hc_opcodes.lua @@ -12,9 +12,9 @@ HCOMP.OperandCount = {} local function buildMainLookup(instructions) - for _,instruction in pairs(instructions) do - HCOMP.OperandCount[instruction.Opcode] = instruction.OperandCount - end + for _,instruction in pairs(instructions) do + HCOMP.OperandCount[instruction.Opcode] = instruction.OperandCount + end end -- Initialize table of single-operand instructions which write 1st operand @@ -22,21 +22,21 @@ HCOMP.OpcodeWritesOperand = {} 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 + 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 = {} 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 + 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 @@ -44,14 +44,14 @@ HCOMP.OpcodeObsolete = {} HCOMP.OpcodeOld = {} 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 + 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) @@ -60,28 +60,28 @@ 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 - HCOMP:RemoveTokenizerOpcodes(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 + 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() + -- 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) diff --git a/lua/wire/client/hlzasm/hc_optimize.lua b/lua/wire/client/hlzasm/hc_optimize.lua index 64acbbda01..4a9eafd626 100644 --- a/lua/wire/client/hlzasm/hc_optimize.lua +++ b/lua/wire/client/hlzasm/hc_optimize.lua @@ -9,30 +9,30 @@ local OptimizationPattern = { -------------------------------------------------------------------------------- - {{{"sstack","A","B"}, - {"rstack","C","A"}, - }, - {{"sstack","A","B"}, - {"mov", "C","B"}, - }, - }, + {{{"sstack","A","B"}, + {"rstack","C","A"}, + }, + {{"sstack","A","B"}, + {"mov", "C","B"}, + }, + }, -------------------------------------------------------------------------------- - {{{"?1" ,"A","B"}, - {"sstack","C","D"}, - {"?2" ,"E","F"}, - {"sstack","C","D"}, - }, - {{"?1" ,"A","B"}, - {"?2" ,"E","F"}, - {"sstack","C","D"}, - }, - }, + {{{"?1" ,"A","B"}, + {"sstack","C","D"}, + {"?2" ,"E","F"}, + {"sstack","C","D"}, + }, + {{"?1" ,"A","B"}, + {"?2" ,"E","F"}, + {"sstack","C","D"}, + }, + }, -------------------------------------------------------------------------------- - {{{"mov","A","A"}, - }, - { - }, - } + {{{"mov","A","A"}, + }, + { + }, + } } @@ -41,12 +41,12 @@ local OptimizationPattern = { -------------------------------------------------------------------------------- -- Compare if two operands match local function CompareOperands(op1,op2) - return op1 and op2 and - (op1.Constant == op2.Constant) and - (op1.Register == op2.Register) and - (op1.Segment == op2.Segment) and - (op1.Memory == op2.Memory) and - (op1.MemoryPointer == op2.MemoryPointer) + return op1 and op2 and + (op1.Constant == op2.Constant) and + (op1.Register == op2.Register) and + (op1.Segment == op2.Segment) and + (op1.Memory == op2.Memory) and + (op1.MemoryPointer == op2.MemoryPointer) end @@ -55,82 +55,82 @@ end -------------------------------------------------------------------------------- -- Optimizes the generated code. Returns true if something was optimized function HCOMP:OptimizeCode() - -- For all opcodes - for index,opcode in ipairs(self.GeneratedCode) do - -- Check all optimization patterns - for _,pattern in pairs(OptimizationPattern) do - -- Check that pattern is long enough - if self.GeneratedCode[index+#pattern[1]-1] then - -- Check if pattern matches - local patternMatches = true - local temporaryOperands = {} - local temporaryOpcodes = {} - - -- Check all opcodes in pattern, continously - for i,matchPattern in ipairs(pattern[1]) do - local matchOpcode = matchPattern[1] - if not self.OpcodeNumber[matchPattern[1]] then - if not temporaryOpcodes[matchPattern[1]] then - temporaryOpcodes[matchPattern[1]] = self.GeneratedCode[index+i-1].Opcode - end - matchOpcode = temporaryOpcodes[matchPattern[1]] - end - - if (matchOpcode) and (self.GeneratedCode[index+i-1].Opcode == matchOpcode) then - local operand1 = self.GeneratedCode[index+i-1].Operands[1] - local operand2 = self.GeneratedCode[index+i-1].Operands[2] - - if matchPattern[2] and (not temporaryOperands[matchPattern[2]]) then - temporaryOperands[matchPattern[2]] = operand1 - end - if matchPattern[3] and (not temporaryOperands[matchPattern[3]]) then - temporaryOperands[matchPattern[3]] = operand2 - end - - -- Compare so operand "A" equals operand "A", etc - if matchPattern[2] and (not CompareOperands(operand1,temporaryOperands[matchPattern[2]])) then - patternMatches = false - end - if matchPattern[3] and (not CompareOperands(operand2,temporaryOperands[matchPattern[3]])) then - patternMatches = false - end - else - patternMatches = false - end - end - - if patternMatches then - -- If match found, delete all old entries - for i=1,#pattern[1] do - table.remove(self.GeneratedCode,index) - end - - -- Re-add the new ones - for i=1,#pattern[2] do - local tempOperands = {} - -- This will properly match letters with operands parsed when matching - if pattern[2][i][2] then tempOperands[1] = temporaryOperands[pattern[2][i][2]] end - if pattern[2][i][3] then tempOperands[2] = temporaryOperands[pattern[2][i][3]] end - - if self.OpcodeNumber[pattern[2][i][1]] then - table.insert(self.GeneratedCode,index+i-1, - { - Opcode = pattern[2][i][1], - Operands = tempOperands, - }) - else - table.insert(self.GeneratedCode,index+i-1, - { - Opcode = temporaryOpcodes[pattern[2][i][1]], - Operands = tempOperands, - }) - end - end - return true - end - end - end - end - - return false + -- For all opcodes + for index,opcode in ipairs(self.GeneratedCode) do + -- Check all optimization patterns + for _,pattern in pairs(OptimizationPattern) do + -- Check that pattern is long enough + if self.GeneratedCode[index+#pattern[1]-1] then + -- Check if pattern matches + local patternMatches = true + local temporaryOperands = {} + local temporaryOpcodes = {} + + -- Check all opcodes in pattern, continously + for i,matchPattern in ipairs(pattern[1]) do + local matchOpcode = matchPattern[1] + if not self.OpcodeNumber[matchPattern[1]] then + if not temporaryOpcodes[matchPattern[1]] then + temporaryOpcodes[matchPattern[1]] = self.GeneratedCode[index+i-1].Opcode + end + matchOpcode = temporaryOpcodes[matchPattern[1]] + end + + if (matchOpcode) and (self.GeneratedCode[index+i-1].Opcode == matchOpcode) then + local operand1 = self.GeneratedCode[index+i-1].Operands[1] + local operand2 = self.GeneratedCode[index+i-1].Operands[2] + + if matchPattern[2] and (not temporaryOperands[matchPattern[2]]) then + temporaryOperands[matchPattern[2]] = operand1 + end + if matchPattern[3] and (not temporaryOperands[matchPattern[3]]) then + temporaryOperands[matchPattern[3]] = operand2 + end + + -- Compare so operand "A" equals operand "A", etc + if matchPattern[2] and (not CompareOperands(operand1,temporaryOperands[matchPattern[2]])) then + patternMatches = false + end + if matchPattern[3] and (not CompareOperands(operand2,temporaryOperands[matchPattern[3]])) then + patternMatches = false + end + else + patternMatches = false + end + end + + if patternMatches then + -- If match found, delete all old entries + for i=1,#pattern[1] do + table.remove(self.GeneratedCode,index) + end + + -- Re-add the new ones + for i=1,#pattern[2] do + local tempOperands = {} + -- This will properly match letters with operands parsed when matching + if pattern[2][i][2] then tempOperands[1] = temporaryOperands[pattern[2][i][2]] end + if pattern[2][i][3] then tempOperands[2] = temporaryOperands[pattern[2][i][3]] end + + if self.OpcodeNumber[pattern[2][i][1]] then + table.insert(self.GeneratedCode,index+i-1, + { + Opcode = pattern[2][i][1], + Operands = tempOperands, + }) + else + table.insert(self.GeneratedCode,index+i-1, + { + Opcode = temporaryOpcodes[pattern[2][i][1]], + Operands = tempOperands, + }) + end + end + return true + end + end + end + end + + return false end diff --git a/lua/wire/client/hlzasm/hc_output.lua b/lua/wire/client/hlzasm/hc_output.lua index 7a3c70a502..e6c7c962b8 100644 --- a/lua/wire/client/hlzasm/hc_output.lua +++ b/lua/wire/client/hlzasm/hc_output.lua @@ -10,105 +10,105 @@ -------------------------------------------------------------------------------- -- Resolve a single block/opcode (offsets and labels in it) function HCOMP:Resolve(block) - -- Set offset for the block - block.Offset = self.WritePointer - - - -- Set pointer offset - block.PointerOffset = self.PointerOffset - - -- Label precedes the opcode and the data in the leaf - if block.Label then - if (self.Settings.SeparateDataSegment == true) and (block.Data) then - - else - block.Label.Value = self.WritePointer - end - end - - -- Account for the opcode generated by the block - if block.Opcode then - if not self.Settings.FixedSizeOutput then -- Variable-sized instructions - -- Write opcode - self.WritePointer = self.WritePointer + 1 - -- Write RM if more than 1 operand - if #block.Operands > 0 then self.WritePointer = self.WritePointer + 1 end - -- Write all segment prefixes and constant operands - for i=1,#block.Operands do - if block.Operands[i].Segment then self.WritePointer = self.WritePointer + 1 end - if block.Operands[i].Constant then self.WritePointer = self.WritePointer + 1 end - if block.Operands[i].MemoryPointer then self.WritePointer = self.WritePointer + 1 end - if block.Operands[i].Memory then self.WritePointer = self.WritePointer + 1 end - end - else -- Fixed-size instructions - self.WritePointer = self.WritePointer + 6 - end - -- Preventive setting up __PTR__ as constant number (const = currentOpcodeOffset + currentOpcodeSize) - for i=1,#block.Operands do - if istable(block.Operands[i].Constant) then - for j=1, #block.Operands[i].Constant do - if (block.Operands[i].Constant[j].Data == "__PTR__") then - block.Operands[i].Constant[j].Data = self.WritePointer - block.Operands[i].Constant[j].Type = self.TOKEN.NUMBER - end - end - end - end - end - - -- Account for extra data in the block - if block.Data then - if self.Settings.SeparateDataSegment == true then - for index,value in ipairs(block.Data) do - if isnumber(value)then -- Data is a number - self.DataPointer = self.DataPointer + 1 - elseif istable(value)then -- Data is a constant expression - self.WritePointer = self.DataPointer + 1 - else -- Data is a string - self.DataPointer = self.DataPointer + #value - end - end - else - for index,value in ipairs(block.Data) do - if isnumber(value)then -- Data is a number - self.WritePointer = self.WritePointer + 1 - elseif istable(value)then -- Data is a constant expression - self.WritePointer = self.WritePointer + 1 - else -- Data is a string - self.WritePointer = self.WritePointer + #value - end - end - end - end - - -- Zero padding after the block - if block.ZeroPadding then - if self.Settings.SeparateDataSegment == true then - - else - self.WritePointer = self.WritePointer + block.ZeroPadding - end - end - - -- Special marker to change write pointer - if block.SetWritePointer then - self.WritePointer = block.SetWritePointer - end - - -- Special marker to change pointer offset - if block.SetPointerOffset then - self.PointerOffset = block.SetPointerOffset - end - - -- Output the block if required - if self.Settings.OutputResolveListing then - self:PrintBlock(block,"rlist") - end - -- Output the block as library if required - if self.Settings.GenerateLibrary then + -- Set offset for the block + block.Offset = self.WritePointer + + + -- Set pointer offset + block.PointerOffset = self.PointerOffset + + -- Label precedes the opcode and the data in the leaf + if block.Label then + if (self.Settings.SeparateDataSegment == true) and (block.Data) then + + else + block.Label.Value = self.WritePointer + end + end + + -- Account for the opcode generated by the block + if block.Opcode then + if not self.Settings.FixedSizeOutput then -- Variable-sized instructions + -- Write opcode + self.WritePointer = self.WritePointer + 1 + -- Write RM if more than 1 operand + if #block.Operands > 0 then self.WritePointer = self.WritePointer + 1 end + -- Write all segment prefixes and constant operands + for i=1,#block.Operands do + if block.Operands[i].Segment then self.WritePointer = self.WritePointer + 1 end + if block.Operands[i].Constant then self.WritePointer = self.WritePointer + 1 end + if block.Operands[i].MemoryPointer then self.WritePointer = self.WritePointer + 1 end + if block.Operands[i].Memory then self.WritePointer = self.WritePointer + 1 end + end + else -- Fixed-size instructions + self.WritePointer = self.WritePointer + 6 + end + -- Preventive setting up __PTR__ as constant number (const = currentOpcodeOffset + currentOpcodeSize) + for i=1,#block.Operands do + if istable(block.Operands[i].Constant) then + for j=1, #block.Operands[i].Constant do + if (block.Operands[i].Constant[j].Data == "__PTR__") then + block.Operands[i].Constant[j].Data = self.WritePointer + block.Operands[i].Constant[j].Type = self.TOKEN.NUMBER + end + end + end + end + end + + -- Account for extra data in the block + if block.Data then + if self.Settings.SeparateDataSegment == true then + for index,value in ipairs(block.Data) do + if isnumber(value)then -- Data is a number + self.DataPointer = self.DataPointer + 1 + elseif istable(value)then -- Data is a constant expression + self.WritePointer = self.DataPointer + 1 + else -- Data is a string + self.DataPointer = self.DataPointer + #value + end + end + else + for index,value in ipairs(block.Data) do + if isnumber(value)then -- Data is a number + self.WritePointer = self.WritePointer + 1 + elseif istable(value)then -- Data is a constant expression + self.WritePointer = self.WritePointer + 1 + else -- Data is a string + self.WritePointer = self.WritePointer + #value + end + end + end + end + + -- Zero padding after the block + if block.ZeroPadding then + if self.Settings.SeparateDataSegment == true then + + else + self.WritePointer = self.WritePointer + block.ZeroPadding + end + end + + -- Special marker to change write pointer + if block.SetWritePointer then + self.WritePointer = block.SetWritePointer + end + + -- Special marker to change pointer offset + if block.SetPointerOffset then + self.PointerOffset = block.SetPointerOffset + end + + -- Output the block if required + if self.Settings.OutputResolveListing then + self:PrintBlock(block,"rlist") + end + -- Output the block as library if required + if self.Settings.GenerateLibrary then -- self:PrintBlock(block,"lib",true) - end - return false + end + return false end @@ -117,63 +117,63 @@ end -------------------------------------------------------------------------------- -- Output a single block to the output stream (for library mode) function HCOMP:OutputLibrary(block) - -- Write label - if block.Label then - if self.DBString then - self:PrintLine("lib",self.DBString) - self.DBString = nil - end - if not self.LabelLookup[block.Label.Name] then - self.LabelLookup[block.Label.Name] = "_"..self.LabelLookupCounter - self.LabelLookupCounter = self.LabelLookupCounter + 1 - end - self:PrintLine("lib",self.LabelLookup[block.Label.Name]..":") - end - - -- Resolve constant values in the block - if block.Opcode then - for i=1,#block.Operands do - if block.Operands[i].Constant and (not tonumber(block.Operands[i].Constant)) then - if block.PointerOffset ~= 0 - then block.Operands[i].Constant = self:PrintTokens(block.Operands[i].Constant).."+"..block.PointerOffset - else block.Operands[i].Constant = self:PrintTokens(block.Operands[i].Constant) - end - end - - if block.Operands[i].MemoryPointer and (not tonumber(block.Operands[i].MemoryPointer)) then - if block.PointerOffset ~= 0 - then block.Operands[i].MemoryPointer = self:PrintTokens(block.Operands[i].MemoryPointer).."+"..block.PointerOffset - else block.Operands[i].MemoryPointer = self:PrintTokens(block.Operands[i].MemoryPointer) - end - end - end - end - - -- Resolve constant values in data - if block.Data then - for index,value in ipairs(block.Data) do - if istable(value)then -- Data is a constant expression - block.Data[index] = self:PrintTokens(value) - end - end - end - - -- Write binary code - local tempWriteByte = self.WriteByte - self.WriteByte = function(self,value,block) - if not self.DBString then - self.DBString = "db "..value - else - if #self.DBString > 40 then - self:PrintLine("lib",self.DBString) - self.DBString = "db "..value - else - self.DBString = self.DBString..","..value - end - end - end - self:WriteBlock(block) - self.WriteByte = tempWriteByte + -- Write label + if block.Label then + if self.DBString then + self:PrintLine("lib",self.DBString) + self.DBString = nil + end + if not self.LabelLookup[block.Label.Name] then + self.LabelLookup[block.Label.Name] = "_"..self.LabelLookupCounter + self.LabelLookupCounter = self.LabelLookupCounter + 1 + end + self:PrintLine("lib",self.LabelLookup[block.Label.Name]..":") + end + + -- Resolve constant values in the block + if block.Opcode then + for i=1,#block.Operands do + if block.Operands[i].Constant and (not tonumber(block.Operands[i].Constant)) then + if block.PointerOffset ~= 0 + then block.Operands[i].Constant = self:PrintTokens(block.Operands[i].Constant).."+"..block.PointerOffset + else block.Operands[i].Constant = self:PrintTokens(block.Operands[i].Constant) + end + end + + if block.Operands[i].MemoryPointer and (not tonumber(block.Operands[i].MemoryPointer)) then + if block.PointerOffset ~= 0 + then block.Operands[i].MemoryPointer = self:PrintTokens(block.Operands[i].MemoryPointer).."+"..block.PointerOffset + else block.Operands[i].MemoryPointer = self:PrintTokens(block.Operands[i].MemoryPointer) + end + end + end + end + + -- Resolve constant values in data + if block.Data then + for index,value in ipairs(block.Data) do + if istable(value)then -- Data is a constant expression + block.Data[index] = self:PrintTokens(value) + end + end + end + + -- Write binary code + local tempWriteByte = self.WriteByte + self.WriteByte = function(self,value,block) + if not self.DBString then + self.DBString = "db "..value + else + if #self.DBString > 40 then + self:PrintLine("lib",self.DBString) + self.DBString = "db "..value + else + self.DBString = self.DBString..","..value + end + end + end + self:WriteBlock(block) + self.WriteByte = tempWriteByte end @@ -182,94 +182,94 @@ end -------------------------------------------------------------------------------- -- Output a single block to the output stream function HCOMP:Output(block) - -- Generate library output - if self.Settings.GenerateLibrary then - self:OutputLibrary(block) - return - end - -- Resolve constant values in the block - if block.Opcode then - for i=1,#block.Operands do - if block.Operands[i].Constant and (not tonumber(block.Operands[i].Constant)) then - -- Prepare expression to parse - self:RestoreParserState(block.Operands[i].Constant) - -- Try to parse the expression - local c,v = self:ConstantExpression(true) - if not c then - if (#block.Operands[i].Constant == 1) and - (block.Operands[i].Constant[1].Type == self.TOKEN.IDENT) then - self:Error("Undefined label or variable: "..block.Operands[i].Constant[1].Data,block) - elseif (#block.Operands[i].Constant == 2) and - (block.Operands[i].Constant[1].Type == self.TOKEN.AND) and - (block.Operands[i].Constant[2].Type == self.TOKEN.IDENT) then - self:Error("Undefined function or array: "..block.Operands[i].Constant[2].Data,block) - else - self:Error("Must be constant expression: "..self:PrintTokens(block.Operands[i].Constant),block) - end - end - -- Set the result - if v - then block.Operands[i].Constant = v + block.PointerOffset - else block.Operands[i].Constant = self.Settings.MagicValue - end - end - - if block.Operands[i].MemoryPointer and (not tonumber(block.Operands[i].MemoryPointer)) then - -- Prepare expression to parse - self:RestoreParserState(block.Operands[i].MemoryPointer) - -- Try to parse the expression - local c,v = self:ConstantExpression(true) - if not c then - if (#block.Operands[i].MemoryPointer == 1) and - (block.Operands[i].MemoryPointer[1].Type == self.TOKEN.IDENT) then - self:Error("Undefined label or variable: "..block.Operands[i].MemoryPointer[1].Data,block) - elseif (#block.Operands[i].MemoryPointer == 2) and - (block.Operands[i].MemoryPointer[1].Type == self.TOKEN.AND) and - (block.Operands[i].MemoryPointer[2].Type == self.TOKEN.IDENT) then - self:Error("Undefined function or array: "..block.Operands[i].MemoryPointer[2].Data,block) - else - self:Error("Must be constant expression: "..self:PrintTokens(block.Operands[i].MemoryPointer),block) - end - end - -- Set the result - if v - then block.Operands[i].MemoryPointer = v + block.PointerOffset - else block.Operands[i].MemoryPointer = self.Settings.MagicValue - end - end - end - end - - -- Resolve constant values in data - if block.Data then - for index,value in ipairs(block.Data) do - if istable(value)then -- Data is a constant expression - -- Prepare expression to parse - self:RestoreParserState(value) - -- Try to parse the expression - self.IgnoreStringInExpression = true - local c,v = self:ConstantExpression(true) - if not c then - if (#value == 1) and - (value[1].Type == self.TOKEN.IDENT) then - self:Error("Undefined label or variable: "..value[1].Data,block) - else - self:Error("Must be constant expression: "..self:PrintTokens(value),block) - end - end - self.IgnoreStringInExpression = false - -- Set the result - block.Data[index] = v or self.Settings.MagicValue - end - end - end - - - -- Output the block - if self.Settings.OutputFinalListing then - self:PrintBlock(block,"flist") - end - self:WriteBlock(block) + -- Generate library output + if self.Settings.GenerateLibrary then + self:OutputLibrary(block) + return + end + -- Resolve constant values in the block + if block.Opcode then + for i=1,#block.Operands do + if block.Operands[i].Constant and (not tonumber(block.Operands[i].Constant)) then + -- Prepare expression to parse + self:RestoreParserState(block.Operands[i].Constant) + -- Try to parse the expression + local c,v = self:ConstantExpression(true) + if not c then + if (#block.Operands[i].Constant == 1) and + (block.Operands[i].Constant[1].Type == self.TOKEN.IDENT) then + self:Error("Undefined label or variable: "..block.Operands[i].Constant[1].Data,block) + elseif (#block.Operands[i].Constant == 2) and + (block.Operands[i].Constant[1].Type == self.TOKEN.AND) and + (block.Operands[i].Constant[2].Type == self.TOKEN.IDENT) then + self:Error("Undefined function or array: "..block.Operands[i].Constant[2].Data,block) + else + self:Error("Must be constant expression: "..self:PrintTokens(block.Operands[i].Constant),block) + end + end + -- Set the result + if v + then block.Operands[i].Constant = v + block.PointerOffset + else block.Operands[i].Constant = self.Settings.MagicValue + end + end + + if block.Operands[i].MemoryPointer and (not tonumber(block.Operands[i].MemoryPointer)) then + -- Prepare expression to parse + self:RestoreParserState(block.Operands[i].MemoryPointer) + -- Try to parse the expression + local c,v = self:ConstantExpression(true) + if not c then + if (#block.Operands[i].MemoryPointer == 1) and + (block.Operands[i].MemoryPointer[1].Type == self.TOKEN.IDENT) then + self:Error("Undefined label or variable: "..block.Operands[i].MemoryPointer[1].Data,block) + elseif (#block.Operands[i].MemoryPointer == 2) and + (block.Operands[i].MemoryPointer[1].Type == self.TOKEN.AND) and + (block.Operands[i].MemoryPointer[2].Type == self.TOKEN.IDENT) then + self:Error("Undefined function or array: "..block.Operands[i].MemoryPointer[2].Data,block) + else + self:Error("Must be constant expression: "..self:PrintTokens(block.Operands[i].MemoryPointer),block) + end + end + -- Set the result + if v + then block.Operands[i].MemoryPointer = v + block.PointerOffset + else block.Operands[i].MemoryPointer = self.Settings.MagicValue + end + end + end + end + + -- Resolve constant values in data + if block.Data then + for index,value in ipairs(block.Data) do + if istable(value)then -- Data is a constant expression + -- Prepare expression to parse + self:RestoreParserState(value) + -- Try to parse the expression + self.IgnoreStringInExpression = true + local c,v = self:ConstantExpression(true) + if not c then + if (#value == 1) and + (value[1].Type == self.TOKEN.IDENT) then + self:Error("Undefined label or variable: "..value[1].Data,block) + else + self:Error("Must be constant expression: "..self:PrintTokens(value),block) + end + end + self.IgnoreStringInExpression = false + -- Set the result + block.Data[index] = v or self.Settings.MagicValue + end + end + end + + + -- Output the block + if self.Settings.OutputFinalListing then + self:PrintBlock(block,"flist") + end + self:WriteBlock(block) end @@ -323,135 +323,135 @@ HCOMP.SegmentRegisterName = RegisterName -------------------------------------------------------------------------------- -- Print a block in a readable format function HCOMP:PrintBlock(block,file,isLibrary) - -- Print corresponding label - if block.Label then - if (self.Settings.OutputLabelsInListing == true) or (isLibrary) then - self:PrintLine(file,block.Label.Name..":")-- /".."/ offset "..block.Label.Value) - end - end - - -- Print a comment - if block.Comment and (not isLibrary) then - self:PrintLine(file,"/".."/ "..block.Comment) - end - - -- Print the opcode - if block.Opcode then -- instruction - local printText = "" - if (self.Settings.OutputOffsetsInListing == true) and (not isLibrary) then - printText = printText .. string.format("%6d ",block.Offset) - else - printText = printText .. " " - end - printText = printText .. block.Opcode .. " " - - for i=1,#block.Operands do - if block.Operands[i].Segment then - printText = printText .. (SegmentRegisterName[block.Operands[i].Segment] or "???") .. ":" - end - - if block.Operands[i].Constant then - if istable(block.Operands[i].Constant) then - printText = printText .. self:PrintTokens(block.Operands[i].Constant) - else - printText = printText .. block.Operands[i].Constant - end - elseif block.Operands[i].MemoryPointer then - if istable(block.Operands[i].MemoryPointer) then - printText = printText .. "#" .. self:PrintTokens(block.Operands[i].MemoryPointer) - else - printText = printText .. "#" .. block.Operands[i].MemoryPointer - end - elseif block.Operands[i].Memory then - if not block.Operands[i].MemAddrOffset then - if block.Operands[i].Memory.CopySize then - if block.Operands[1].Memory and i ~= 1 then - if block.Operands[i].Memory.CopySize then - self:Warning("Operation will only use first byte of large variables on left and right, use &varname to get a pointer instead") - end - else - if not block.Operands[i].MemAddrOffset then - PrintTable(block.Operands[i]) - self:Warning("Operation will only use first byte of large variable on right, use &varname to get a pointer instead") - end - end - end - end - if istable(block.Operands[i].Memory) then - if block.Operands[i].Memory.Value - then - if block.Operands[i].MemAddrOffset then - printText = printText .. "#" .. block.Operands[i].Memory.Value + block.Operands[i].MemAddrOffset - else - printText = printText .. "#" .. block.Operands[i].Memory.Value - end - else printText = printText .. "#" .. block.Operands[i].Memory.Name - end - else - printText = printText .. "#" .. block.Operands[i].Memory - end - elseif block.Operands[i].MemoryRegister then - printText = printText .. "#" .. (RegisterName[block.Operands[i].MemoryRegister] or "???") - else - printText = printText .. (RegisterName[block.Operands[i].Register] or "???") - end - - if i < #block.Operands then printText = printText.."," end - end - - self:PrintLine(file,printText) - end - - -- Print the data - if block.Data then - local printText = "" - if (self.Settings.OutputOffsetsInListing == true) and (not isLibrary) then - printText = printText .. string.format("%6d db ",block.Offset) - else - printText = printText .. " db " - end - - for index,value in ipairs(block.Data) do - if isnumber(value)then -- Data is a number - printText = printText .. value - elseif istable(value)then -- Data is an expression - printText = printText .. self:PrintTokens(value) - else -- Data is a string - printText = printText .. "\"" .. value .. "\"" - end - if index < #block.Data then - printText = printText .. "," - end - end - self:PrintLine(file,printText) - end - - -- Add zero padding - if block.ZeroPadding and (block.ZeroPadding > 0) then - if (self.Settings.OutputOffsetsInListing == true) and (not isLibrary) then - self:PrintLine(file,string.format("%6d alloc %d",block.Offset,block.ZeroPadding)) - else - self:PrintLine(file,string.format("alloc %d",block.ZeroPadding)) - end - end - - -- Parse marker commands - if block.SetWritePointer then - if (self.Settings.OutputOffsetsInListing == true) and (not isLibrary) then - self:PrintLine(file,string.format("%6d org %d",block.Offset,block.SetWritePointer)) - else - self:PrintLine(file,string.format("org %d",block.SetWritePointer)) - end - end - - -- Parse marker commands - if block.SetPointerOffset then - if (self.Settings.OutputOffsetsInListing == true) and (not isLibrary) then - self:PrintLine(file,string.format("%6d offset %d",block.Offset,block.SetPointerOffset)) - else - self:PrintLine(file,string.format("offset %d",block.SetPointerOffset)) - end - end + -- Print corresponding label + if block.Label then + if (self.Settings.OutputLabelsInListing == true) or (isLibrary) then + self:PrintLine(file,block.Label.Name..":")-- /".."/ offset "..block.Label.Value) + end + end + + -- Print a comment + if block.Comment and (not isLibrary) then + self:PrintLine(file,"/".."/ "..block.Comment) + end + + -- Print the opcode + if block.Opcode then -- instruction + local printText = "" + if (self.Settings.OutputOffsetsInListing == true) and (not isLibrary) then + printText = printText .. string.format("%6d ",block.Offset) + else + printText = printText .. " " + end + printText = printText .. block.Opcode .. " " + + for i=1,#block.Operands do + if block.Operands[i].Segment then + printText = printText .. (SegmentRegisterName[block.Operands[i].Segment] or "???") .. ":" + end + + if block.Operands[i].Constant then + if istable(block.Operands[i].Constant) then + printText = printText .. self:PrintTokens(block.Operands[i].Constant) + else + printText = printText .. block.Operands[i].Constant + end + elseif block.Operands[i].MemoryPointer then + if istable(block.Operands[i].MemoryPointer) then + printText = printText .. "#" .. self:PrintTokens(block.Operands[i].MemoryPointer) + else + printText = printText .. "#" .. block.Operands[i].MemoryPointer + end + elseif block.Operands[i].Memory then + if not block.Operands[i].MemAddrOffset then + if block.Operands[i].Memory.CopySize then + if block.Operands[1].Memory and i ~= 1 then + if block.Operands[i].Memory.CopySize then + self:Warning("Operation will only use first byte of large variables on left and right, use &varname to get a pointer instead") + end + else + if not block.Operands[i].MemAddrOffset then + PrintTable(block.Operands[i]) + self:Warning("Operation will only use first byte of large variable on right, use &varname to get a pointer instead") + end + end + end + end + if istable(block.Operands[i].Memory) then + if block.Operands[i].Memory.Value + then + if block.Operands[i].MemAddrOffset then + printText = printText .. "#" .. block.Operands[i].Memory.Value + block.Operands[i].MemAddrOffset + else + printText = printText .. "#" .. block.Operands[i].Memory.Value + end + else printText = printText .. "#" .. block.Operands[i].Memory.Name + end + else + printText = printText .. "#" .. block.Operands[i].Memory + end + elseif block.Operands[i].MemoryRegister then + printText = printText .. "#" .. (RegisterName[block.Operands[i].MemoryRegister] or "???") + else + printText = printText .. (RegisterName[block.Operands[i].Register] or "???") + end + + if i < #block.Operands then printText = printText.."," end + end + + self:PrintLine(file,printText) + end + + -- Print the data + if block.Data then + local printText = "" + if (self.Settings.OutputOffsetsInListing == true) and (not isLibrary) then + printText = printText .. string.format("%6d db ",block.Offset) + else + printText = printText .. " db " + end + + for index,value in ipairs(block.Data) do + if isnumber(value)then -- Data is a number + printText = printText .. value + elseif istable(value)then -- Data is an expression + printText = printText .. self:PrintTokens(value) + else -- Data is a string + printText = printText .. "\"" .. value .. "\"" + end + if index < #block.Data then + printText = printText .. "," + end + end + self:PrintLine(file,printText) + end + + -- Add zero padding + if block.ZeroPadding and (block.ZeroPadding > 0) then + if (self.Settings.OutputOffsetsInListing == true) and (not isLibrary) then + self:PrintLine(file,string.format("%6d alloc %d",block.Offset,block.ZeroPadding)) + else + self:PrintLine(file,string.format("alloc %d",block.ZeroPadding)) + end + end + + -- Parse marker commands + if block.SetWritePointer then + if (self.Settings.OutputOffsetsInListing == true) and (not isLibrary) then + self:PrintLine(file,string.format("%6d org %d",block.Offset,block.SetWritePointer)) + else + self:PrintLine(file,string.format("org %d",block.SetWritePointer)) + end + end + + -- Parse marker commands + if block.SetPointerOffset then + if (self.Settings.OutputOffsetsInListing == true) and (not isLibrary) then + self:PrintLine(file,string.format("%6d offset %d",block.Offset,block.SetPointerOffset)) + else + self:PrintLine(file,string.format("offset %d",block.SetPointerOffset)) + end + end end @@ -460,77 +460,77 @@ end -------------------------------------------------------------------------------- -- Print a leaf in a readable format with specific nesting level function HCOMP:PrintLeaf(leaf,level) - -- Generate string for padding - if not level then level = 0 end - local pad = string.rep(" ",level) + -- Generate string for padding + if not level then level = 0 end + local pad = string.rep(" ",level) - if istable(leaf) then - if leaf.PreviousLeaf then + if istable(leaf) then + if leaf.PreviousLeaf then -- self:PrintLine("ctree",pad.."previous leaf:") - self:PrintLeaf(leaf.PreviousLeaf,level) - end - - if leaf.Opcode then - if leaf.Opcode == "LABEL" then - self:PrintLine("ctree",pad..leaf.Label.Name..": ("..string.lower(leaf.Label.Type)..")") - elseif leaf.Opcode == "DATA" then - if leaf.Label then - self:PrintLine("ctree",pad..leaf.Label.Name..": ("..string.lower(leaf.Label.Type)..")") - end - self:PrintLine("ctree",pad.." ["..(leaf.ZeroPadding or 0).." zero bytes, "..(#(leaf.Data or {})).." data bytes]") - elseif leaf.Opcode == "MARKER" then - self:PrintLine("ctree",pad.." [marker]") - else - self:PrintLine("ctree",pad..leaf.Opcode) - for i=1,#leaf.Operands do self:PrintLeaf(leaf.Operands[i],level+1) end - end - else - if leaf.Constant then - if istable(leaf.Constant) then - self:PrintLine("ctree",pad..self:PrintTokens(leaf.Constant)) - else - self:PrintLine("ctree",pad..leaf.Constant) - end - elseif leaf.Memory then - self:PrintLine("ctree",pad.."#"..leaf.Memory.Name) - elseif leaf.Register then - self:PrintLine("ctree",pad..RegisterName[leaf.Register]) - elseif leaf.MemoryRegister then - self:PrintLine("ctree",pad.."#"..RegisterName[leaf.MemoryRegister]) - elseif leaf.MemoryPointer then - if istable(leaf.MemoryPointer) then - if leaf.MemoryPointer.Opcode then - self:PrintLine("ctree",pad.."#[") - self:PrintLeaf(leaf.MemoryPointer,level+1) - self:PrintLine("ctree",pad.." ]") - else - self:PrintLine("ctree",pad.."#"..self:PrintTokens(leaf.MemoryPointer)) - end - else - self:PrintLine("ctree",pad.."#"..leaf.MemoryPointer) - end - elseif leaf.Stack then - if istable(leaf.Stack) then - self:PrintLine("ctree",pad.."stack[") - self:PrintLeaf(leaf.Stack,level+1) - self:PrintLine("ctree",pad.." ]") - else - self:PrintLine("ctree",pad.."stack["..leaf.Stack.."]") - end - elseif leaf.PointerToLabel then - self:PrintLine("ctree",pad.."&"..leaf.PointerToLabel.Name) - elseif leaf.TrigonometryHack then - self:PrintLine("ctree",pad.."(trigonometry hack)") - else - for k,v in pairs(leaf) do - print(k,v) - end - self:Error("Internal error 295") - end - end - else - self:Error("Internal error 229") - end + self:PrintLeaf(leaf.PreviousLeaf,level) + end + + if leaf.Opcode then + if leaf.Opcode == "LABEL" then + self:PrintLine("ctree",pad..leaf.Label.Name..": ("..string.lower(leaf.Label.Type)..")") + elseif leaf.Opcode == "DATA" then + if leaf.Label then + self:PrintLine("ctree",pad..leaf.Label.Name..": ("..string.lower(leaf.Label.Type)..")") + end + self:PrintLine("ctree",pad.." ["..(leaf.ZeroPadding or 0).." zero bytes, "..(#(leaf.Data or {})).." data bytes]") + elseif leaf.Opcode == "MARKER" then + self:PrintLine("ctree",pad.." [marker]") + else + self:PrintLine("ctree",pad..leaf.Opcode) + for i=1,#leaf.Operands do self:PrintLeaf(leaf.Operands[i],level+1) end + end + else + if leaf.Constant then + if istable(leaf.Constant) then + self:PrintLine("ctree",pad..self:PrintTokens(leaf.Constant)) + else + self:PrintLine("ctree",pad..leaf.Constant) + end + elseif leaf.Memory then + self:PrintLine("ctree",pad.."#"..leaf.Memory.Name) + elseif leaf.Register then + self:PrintLine("ctree",pad..RegisterName[leaf.Register]) + elseif leaf.MemoryRegister then + self:PrintLine("ctree",pad.."#"..RegisterName[leaf.MemoryRegister]) + elseif leaf.MemoryPointer then + if istable(leaf.MemoryPointer) then + if leaf.MemoryPointer.Opcode then + self:PrintLine("ctree",pad.."#[") + self:PrintLeaf(leaf.MemoryPointer,level+1) + self:PrintLine("ctree",pad.." ]") + else + self:PrintLine("ctree",pad.."#"..self:PrintTokens(leaf.MemoryPointer)) + end + else + self:PrintLine("ctree",pad.."#"..leaf.MemoryPointer) + end + elseif leaf.Stack then + if istable(leaf.Stack) then + self:PrintLine("ctree",pad.."stack[") + self:PrintLeaf(leaf.Stack,level+1) + self:PrintLine("ctree",pad.." ]") + else + self:PrintLine("ctree",pad.."stack["..leaf.Stack.."]") + end + elseif leaf.PointerToLabel then + self:PrintLine("ctree",pad.."&"..leaf.PointerToLabel.Name) + elseif leaf.TrigonometryHack then + self:PrintLine("ctree",pad.."(trigonometry hack)") + else + for k,v in pairs(leaf) do + print(k,v) + end + self:Error("Internal error 295") + end + end + else + self:Error("Internal error 229") + end end @@ -539,56 +539,56 @@ end -------------------------------------------------------------------------------- -- Generate RM for an operand function HCOMP:OperandRM(operand,block) - if operand.Constant then - if not operand.Segment then - operand.Value = operand.Constant - return 0 - else - operand.Value = operand.Constant - return 50 - end - elseif operand.Memory then - if istable(operand.Memory) then -- label - if operand.MemAddrOffset then - operand.Value = operand.Memory.Value + operand.MemAddrOffset - else - operand.Value = operand.Memory.Value - end - else -- constant - operand.Value = operand.Memory - end - return 25 - elseif operand.MemoryPointer then - operand.Value = operand.MemoryPointer - return 25 - elseif operand.Register then - if not operand.Segment then - if (operand.Register >= 1) and (operand.Register <= 8) then return operand.Register end - if (operand.Register >= 16) and (operand.Register <= 23) then return operand.Register-16+9 end - if (operand.Register >= 1024) and (operand.Register < 2048) then return operand.Register-1024+1000 end - if (operand.Register >= 96) and (operand.Register <= 127) then return operand.Register-96+2048 end - else - if (operand.Register >= 1) and (operand.Register <= 8) then return operand.Register+26 end - if (operand.Register >= 16) and (operand.Register <= 23) then - self:Error("Invalid instruction operand (cannot use segment prefix for segment register access)",block) - end - if (operand.Register >= 1024) and (operand.Register < 2048) then - self:Error("Invalid instruction operand (cannot use segment prefix for port access)",block) - end - if (operand.Register >= 96) and (operand.Register <= 127) then return operand.Register-96+2112 end - end - elseif operand.MemoryRegister then - if (operand.MemoryRegister >= 1) and (operand.MemoryRegister <= 8) then return operand.MemoryRegister+16 end - if (operand.MemoryRegister >= 16) and (operand.MemoryRegister <= 23) then - self:Error("Invalid instruction operand (cannot use segment register for memory access)",block) - end - if (operand.MemoryRegister >= 1024) and (operand.MemoryRegister < 2048) then - self:Error("Invalid instruction operand (cannot use port for memory access)",block) - end - if (operand.MemoryRegister >= 96) and (operand.MemoryRegister <= 127) then return operand.MemoryRegister-96+2080 end - end - - self:Error("Invalid instruction operand",block) + if operand.Constant then + if not operand.Segment then + operand.Value = operand.Constant + return 0 + else + operand.Value = operand.Constant + return 50 + end + elseif operand.Memory then + if istable(operand.Memory) then -- label + if operand.MemAddrOffset then + operand.Value = operand.Memory.Value + operand.MemAddrOffset + else + operand.Value = operand.Memory.Value + end + else -- constant + operand.Value = operand.Memory + end + return 25 + elseif operand.MemoryPointer then + operand.Value = operand.MemoryPointer + return 25 + elseif operand.Register then + if not operand.Segment then + if (operand.Register >= 1) and (operand.Register <= 8) then return operand.Register end + if (operand.Register >= 16) and (operand.Register <= 23) then return operand.Register-16+9 end + if (operand.Register >= 1024) and (operand.Register < 2048) then return operand.Register-1024+1000 end + if (operand.Register >= 96) and (operand.Register <= 127) then return operand.Register-96+2048 end + else + if (operand.Register >= 1) and (operand.Register <= 8) then return operand.Register+26 end + if (operand.Register >= 16) and (operand.Register <= 23) then + self:Error("Invalid instruction operand (cannot use segment prefix for segment register access)",block) + end + if (operand.Register >= 1024) and (operand.Register < 2048) then + self:Error("Invalid instruction operand (cannot use segment prefix for port access)",block) + end + if (operand.Register >= 96) and (operand.Register <= 127) then return operand.Register-96+2112 end + end + elseif operand.MemoryRegister then + if (operand.MemoryRegister >= 1) and (operand.MemoryRegister <= 8) then return operand.MemoryRegister+16 end + if (operand.MemoryRegister >= 16) and (operand.MemoryRegister <= 23) then + self:Error("Invalid instruction operand (cannot use segment register for memory access)",block) + end + if (operand.MemoryRegister >= 1024) and (operand.MemoryRegister < 2048) then + self:Error("Invalid instruction operand (cannot use port for memory access)",block) + end + if (operand.MemoryRegister >= 96) and (operand.MemoryRegister <= 127) then return operand.MemoryRegister-96+2080 end + end + + self:Error("Invalid instruction operand",block) end @@ -597,77 +597,77 @@ end -------------------------------------------------------------------------------- -- Write the block to output stream function HCOMP:WriteBlock(block) - -- Write the opcode - if block.Opcode then - if not self.OpcodeNumber[block.Opcode] then - self:Error("Undefined opcode: "..block.Opcode,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) - if #block.Operands > 1 then RM = RM + self:OperandRM(block.Operands[2],block)*10000 end - end - - -- Generate segment offset marker - if (#block.Operands > 0) and (block.Operands[1].Segment) then Opcode = Opcode + 1000 end - if (#block.Operands > 1) and (block.Operands[2].Segment) then Opcode = Opcode + 10000 end - - if not self.Settings.FixedSizeOutput then -- Variable-size instructions - -- Write opcode - self:WriteByte(Opcode,block,negativeOp) - -- Write RM - if RM then self:WriteByte(RM,block) end - - -- Write segment offsets - if (#block.Operands > 0) and (block.Operands[1].Segment) then self:WriteByte(block.Operands[1].Segment,block) end - if (#block.Operands > 1) and (block.Operands[2].Segment) then self:WriteByte(block.Operands[2].Segment,block) end - - -- Write immediate bytes - if (#block.Operands > 0) and (block.Operands[1].Value) then self:WriteByte(block.Operands[1].Value,block) end - 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,negativeOp) - - -- Write RM - self:WriteByte(RM or 0,block) - - -- Write segment offsets - if #block.Operands > 0 then self:WriteByte(block.Operands[1].Segment or -4,block) else self:WriteByte(-4,block) end - if #block.Operands > 1 then self:WriteByte(block.Operands[2].Segment or -4,block) else self:WriteByte(-4,block) end - - -- Write immediate bytes - if #block.Operands > 0 then self:WriteByte(block.Operands[1].Value or 0,block) else self:WriteByte(0,block) end - if #block.Operands > 1 then self:WriteByte(block.Operands[2].Value or 0,block) else self:WriteByte(0,block) end - end - end - - -- Write the data - if block.Data then - for index,value in ipairs(block.Data) do - if isnumber(value) then -- Data is a number - self:WriteByte(value,block) - else -- Data is a string - for charIdx=1,#value do - self:WriteByte(string.byte(value,charIdx),block) - end - end - end - end - - -- Write zero padding - if block.ZeroPadding then - for i=1,block.ZeroPadding do self:WriteByte(0,block) end - end - - -- Set write pointer - if block.SetWritePointer then - self.WritePointer = block.SetWritePointer - end + -- Write the opcode + if block.Opcode then + if not self.OpcodeNumber[block.Opcode] then + self:Error("Undefined opcode: "..block.Opcode,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) + if #block.Operands > 1 then RM = RM + self:OperandRM(block.Operands[2],block)*10000 end + end + + -- Generate segment offset marker + if (#block.Operands > 0) and (block.Operands[1].Segment) then Opcode = Opcode + 1000 end + if (#block.Operands > 1) and (block.Operands[2].Segment) then Opcode = Opcode + 10000 end + + if not self.Settings.FixedSizeOutput then -- Variable-size instructions + -- Write opcode + self:WriteByte(Opcode,block,negativeOp) + -- Write RM + if RM then self:WriteByte(RM,block) end + + -- Write segment offsets + if (#block.Operands > 0) and (block.Operands[1].Segment) then self:WriteByte(block.Operands[1].Segment,block) end + if (#block.Operands > 1) and (block.Operands[2].Segment) then self:WriteByte(block.Operands[2].Segment,block) end + + -- Write immediate bytes + if (#block.Operands > 0) and (block.Operands[1].Value) then self:WriteByte(block.Operands[1].Value,block) end + 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,negativeOp) + + -- Write RM + self:WriteByte(RM or 0,block) + + -- Write segment offsets + if #block.Operands > 0 then self:WriteByte(block.Operands[1].Segment or -4,block) else self:WriteByte(-4,block) end + if #block.Operands > 1 then self:WriteByte(block.Operands[2].Segment or -4,block) else self:WriteByte(-4,block) end + + -- Write immediate bytes + if #block.Operands > 0 then self:WriteByte(block.Operands[1].Value or 0,block) else self:WriteByte(0,block) end + if #block.Operands > 1 then self:WriteByte(block.Operands[2].Value or 0,block) else self:WriteByte(0,block) end + end + end + + -- Write the data + if block.Data then + for index,value in ipairs(block.Data) do + if isnumber(value) then -- Data is a number + self:WriteByte(value,block) + else -- Data is a string + for charIdx=1,#value do + self:WriteByte(string.byte(value,charIdx),block) + end + end + end + end + + -- Write zero padding + if block.ZeroPadding then + for i=1,block.ZeroPadding do self:WriteByte(0,block) end + end + + -- Set write pointer + if block.SetWritePointer then + self.WritePointer = block.SetWritePointer + end end diff --git a/lua/wire/client/hlzasm/hc_preprocess.lua b/lua/wire/client/hlzasm/hc_preprocess.lua index fb89615938..8aa2f9d2aa 100644 --- a/lua/wire/client/hlzasm/hc_preprocess.lua +++ b/lua/wire/client/hlzasm/hc_preprocess.lua @@ -9,17 +9,17 @@ -------------------------------------------------------------------------------- -- Load file function HCOMP:LoadFile(filename) - return file.Read("data/"..self.Location.."/"..filename, "GAME") -- So we also get /addons/wire/data/ + return file.Read("data/"..self.Location.."/"..filename, "GAME") -- So we also get /addons/wire/data/ end -- Save file function HCOMP:SaveFile(filename,text) - file.Write(self.Location.."/"..filename,text) + file.Write(self.Location.."/"..filename,text) end -- Trim spaces at string sides local function trimString(str) - return string.gsub(str, "^%s*(.-)%s*$", "%1") + return string.gsub(str, "^%s*(.-)%s*$", "%1") end @@ -28,246 +28,246 @@ end -------------------------------------------------------------------------------- -- Handle preprocessor macro function HCOMP:ParsePreprocessMacro(lineText,macroPosition) - -- Trim spaces - local macroLine = trimString(lineText) + -- Trim spaces + local macroLine = trimString(lineText) - -- Find out macro name and parameters - local macroNameEnd = (string.find(macroLine," ") or 0) - local macroName = trimString(string.sub(macroLine,2,macroNameEnd-1)) - local macroParameters = trimString(string.sub(macroLine,macroNameEnd+1)) + -- Find out macro name and parameters + local macroNameEnd = (string.find(macroLine," ") or 0) + local macroName = trimString(string.sub(macroLine,2,macroNameEnd-1)) + local macroParameters = trimString(string.sub(macroLine,macroNameEnd+1)) - -- Stop parsing macros inside of a failed ifdef/ifndef - if self.SkipToEndIf then - if macroName == "else" then - if self.EndIfsToSkip == 0 then - self.SkipToEndIf = false - return self:ParsePreprocessMacro(lineText,macroPosition) -- Rerun function to parse else correctly - end - end - if macroName == "endif" then - if self.EndIfsToSkip > 0 then - self.EndIfsToSkip = self.EndIfsToSkip - 1 - else - self.SkipToEndIf = false - return self:ParsePreprocessMacro(lineText,macroPosition) -- Rerun function to parse endif correctly - end - end - if macroName == "ifdef" or macroName == "ifndef" then - self.EndIfsToSkip = self.EndIfsToSkip + 1 - end - local InComment = false - -- If this while loop hits end of file before #endif it won't produce an error, seems like the original behavior for ifdefs - while self:getChar() ~= "" do - if self:getChar() == '/' and not InComment then - self:nextChar() - if self:getChar() == '*' then - self:nextChar() - InComment = true - end - if self:getChar() == '*' then - self:nextChar() - if self:getChar() == '/' then - self:nextChar() - InComment = false - end - end - end - if (self.Code[1].Col == 1) and (self:getChar() == "#") and not InComment then - self.Code[1].NextCharPos = self.Code[1].NextCharPos - 1 -- Exit to let tokenizer handle from here - break - end - self:nextChar() - end - return - end + -- Stop parsing macros inside of a failed ifdef/ifndef + if self.SkipToEndIf then + if macroName == "else" then + if self.EndIfsToSkip == 0 then + self.SkipToEndIf = false + return self:ParsePreprocessMacro(lineText,macroPosition) -- Rerun function to parse else correctly + end + end + if macroName == "endif" then + if self.EndIfsToSkip > 0 then + self.EndIfsToSkip = self.EndIfsToSkip - 1 + else + self.SkipToEndIf = false + return self:ParsePreprocessMacro(lineText,macroPosition) -- Rerun function to parse endif correctly + end + end + if macroName == "ifdef" or macroName == "ifndef" then + self.EndIfsToSkip = self.EndIfsToSkip + 1 + end + local InComment = false + -- If this while loop hits end of file before #endif it won't produce an error, seems like the original behavior for ifdefs + while self:getChar() ~= "" do + if self:getChar() == '/' and not InComment then + self:nextChar() + if self:getChar() == '*' then + self:nextChar() + InComment = true + end + if self:getChar() == '*' then + self:nextChar() + if self:getChar() == '/' then + self:nextChar() + InComment = false + end + end + end + if (self.Code[1].Col == 1) and (self:getChar() == "#") and not InComment then + self.Code[1].NextCharPos = self.Code[1].NextCharPos - 1 -- Exit to let tokenizer handle from here + break + end + self:nextChar() + end + return + end - if macroName == "pragma" then - local pragmaName = string.lower(trimString(string.sub(macroParameters,1,(string.find(macroParameters," ") or 0)-1))) - local pragmaCommand = trimString(string.sub(macroParameters,(string.find(macroParameters," ") or 0)+1)) + if macroName == "pragma" then + local pragmaName = string.lower(trimString(string.sub(macroParameters,1,(string.find(macroParameters," ") or 0)-1))) + local pragmaCommand = trimString(string.sub(macroParameters,(string.find(macroParameters," ") or 0)+1)) - if pragmaName == "set" then - local entryName = trimString(string.sub(pragmaCommand,1,(string.find(pragmaCommand," ") or 0)-1)) - local entryValue = trimString(string.sub(pragmaCommand,(string.find(pragmaCommand," ") or 0)+1)) + if pragmaName == "set" then + local entryName = trimString(string.sub(pragmaCommand,1,(string.find(pragmaCommand," ") or 0)-1)) + local entryValue = trimString(string.sub(pragmaCommand,(string.find(pragmaCommand," ") or 0)+1)) - if entryValue == "true" then - self.Settings[entryName] = true - elseif entryValue == "false" then - self.Settings[entryName] = false - else - self.Settings[entryName] = tonumber(entryValue) or entryValue - end - elseif pragmaName == "language" then - if string.lower(pragmaCommand) == "hlzasm" then self.Settings.CurrentLanguage = "HLZASM" end - if string.lower(pragmaCommand) == "zasm" then self.Settings.CurrentLanguage = "ZASM" end - elseif pragmaName == "crt" then - local crtFilename = "lib\\"..string.lower(pragmaCommand).."\\init.txt" - local fileText = self:LoadFile(crtFilename) - if fileText then - table.insert(self.Code, 1, { Text = fileText, Line = 1, Col = 1, File = crtFilename, ParentFile = macroPosition.File, NextCharPos = 1 }) - else - self:Error("Unable to include CRT library "..pragmaCommand, - macroPosition.Line,macroPosition.Col,macroPosition.File) - end + if entryValue == "true" then + self.Settings[entryName] = true + elseif entryValue == "false" then + self.Settings[entryName] = false + else + self.Settings[entryName] = tonumber(entryValue) or entryValue + end + elseif pragmaName == "language" then + if string.lower(pragmaCommand) == "hlzasm" then self.Settings.CurrentLanguage = "HLZASM" end + if string.lower(pragmaCommand) == "zasm" then self.Settings.CurrentLanguage = "ZASM" end + elseif pragmaName == "crt" then + local crtFilename = "lib\\"..string.lower(pragmaCommand).."\\init.txt" + local fileText = self:LoadFile(crtFilename) + if fileText then + table.insert(self.Code, 1, { Text = fileText, Line = 1, Col = 1, File = crtFilename, ParentFile = macroPosition.File, NextCharPos = 1 }) + else + self:Error("Unable to include CRT library "..pragmaCommand, + macroPosition.Line,macroPosition.Col,macroPosition.File) + end - self.Defines[string.upper(pragmaCommand)] = "" - table.insert(self.SearchPaths,"lib\\"..string.lower(pragmaCommand)) - elseif pragmaName == "cpuname" then - CPULib.CPUName = pragmaCommand - elseif pragmaName == "searchpath" then - table.insert(self.SearchPaths,pragmaCommand) - elseif pragmaName == "silence" or pragmaName == "mute" then - if pragmaCommand == "self" then - self.SilencedFiles[macroPosition.File] = { Silenced = true, FromParent = false } - elseif pragmaCommand == "includes" or pragmaCommand == "other" then - self.SilencedParents[macroPosition.File] = true - end - elseif pragmaName == "allow" or pragmaName == "zap" then - if not self.Settings.AutoBusyRegisters then - self.Settings.AutoBusyRegisters = true - end - local StartRegister, EndRegister = string.match(macroParameters, "([^,%s]+)%s*,%s*([^,%s]+)") - if StartRegister == nil then - self:Error("Missing register range argument", - macroPosition.Line,macroPosition.Col,macroPosition.File) - end - local StartInd,EndInd = -1,-1 - for ind,reg in ipairs(self.RegisterName) do - if reg == StartRegister then - StartInd = ind - end - if reg == EndRegister then - EndInd = ind - break - end - end - if StartInd ~= -1 and EndInd ~= -1 then - table.insert(self.Settings.AutoBusyRegisterRanges,{false,StartInd,EndInd}) - else - self:Error(StartRegister .. " to " .. EndRegister .. " is an invalid range!", - macroPosition.Line,macroPosition.Col,macroPosition.File) - end - elseif pragmaName == "disallow" or pragmaName == "preserve" then - if not self.Settings.AutoBusyRegisters then - self.Settings.AutoBusyRegisters = true - end - local StartRegister, EndRegister = string.match(macroParameters, "([^,%s]+)%s*,%s*([^,%s]+)") - if StartRegister == nil then - self:Error("Missing register range argument", - macroPosition.Line,macroPosition.Col,macroPosition.File) - end - local StartInd,EndInd = -1,-1 - for ind,reg in ipairs(self.RegisterName) do - if reg == StartRegister then - StartInd = ind - end - if reg == EndRegister then - EndInd = ind - break - end - end - if StartInd ~= -1 and EndInd ~= -1 then - table.insert(self.Settings.AutoBusyRegisterRanges,{true,StartInd,EndInd}) - else - self:Error(StartRegister .. " to " .. EndRegister .. " is an invalid range!", - macroPosition.Line,macroPosition.Col,macroPosition.File) - end - end - elseif macroName == "define" then -- #define - local defineName = trimString(string.sub(macroParameters,1,(string.find(macroParameters," ") or 0)-1)) - local defineValue = string.sub(macroParameters,(string.find(macroParameters," ") or 0)+1) - if tonumber(defineName) then - self:Error("Bad idea to redefine numbers", - macroPosition.Line,macroPosition.Col,macroPosition.File) - end - self.Defines[defineName] = defineValue - elseif macroName == "undef" then -- #undef - local defineName = trimString(string.sub(macroParameters,1,(string.find(macroParameters," ") or 0)-1)) - if tonumber(defineName) then - self:Error("Bad idea to undefine numbers", - macroPosition.Line,macroPosition.Col,macroPosition.File) - end + self.Defines[string.upper(pragmaCommand)] = "" + table.insert(self.SearchPaths,"lib\\"..string.lower(pragmaCommand)) + elseif pragmaName == "cpuname" then + CPULib.CPUName = pragmaCommand + elseif pragmaName == "searchpath" then + table.insert(self.SearchPaths,pragmaCommand) + elseif pragmaName == "silence" or pragmaName == "mute" then + if pragmaCommand == "self" then + self.SilencedFiles[macroPosition.File] = { Silenced = true, FromParent = false } + elseif pragmaCommand == "includes" or pragmaCommand == "other" then + self.SilencedParents[macroPosition.File] = true + end + elseif pragmaName == "allow" or pragmaName == "zap" then + if not self.Settings.AutoBusyRegisters then + self.Settings.AutoBusyRegisters = true + end + local StartRegister, EndRegister = string.match(macroParameters, "([^,%s]+)%s*,%s*([^,%s]+)") + if StartRegister == nil then + self:Error("Missing register range argument", + macroPosition.Line,macroPosition.Col,macroPosition.File) + end + local StartInd,EndInd = -1,-1 + for ind,reg in ipairs(self.RegisterName) do + if reg == StartRegister then + StartInd = ind + end + if reg == EndRegister then + EndInd = ind + break + end + end + if StartInd ~= -1 and EndInd ~= -1 then + table.insert(self.Settings.AutoBusyRegisterRanges,{false,StartInd,EndInd}) + else + self:Error(StartRegister .. " to " .. EndRegister .. " is an invalid range!", + macroPosition.Line,macroPosition.Col,macroPosition.File) + end + elseif pragmaName == "disallow" or pragmaName == "preserve" then + if not self.Settings.AutoBusyRegisters then + self.Settings.AutoBusyRegisters = true + end + local StartRegister, EndRegister = string.match(macroParameters, "([^,%s]+)%s*,%s*([^,%s]+)") + if StartRegister == nil then + self:Error("Missing register range argument", + macroPosition.Line,macroPosition.Col,macroPosition.File) + end + local StartInd,EndInd = -1,-1 + for ind,reg in ipairs(self.RegisterName) do + if reg == StartRegister then + StartInd = ind + end + if reg == EndRegister then + EndInd = ind + break + end + end + if StartInd ~= -1 and EndInd ~= -1 then + table.insert(self.Settings.AutoBusyRegisterRanges,{true,StartInd,EndInd}) + else + self:Error(StartRegister .. " to " .. EndRegister .. " is an invalid range!", + macroPosition.Line,macroPosition.Col,macroPosition.File) + end + end + elseif macroName == "define" then -- #define + local defineName = trimString(string.sub(macroParameters,1,(string.find(macroParameters," ") or 0)-1)) + local defineValue = string.sub(macroParameters,(string.find(macroParameters," ") or 0)+1) + if tonumber(defineName) then + self:Error("Bad idea to redefine numbers", + macroPosition.Line,macroPosition.Col,macroPosition.File) + end + self.Defines[defineName] = defineValue + elseif macroName == "undef" then -- #undef + local defineName = trimString(string.sub(macroParameters,1,(string.find(macroParameters," ") or 0)-1)) + if tonumber(defineName) then + self:Error("Bad idea to undefine numbers", + macroPosition.Line,macroPosition.Col,macroPosition.File) + end self.Defines[defineName] = nil - elseif macroName == "ifdef" then -- #ifdef - local defineName = trimString(string.sub(macroParameters,1,(string.find(macroParameters," ") or 0)-1)) - if self.Defines[defineName] then - self.IFDEFLevel[#self.IFDEFLevel+1] = false - else - if self.Settings.NewIfDefs then - self.SkipToEndIf = true - self.EndIfsToSkip = 0 - end - self.IFDEFLevel[#self.IFDEFLevel+1] = true - end - elseif macroName == "ifndef" then -- #ifndef - local defineName = trimString(string.sub(macroParameters,1,(string.find(macroParameters," ") or 0)-1)) - if not self.Defines[defineName] then - self.IFDEFLevel[#self.IFDEFLevel+1] = false - else - if self.Settings.NewIfDefs then - self.SkipToEndIf = true - self.EndIfsToSkip = 0 - end - self.IFDEFLevel[#self.IFDEFLevel+1] = true - end - elseif macroName == "else" then -- #else - if #self.IFDEFLevel == 0 then - self:Error("Unexpected #else macro", - macroPosition.Line,macroPosition.Col,macroPosition.File) - end + elseif macroName == "ifdef" then -- #ifdef + local defineName = trimString(string.sub(macroParameters,1,(string.find(macroParameters," ") or 0)-1)) + if self.Defines[defineName] then + self.IFDEFLevel[#self.IFDEFLevel+1] = false + else + if self.Settings.NewIfDefs then + self.SkipToEndIf = true + self.EndIfsToSkip = 0 + end + self.IFDEFLevel[#self.IFDEFLevel+1] = true + end + elseif macroName == "ifndef" then -- #ifndef + local defineName = trimString(string.sub(macroParameters,1,(string.find(macroParameters," ") or 0)-1)) + if not self.Defines[defineName] then + self.IFDEFLevel[#self.IFDEFLevel+1] = false + else + if self.Settings.NewIfDefs then + self.SkipToEndIf = true + self.EndIfsToSkip = 0 + end + self.IFDEFLevel[#self.IFDEFLevel+1] = true + end + elseif macroName == "else" then -- #else + if #self.IFDEFLevel == 0 then + self:Error("Unexpected #else macro", + macroPosition.Line,macroPosition.Col,macroPosition.File) + end - self.IFDEFLevel[#self.IFDEFLevel] = not self.IFDEFLevel[#self.IFDEFLevel] - elseif macroName == "endif" then -- #endif - if #self.IFDEFLevel == 0 then - self:Error("Unexpected #endif macro", - macroPosition.Line,macroPosition.Col,macroPosition.File) - end + self.IFDEFLevel[#self.IFDEFLevel] = not self.IFDEFLevel[#self.IFDEFLevel] + elseif macroName == "endif" then -- #endif + if #self.IFDEFLevel == 0 then + self:Error("Unexpected #endif macro", + macroPosition.Line,macroPosition.Col,macroPosition.File) + end - self.IFDEFLevel[#self.IFDEFLevel] = nil - elseif (macroName == "include") or - (macroName == "#include##") then -- #include or ZASM2 compatible ##include## - local symL,symR - local fileName + self.IFDEFLevel[#self.IFDEFLevel] = nil + elseif (macroName == "include") or + (macroName == "#include##") then -- #include or ZASM2 compatible ##include## + local symL,symR + local fileName - -- ZASM2 compatibility syntax support - if macroName == "#include##" then - symL,symR = "<",">" - fileName = trimString(string.sub(macroParameters,1,-1)) - else - symL,symR = string.sub(macroParameters,1,1),string.sub(macroParameters,-1,-1) - fileName = trimString(string.sub(macroParameters,2,-2)) - end + -- ZASM2 compatibility syntax support + if macroName == "#include##" then + symL,symR = "<",">" + fileName = trimString(string.sub(macroParameters,1,-1)) + else + symL,symR = string.sub(macroParameters,1,1),string.sub(macroParameters,-1,-1) + fileName = trimString(string.sub(macroParameters,2,-2)) + end - -- Full file name including the path to file - local fullFileName - if (symL == "\"") and (symR == "\"") then -- File relative to current one - fullFileName = self.WorkingDir..fileName - elseif (symL == "<") and (symR == ">") then -- File relative to root directory - fullFileName = fileName - else - self:Error("Invalid syntax for #include macro (wrong brackets)", - macroPosition.Line,macroPosition.Col,macroPosition.File) - end + -- Full file name including the path to file + local fullFileName + if (symL == "\"") and (symR == "\"") then -- File relative to current one + fullFileName = self.WorkingDir..fileName + elseif (symL == "<") and (symR == ">") then -- File relative to root directory + fullFileName = fileName + else + self:Error("Invalid syntax for #include macro (wrong brackets)", + macroPosition.Line,macroPosition.Col,macroPosition.File) + end - -- Search for file on search paths - local fileText = self:LoadFile(fullFileName) - if (symL == "<") and (symR == ">") and (not fileText) then - for _,searchPath in pairs(self.SearchPaths) do - if not fileText then - fileText = self:LoadFile(searchPath.."\\"..fullFileName) - fileName = searchPath.."\\"..fullFileName - end - end - end + -- Search for file on search paths + local fileText = self:LoadFile(fullFileName) + if (symL == "<") and (symR == ">") and (not fileText) then + for _,searchPath in pairs(self.SearchPaths) do + if not fileText then + fileText = self:LoadFile(searchPath.."\\"..fullFileName) + fileName = searchPath.."\\"..fullFileName + end + end + end - -- Push this file on top of the stack - if fileText then - table.insert(self.Code, 1, { Text = fileText, Line = 1, Col = 1, File = fileName, ParentFile = macroPosition.File, NextCharPos = 1 }) - else - self:Error("Cannot open file: "..fileName, - macroPosition.Line,macroPosition.Col,macroPosition.File) - end - else - self:Error("Invalid macro: #"..macroName, - macroPosition.Line,macroPosition.Col,macroPosition.File) - end + -- Push this file on top of the stack + if fileText then + table.insert(self.Code, 1, { Text = fileText, Line = 1, Col = 1, File = fileName, ParentFile = macroPosition.File, NextCharPos = 1 }) + else + self:Error("Cannot open file: "..fileName, + macroPosition.Line,macroPosition.Col,macroPosition.File) + end + else + self:Error("Invalid macro: #"..macroName, + macroPosition.Line,macroPosition.Col,macroPosition.File) + end end diff --git a/lua/wire/client/hlzasm/hc_syntax.lua b/lua/wire/client/hlzasm/hc_syntax.lua index e558609d6b..d245e910ac 100644 --- a/lua/wire/client/hlzasm/hc_syntax.lua +++ b/lua/wire/client/hlzasm/hc_syntax.lua @@ -7,19 +7,19 @@ -- Syntax lookup for vector definitions local VectorSyntax = { - FLOAT = { {} }, - SCALAR = { {} }, - VECTOR1F = { {"x"} }, - VECTOR2F = { {"x"},{"y"} }, - VECTOR3F = { {"x"},{"y"},{"z"} }, - VECTOR4F = { {"x"},{"y"},{"z"},{"w"} }, - VEC1F = { {"x"} }, - VEC2F = { {"x"},{"y"} }, - VEC3F = { {"x"},{"y"},{"z"} }, - VEC4F = { {"x"},{"y"},{"z"},{"w"} }, - UV = { {"x","u"},{"y","v"} }, - COLOR = { {"x","r"},{"y","g"},{"z","b"},{"w","a"} }, - MATRIX = {}, + FLOAT = { {} }, + SCALAR = { {} }, + VECTOR1F = { {"x"} }, + VECTOR2F = { {"x"},{"y"} }, + VECTOR3F = { {"x"},{"y"},{"z"} }, + VECTOR4F = { {"x"},{"y"},{"z"},{"w"} }, + VEC1F = { {"x"} }, + VEC2F = { {"x"},{"y"} }, + VEC3F = { {"x"},{"y"},{"z"} }, + VEC4F = { {"x"},{"y"},{"z"},{"w"} }, + UV = { {"x","u"},{"y","v"} }, + COLOR = { {"x","r"},{"y","g"},{"z","b"},{"w","a"} }, + MATRIX = {}, } for i=0,15 do VectorSyntax.MATRIX[i+1] = {tostring(i)} end @@ -29,1229 +29,1229 @@ for i=0,15 do VectorSyntax.MATRIX[i+1] = {tostring(i)} end -------------------------------------------------------------------------------- -- Compile an opcode (called after if self:MatchToken(TOKEN.OPCODE)) 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") - end - if self.OpcodeOld[opcodeName] then - self:Warning("Mnemonic \""..opcodeName.."\" is an old mnemonic for this instruction. Please use the newer mnemonic \""..self.OpcodeOld[opcodeName].."\".") - end - - -- Create leaf - local opcodeLeaf = self:NewLeaf() - opcodeLeaf.Opcode = opcodeName - opcodeLeaf.ExplictAssign = true - - -- Parse operands - for i=1,operandCount do - local segmentOffset,constantValue,expressionLeaf - local isMemoryReference,useSpecialMemorySyntax - - -- Check if it's a special memory reference ([<...>]) - if self:MatchToken(TOKEN.LSUBSCR) then - isMemoryReference = true - useSpecialMemorySyntax = true - end - - -- Check for segment prefix (ES:<...> or ES+<...>) - if ((self:PeekToken() == TOKEN.SEGMENT) or (self:PeekToken() == TOKEN.REGISTER)) and - ((self:PeekToken(1) == TOKEN.DCOLON) or - (useSpecialMemorySyntax and (self:PeekToken(1) == TOKEN.PLUS))) then -- next character is : or + - if self:MatchToken(TOKEN.SEGMENT) then - -- 1 to 8: CS .. LS - segmentOffset = self.TokenData - elseif self:MatchToken(TOKEN.REGISTER) then - if self.TokenData >= 96 then -- 17+: extended registers - segmentOffset = 17 + self.TokenData - 96 - else -- 9 to 16: EAX .. EBP - segmentOffset = self.TokenData + 8 - end - end - - if useSpecialMemorySyntax then - if not self:MatchToken(TOKEN.DCOLON) then self:ExpectToken(TOKEN.PLUS) end - else - self:ExpectToken(TOKEN.DCOLON) - end - end - - -- Check if it's a memory reference (#<...>) - if not useSpecialMemorySyntax then - if self:MatchToken(TOKEN.HASH) then isMemoryReference = true end - end - - -- Parse operand expression (use previous result if previous const wasnt related to seg offset) - local c,v,e = self:ConstantExpression() - if c then -- Constant value - if v - then constantValue = v -- Exact value - else constantValue = e -- Expression to be recalculated later - end - else -- Expression - expressionLeaf = self:Expression() - if expressionLeaf.Opcode then - self:Warning("Using complex expression as operand: might corrupt user register") - end - -- FIXME: warning about using extra registers? - end - - -- Check for segment prefix again (reversed syntax <...>:ES) - if self:MatchToken(TOKEN.DCOLON) then - if (not segmentOffset) and - ((self:PeekToken() == TOKEN.SEGMENT) or (self:PeekToken() == TOKEN.REGISTER)) then - if self:MatchToken(TOKEN.SEGMENT) then - -- 1 to 8: CS .. LS - segmentOffset = self.TokenData - elseif self:MatchToken(TOKEN.REGISTER) then - if self.TokenData >= 96 then -- 17+: extended registers - segmentOffset = 17 + self.TokenData - 96 - else -- 9 to 16: EAX .. EBP - segmentOffset = self.TokenData + 8 - end - end - else - self:Error("Invalid segment offset syntax") - end - end - - -- Trailing bracket for [...] memory syntax - if useSpecialMemorySyntax then - self:ExpectToken(TOKEN.RSUBSCR) - end - - -- Create operand - if isMemoryReference then - if expressionLeaf then - if expressionLeaf.Register then - opcodeLeaf.Operands[i] = { MemoryRegister = expressionLeaf.Register, Segment = segmentOffset } - else - opcodeLeaf.Operands[i] = { MemoryPointer = expressionLeaf, Segment = segmentOffset } - end - else - opcodeLeaf.Operands[i] = { MemoryPointer = constantValue, Segment = segmentOffset } - end - else - if expressionLeaf then - if expressionLeaf.Register then - if (expressionLeaf.Register >= 16) and (expressionLeaf.Register <= 23) and (segmentOffset) then - -- Swap EBX:ES with ES:EBX (because the former one is invalid in ZCPU) - local register = expressionLeaf.Register - local segment = segmentOffset - - -- Convert segment register index to register index - if (segment >= 1) and (segment <= 8) then expressionLeaf.Register = segment + 15 end - if (segment >= 9) and (segment <= 16) then expressionLeaf.Register = segment - 8 end - - -- Convert register index to segment register index - if (register >= 1) and (register <= 8) then segmentOffset = register + 8 end - if (register >= 16) and (register <= 23) then segmentOffset = register - 15 end - end - opcodeLeaf.Operands[i] = { Register = expressionLeaf.Register, Segment = segmentOffset } - else - if segmentOffset then - opcodeLeaf.Operands[i] = self:NewLeaf() - opcodeLeaf.Operands[i].Opcode = "add" - opcodeLeaf.Operands[i].Operands[1] = { Register = segmentOffset+15 } - opcodeLeaf.Operands[i].Operands[2] = expressionLeaf - else - opcodeLeaf.Operands[i] = expressionLeaf - end - end - else - opcodeLeaf.Operands[i] = { Constant = constantValue, Segment = segmentOffset } - end - end - - -- Attach information from expression - if expressionLeaf then - opcodeLeaf.Operands[i].PreviousLeaf = expressionLeaf.PreviousLeaf - end - - -- Syntax - if i < operandCount then - self:ExpectToken(TOKEN.COMMA) - else - if self:MatchToken(TOKEN.COMMA) then - self:Error("Invalid operand count") - end - end - end - - -- Check if first operand is a non-preserved register - if self.BusyRegisters then - if opcodeLeaf.Operands[1] and opcodeLeaf.Operands[1].Register and - (self.BusyRegisters[opcodeLeaf.Operands[1].Register] == false) and - (self.BlockDepth > 0) then - self:Warning("Warning: using an unpreserved register") - end - - if opcodeLeaf.Operands[1] and opcodeLeaf.Operands[1].MemoryRegister and - (self.BusyRegisters[opcodeLeaf.Operands[1].MemoryRegister] == false) and - (self.BlockDepth > 0) then - self:Warning("Warning: using an unpreserved register") - end - end - - -- Add opcode to tail - self:AddLeafToTail(opcodeLeaf) - self:MatchToken(TOKEN.COLON) - return true + 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") + end + if self.OpcodeOld[opcodeName] then + self:Warning("Mnemonic \""..opcodeName.."\" is an old mnemonic for this instruction. Please use the newer mnemonic \""..self.OpcodeOld[opcodeName].."\".") + end + + -- Create leaf + local opcodeLeaf = self:NewLeaf() + opcodeLeaf.Opcode = opcodeName + opcodeLeaf.ExplictAssign = true + + -- Parse operands + for i=1,operandCount do + local segmentOffset,constantValue,expressionLeaf + local isMemoryReference,useSpecialMemorySyntax + + -- Check if it's a special memory reference ([<...>]) + if self:MatchToken(TOKEN.LSUBSCR) then + isMemoryReference = true + useSpecialMemorySyntax = true + end + + -- Check for segment prefix (ES:<...> or ES+<...>) + if ((self:PeekToken() == TOKEN.SEGMENT) or (self:PeekToken() == TOKEN.REGISTER)) and + ((self:PeekToken(1) == TOKEN.DCOLON) or + (useSpecialMemorySyntax and (self:PeekToken(1) == TOKEN.PLUS))) then -- next character is : or + + if self:MatchToken(TOKEN.SEGMENT) then + -- 1 to 8: CS .. LS + segmentOffset = self.TokenData + elseif self:MatchToken(TOKEN.REGISTER) then + if self.TokenData >= 96 then -- 17+: extended registers + segmentOffset = 17 + self.TokenData - 96 + else -- 9 to 16: EAX .. EBP + segmentOffset = self.TokenData + 8 + end + end + + if useSpecialMemorySyntax then + if not self:MatchToken(TOKEN.DCOLON) then self:ExpectToken(TOKEN.PLUS) end + else + self:ExpectToken(TOKEN.DCOLON) + end + end + + -- Check if it's a memory reference (#<...>) + if not useSpecialMemorySyntax then + if self:MatchToken(TOKEN.HASH) then isMemoryReference = true end + end + + -- Parse operand expression (use previous result if previous const wasnt related to seg offset) + local c,v,e = self:ConstantExpression() + if c then -- Constant value + if v + then constantValue = v -- Exact value + else constantValue = e -- Expression to be recalculated later + end + else -- Expression + expressionLeaf = self:Expression() + if expressionLeaf.Opcode then + self:Warning("Using complex expression as operand: might corrupt user register") + end + -- FIXME: warning about using extra registers? + end + + -- Check for segment prefix again (reversed syntax <...>:ES) + if self:MatchToken(TOKEN.DCOLON) then + if (not segmentOffset) and + ((self:PeekToken() == TOKEN.SEGMENT) or (self:PeekToken() == TOKEN.REGISTER)) then + if self:MatchToken(TOKEN.SEGMENT) then + -- 1 to 8: CS .. LS + segmentOffset = self.TokenData + elseif self:MatchToken(TOKEN.REGISTER) then + if self.TokenData >= 96 then -- 17+: extended registers + segmentOffset = 17 + self.TokenData - 96 + else -- 9 to 16: EAX .. EBP + segmentOffset = self.TokenData + 8 + end + end + else + self:Error("Invalid segment offset syntax") + end + end + + -- Trailing bracket for [...] memory syntax + if useSpecialMemorySyntax then + self:ExpectToken(TOKEN.RSUBSCR) + end + + -- Create operand + if isMemoryReference then + if expressionLeaf then + if expressionLeaf.Register then + opcodeLeaf.Operands[i] = { MemoryRegister = expressionLeaf.Register, Segment = segmentOffset } + else + opcodeLeaf.Operands[i] = { MemoryPointer = expressionLeaf, Segment = segmentOffset } + end + else + opcodeLeaf.Operands[i] = { MemoryPointer = constantValue, Segment = segmentOffset } + end + else + if expressionLeaf then + if expressionLeaf.Register then + if (expressionLeaf.Register >= 16) and (expressionLeaf.Register <= 23) and (segmentOffset) then + -- Swap EBX:ES with ES:EBX (because the former one is invalid in ZCPU) + local register = expressionLeaf.Register + local segment = segmentOffset + + -- Convert segment register index to register index + if (segment >= 1) and (segment <= 8) then expressionLeaf.Register = segment + 15 end + if (segment >= 9) and (segment <= 16) then expressionLeaf.Register = segment - 8 end + + -- Convert register index to segment register index + if (register >= 1) and (register <= 8) then segmentOffset = register + 8 end + if (register >= 16) and (register <= 23) then segmentOffset = register - 15 end + end + opcodeLeaf.Operands[i] = { Register = expressionLeaf.Register, Segment = segmentOffset } + else + if segmentOffset then + opcodeLeaf.Operands[i] = self:NewLeaf() + opcodeLeaf.Operands[i].Opcode = "add" + opcodeLeaf.Operands[i].Operands[1] = { Register = segmentOffset+15 } + opcodeLeaf.Operands[i].Operands[2] = expressionLeaf + else + opcodeLeaf.Operands[i] = expressionLeaf + end + end + else + opcodeLeaf.Operands[i] = { Constant = constantValue, Segment = segmentOffset } + end + end + + -- Attach information from expression + if expressionLeaf then + opcodeLeaf.Operands[i].PreviousLeaf = expressionLeaf.PreviousLeaf + end + + -- Syntax + if i < operandCount then + self:ExpectToken(TOKEN.COMMA) + else + if self:MatchToken(TOKEN.COMMA) then + self:Error("Invalid operand count") + end + end + end + + -- Check if first operand is a non-preserved register + if self.BusyRegisters then + if opcodeLeaf.Operands[1] and opcodeLeaf.Operands[1].Register and + (self.BusyRegisters[opcodeLeaf.Operands[1].Register] == false) and + (self.BlockDepth > 0) then + self:Warning("Warning: using an unpreserved register") + end + + if opcodeLeaf.Operands[1] and opcodeLeaf.Operands[1].MemoryRegister and + (self.BusyRegisters[opcodeLeaf.Operands[1].MemoryRegister] == false) and + (self.BlockDepth > 0) then + self:Warning("Warning: using an unpreserved register") + end + end + + -- Add opcode to tail + self:AddLeafToTail(opcodeLeaf) + self:MatchToken(TOKEN.COLON) + return true end -------------------------------------------------------------------------------- -- Start a new block function HCOMP:BlockStart(blockType) - if self.BlockDepth == 0 then - -- Create leaf that corresponds to ENTER instruction - self.HeadLeaf = self:NewLeaf() - self.HeadLeaf.Opcode = "enter" - self.HeadLeaf.Operands[1] = { Constant = self.Settings.MagicValue } - self:AddLeafToTail(self.HeadLeaf) - - self.LocalLabels = {} - self.StackPointer = 0 - if self.GenerateInlineFunction then - self.ParameterPointer = 0 -- Skip EBP - else - self.ParameterPointer = 1 -- Skip EBP and return address - end - - self.StringsTable = {} - - self.BlockType = {} - self.SpecialLeaf = {} - - -- Create busy registers list - self.BusyRegisters = { false,false,false,false,false,false,true,true } - self.RegisterIdentities = {} -- holds register vars with the identity as key and value as index in busyregisters - for i=#self.BusyRegisters,128 do -- Allows R0-R31 and segment registers to be used, but only if ZAPPED beforehand - self.BusyRegisters[i] = true - end - if self.Settings.AutoBusyRegisters then - for _,range in ipairs(self.Settings.AutoBusyRegisterRanges) do - for i=range[2],range[3] do - if self.RegisterName[i] ~= 'N/A' then - self.BusyRegisters[i] = range[1] - end - end - end - end - end - - -- Create a leaf that corresponds to label for BREAK - local breakLeaf = self:NewLeaf() - breakLeaf.Opcode = "LABEL" - breakLeaf.Label = self:GetTempLabel() - breakLeaf.Label.Type = "Pointer" - breakLeaf.Label.Leaf = breakLeaf - - -- Create a leaf that corresponds to label for CONTINUE - local continueLeaf = self:NewLeaf() - continueLeaf.Opcode = "LABEL" - continueLeaf.Label = self:GetTempLabel() - continueLeaf.Label.Type = "Pointer" - continueLeaf.Label.Leaf = continueLeaf - self:AddLeafToTail(continueLeaf) - - -- Only FOR loops have step code - if (blockType == "WHILE") or - (blockType == "DO") then - self.CurrentStepLeaf = nil - end - - self.SpecialLeaf[#self.SpecialLeaf+1] = { - Break = breakLeaf, - Continue = continueLeaf, - JumpBack = self:NewLeaf(), - Step = self.CurrentStepLeaf, - } - - if (blockType == "FOR") or - (blockType == "WHILE") or - (blockType == "DO") then - self.CurrentContinueLeaf = self.SpecialLeaf[#self.SpecialLeaf].Continue - self.CurrentBreakLeaf = self.SpecialLeaf[#self.SpecialLeaf].Break - end - - -- Push block type - table.insert(self.BlockType,blockType or "FUNCTION") - self.BlockDepth = self.BlockDepth + 1 + if self.BlockDepth == 0 then + -- Create leaf that corresponds to ENTER instruction + self.HeadLeaf = self:NewLeaf() + self.HeadLeaf.Opcode = "enter" + self.HeadLeaf.Operands[1] = { Constant = self.Settings.MagicValue } + self:AddLeafToTail(self.HeadLeaf) + + self.LocalLabels = {} + self.StackPointer = 0 + if self.GenerateInlineFunction then + self.ParameterPointer = 0 -- Skip EBP + else + self.ParameterPointer = 1 -- Skip EBP and return address + end + + self.StringsTable = {} + + self.BlockType = {} + self.SpecialLeaf = {} + + -- Create busy registers list + self.BusyRegisters = { false,false,false,false,false,false,true,true } + self.RegisterIdentities = {} -- holds register vars with the identity as key and value as index in busyregisters + for i=#self.BusyRegisters,128 do -- Allows R0-R31 and segment registers to be used, but only if ZAPPED beforehand + self.BusyRegisters[i] = true + end + if self.Settings.AutoBusyRegisters then + for _,range in ipairs(self.Settings.AutoBusyRegisterRanges) do + for i=range[2],range[3] do + if self.RegisterName[i] ~= 'N/A' then + self.BusyRegisters[i] = range[1] + end + end + end + end + end + + -- Create a leaf that corresponds to label for BREAK + local breakLeaf = self:NewLeaf() + breakLeaf.Opcode = "LABEL" + breakLeaf.Label = self:GetTempLabel() + breakLeaf.Label.Type = "Pointer" + breakLeaf.Label.Leaf = breakLeaf + + -- Create a leaf that corresponds to label for CONTINUE + local continueLeaf = self:NewLeaf() + continueLeaf.Opcode = "LABEL" + continueLeaf.Label = self:GetTempLabel() + continueLeaf.Label.Type = "Pointer" + continueLeaf.Label.Leaf = continueLeaf + self:AddLeafToTail(continueLeaf) + + -- Only FOR loops have step code + if (blockType == "WHILE") or + (blockType == "DO") then + self.CurrentStepLeaf = nil + end + + self.SpecialLeaf[#self.SpecialLeaf+1] = { + Break = breakLeaf, + Continue = continueLeaf, + JumpBack = self:NewLeaf(), + Step = self.CurrentStepLeaf, + } + + if (blockType == "FOR") or + (blockType == "WHILE") or + (blockType == "DO") then + self.CurrentContinueLeaf = self.SpecialLeaf[#self.SpecialLeaf].Continue + self.CurrentBreakLeaf = self.SpecialLeaf[#self.SpecialLeaf].Break + end + + -- Push block type + table.insert(self.BlockType,blockType or "FUNCTION") + self.BlockDepth = self.BlockDepth + 1 end -------------------------------------------------------------------------------- -- End the block function HCOMP:BlockEnd() - -- If required, end the previous block - local endPreviousBlock = self.SpecialLeaf[#self.SpecialLeaf].EndPreviousBlock - - -- If required, add leaf that jumps back to block start - if self.SpecialLeaf[#self.SpecialLeaf].JumpBack.Opcode ~= "INVALID" then - self.SpecialLeaf[#self.SpecialLeaf].JumpBack.CurrentPosition = self:CurrentSourcePosition() - self:AddLeafToTail(self.SpecialLeaf[#self.SpecialLeaf].JumpBack) - end - - -- Add leaf that corresponds to break label - self.SpecialLeaf[#self.SpecialLeaf].Break.CurrentPosition = self:CurrentSourcePosition() - self:AddLeafToTail(self.SpecialLeaf[#self.SpecialLeaf].Break) - - -- Pop current continue leaf if required - if self.CurrentContinueLeaf == self.SpecialLeaf[#self.SpecialLeaf].Continue then - if self.SpecialLeaf[#self.SpecialLeaf-1] then - self.CurrentContinueLeaf = self.SpecialLeaf[#self.SpecialLeaf-1].Continue - self.CurrentBreakLeaf = self.SpecialLeaf[#self.SpecialLeaf-1].Break - self.CurrentStepLeaf = self.SpecialLeaf[#self.SpecialLeaf-1].Step - else - self.CurrentContinueLeaf = nil - self.CurrentBreakLeaf = nil - self.CurrentStepLeaf = nil - end - end - - -- Pop unused leaves - self.SpecialLeaf[#self.SpecialLeaf] = nil - - -- Pop block type - local blockType = self.BlockType[#self.BlockType] - self.BlockType[#self.BlockType] = nil - - self.BlockDepth = self.BlockDepth - 1 - if self.BlockDepth == 0 then - -- Update head leaf with new stack data - self.HeadLeaf.Operands[1].Constant = -self.StackPointer - if (self.StackPointer == 0) and - (self.ParameterPointer == 0) and - (not self.Settings.AlwaysEnterLeave) then self.HeadLeaf.Opcode = "DATA" end - - -- Create leaf for exiting local scope - local leaveLeaf = self:NewLeaf() - leaveLeaf.Opcode = "leave" - if (self.StackPointer ~= 0) or - (self.ParameterPointer ~= 0) or - (self.Settings.AlwaysEnterLeave) then - self:AddLeafToTail(leaveLeaf) - end - - -- Create leaf for returning from call - if blockType == "FUNCTION" then - if not self.GenerateInlineFunction then - local retLeaf = self:NewLeaf() - retLeaf.Opcode = "ret" - self:AddLeafToTail(retLeaf) - end - end - - -- Write down strings table - for string,leaf in pairs(self.StringsTable) do - self:AddLeafToTail(leaf) - end - self.StringsTable = nil - - -- Add local labels to lookup list - for labelName,labelData in pairs(self.LocalLabels) do - self.DebugInfo.Labels["local."..labelName] = { StackOffset = labelData.StackOffset } - end - - self.LocalLabels = nil - self.StackPointer = nil - self.ParameterPointer = nil - - self.BlockType = nil - self.SpecialLeaf = nil - - -- Zap all registers preserved inside the function - self.BusyRegisters = nil - - -- Disable inlining - if self.GenerateInlineFunction then - self.Functions[self.GenerateInlineFunction].InlineCode = self.InlineFunctionCode - self.GenerateInlineFunction = nil - self.InlineFunctionCode = nil - end - - -- Disable parent label - self.CurrentParentLabel = nil - end - - -- End it, see first line of the function - if endPreviousBlock then - self:BlockEnd() - end + -- If required, end the previous block + local endPreviousBlock = self.SpecialLeaf[#self.SpecialLeaf].EndPreviousBlock + + -- If required, add leaf that jumps back to block start + if self.SpecialLeaf[#self.SpecialLeaf].JumpBack.Opcode ~= "INVALID" then + self.SpecialLeaf[#self.SpecialLeaf].JumpBack.CurrentPosition = self:CurrentSourcePosition() + self:AddLeafToTail(self.SpecialLeaf[#self.SpecialLeaf].JumpBack) + end + + -- Add leaf that corresponds to break label + self.SpecialLeaf[#self.SpecialLeaf].Break.CurrentPosition = self:CurrentSourcePosition() + self:AddLeafToTail(self.SpecialLeaf[#self.SpecialLeaf].Break) + + -- Pop current continue leaf if required + if self.CurrentContinueLeaf == self.SpecialLeaf[#self.SpecialLeaf].Continue then + if self.SpecialLeaf[#self.SpecialLeaf-1] then + self.CurrentContinueLeaf = self.SpecialLeaf[#self.SpecialLeaf-1].Continue + self.CurrentBreakLeaf = self.SpecialLeaf[#self.SpecialLeaf-1].Break + self.CurrentStepLeaf = self.SpecialLeaf[#self.SpecialLeaf-1].Step + else + self.CurrentContinueLeaf = nil + self.CurrentBreakLeaf = nil + self.CurrentStepLeaf = nil + end + end + + -- Pop unused leaves + self.SpecialLeaf[#self.SpecialLeaf] = nil + + -- Pop block type + local blockType = self.BlockType[#self.BlockType] + self.BlockType[#self.BlockType] = nil + + self.BlockDepth = self.BlockDepth - 1 + if self.BlockDepth == 0 then + -- Update head leaf with new stack data + self.HeadLeaf.Operands[1].Constant = -self.StackPointer + if (self.StackPointer == 0) and + (self.ParameterPointer == 0) and + (not self.Settings.AlwaysEnterLeave) then self.HeadLeaf.Opcode = "DATA" end + + -- Create leaf for exiting local scope + local leaveLeaf = self:NewLeaf() + leaveLeaf.Opcode = "leave" + if (self.StackPointer ~= 0) or + (self.ParameterPointer ~= 0) or + (self.Settings.AlwaysEnterLeave) then + self:AddLeafToTail(leaveLeaf) + end + + -- Create leaf for returning from call + if blockType == "FUNCTION" then + if not self.GenerateInlineFunction then + local retLeaf = self:NewLeaf() + retLeaf.Opcode = "ret" + self:AddLeafToTail(retLeaf) + end + end + + -- Write down strings table + for string,leaf in pairs(self.StringsTable) do + self:AddLeafToTail(leaf) + end + self.StringsTable = nil + + -- Add local labels to lookup list + for labelName,labelData in pairs(self.LocalLabels) do + self.DebugInfo.Labels["local."..labelName] = { StackOffset = labelData.StackOffset } + end + + self.LocalLabels = nil + self.StackPointer = nil + self.ParameterPointer = nil + + self.BlockType = nil + self.SpecialLeaf = nil + + -- Zap all registers preserved inside the function + self.BusyRegisters = nil + + -- Disable inlining + if self.GenerateInlineFunction then + self.Functions[self.GenerateInlineFunction].InlineCode = self.InlineFunctionCode + self.GenerateInlineFunction = nil + self.InlineFunctionCode = nil + end + + -- Disable parent label + self.CurrentParentLabel = nil + end + + -- End it, see first line of the function + if endPreviousBlock then + self:BlockEnd() + end end -------------------------------------------------------------------------------- -- Parse ELSE clause function HCOMP:ParseElse(parentBlockExists) - -- Add a jump over the else clause - local jumpLeaf = self:NewLeaf() - jumpLeaf.Opcode = "jmp" - self:AddLeafToTail(jumpLeaf) - - -- Alter the conditional jump so it goes to else clause - local jumpOverLabelLeaf = self:NewLeaf() - local jumpOverLabel = self:GetTempLabel() - jumpOverLabelLeaf.Opcode = "LABEL" - jumpOverLabel.Type = "Pointer" - jumpOverLabel.Leaf = jumpOverLabelLeaf - jumpOverLabelLeaf.Label = jumpOverLabel - self:AddLeafToTail(jumpOverLabelLeaf) - - if parentBlockExists and self.SpecialLeaf[#self.SpecialLeaf].ConditionalBreak then - self:AddLeafToTail(self.SpecialLeaf[#self.SpecialLeaf].ConditionalBreak) - end - - -- Enter the ELSE block - local needBlock = self:MatchToken(self.TOKEN.LBRACKET) - self:BlockStart("ELSE") - - -- Update properly the jump leaf - jumpLeaf.Operands[1] = { PointerToLabel = self.SpecialLeaf[#self.SpecialLeaf].Break.Label } - - if needBlock then - -- Special marker that means that ending this block ends previous one too - self.SpecialLeaf[#self.SpecialLeaf].EndPreviousBlock = parentBlockExists - else - -- Parse next statement if dont need a block - local previousBlockDepth = #self.BlockType - self:Statement() - - -- If did not enter any new blocks, it was a plain statement. Pop ELSE block - if #self.BlockType == previousBlockDepth then - -- End the ELSE block - self:BlockEnd() - - -- End the IF block, if required - if parentBlockExists then self:BlockEnd() end - else - -- Special marker that means that ending this block ends ELSE block early too - self.SpecialLeaf[#self.SpecialLeaf].EndPreviousBlock = true - -- Special marker that means that ending ELSE block ends previous one too - self.SpecialLeaf[#self.SpecialLeaf-1].EndPreviousBlock = parentBlockExists - end - end + -- Add a jump over the else clause + local jumpLeaf = self:NewLeaf() + jumpLeaf.Opcode = "jmp" + self:AddLeafToTail(jumpLeaf) + + -- Alter the conditional jump so it goes to else clause + local jumpOverLabelLeaf = self:NewLeaf() + local jumpOverLabel = self:GetTempLabel() + jumpOverLabelLeaf.Opcode = "LABEL" + jumpOverLabel.Type = "Pointer" + jumpOverLabel.Leaf = jumpOverLabelLeaf + jumpOverLabelLeaf.Label = jumpOverLabel + self:AddLeafToTail(jumpOverLabelLeaf) + + if parentBlockExists and self.SpecialLeaf[#self.SpecialLeaf].ConditionalBreak then + self:AddLeafToTail(self.SpecialLeaf[#self.SpecialLeaf].ConditionalBreak) + end + + -- Enter the ELSE block + local needBlock = self:MatchToken(self.TOKEN.LBRACKET) + self:BlockStart("ELSE") + + -- Update properly the jump leaf + jumpLeaf.Operands[1] = { PointerToLabel = self.SpecialLeaf[#self.SpecialLeaf].Break.Label } + + if needBlock then + -- Special marker that means that ending this block ends previous one too + self.SpecialLeaf[#self.SpecialLeaf].EndPreviousBlock = parentBlockExists + else + -- Parse next statement if dont need a block + local previousBlockDepth = #self.BlockType + self:Statement() + + -- If did not enter any new blocks, it was a plain statement. Pop ELSE block + if #self.BlockType == previousBlockDepth then + -- End the ELSE block + self:BlockEnd() + + -- End the IF block, if required + if parentBlockExists then self:BlockEnd() end + else + -- Special marker that means that ending this block ends ELSE block early too + self.SpecialLeaf[#self.SpecialLeaf].EndPreviousBlock = true + -- Special marker that means that ending ELSE block ends previous one too + self.SpecialLeaf[#self.SpecialLeaf-1].EndPreviousBlock = parentBlockExists + end + end end -------------------------------------------------------------------------------- function HCOMP:DeclareRegisterVariable() - if self.BlockDepth > 0 then - for reg=1,#self.BusyRegisters do - if not self.BusyRegisters[reg] then - self.BusyRegisters[reg] = true - return reg - end - end - self:Error("Out of free registers for declaring local variables") - else - self:Error("Unable to declare a register variable") - end + if self.BlockDepth > 0 then + for reg=1,#self.BusyRegisters do + if not self.BusyRegisters[reg] then + self.BusyRegisters[reg] = true + return reg + end + end + self:Error("Out of free registers for declaring local variables") + else + self:Error("Unable to declare a register variable") + end end -------------------------------------------------------------------------------- -- Compile a variable/function. Returns corresponding labels function HCOMP:DefineVariable(isFunctionParam,isForwardDecl,isRegisterDecl,isStructMember) local TOKEN,TOKENSET = self.TOKEN,self.TOKENSET - local varType,varSize,isStruct - if self:MatchToken(TOKEN.IDENT) then -- Define structure - varType = self.TokenData - varSize = 0 -- Depends on pointer level - isStruct = true - WireTextEditor.Modes.ZCPU.keywordsTable[string.upper(varType)] = true -- Add the new struct to the keywords table. - else -- Define variable - self:ExpectToken(TOKEN.TYPE) - varType = self.TokenData - varSize = 1 - if varType == 5 then varSize = 4 end - end - - -- Variable labels list - local labelsList = {} - - -- Parse all variables to define - while true do - -- Get pointer level (0, *, **, ***, etc) - local pointerLevel = 0 - while self:MatchToken(TOKEN.TIMES) do pointerLevel = pointerLevel + 1 end - - -- Fix structure size - if isStruct then - if pointerLevel > 0 - then varSize = 1 - else varSize = self.StructSize[varType] - end - end - - -- Get variable name - self:ExpectToken(TOKEN.IDENT) - local varName = self.TokenData - - -- Try to read information about array size, stuff - local arraySize - while self:MatchToken(TOKEN.LSUBSCR) do -- varname[] - if self:MatchToken(TOKEN.RSUBSCR) then -- varname[] - if isFunctionParam then -- just a pointer to an array - pointerLevel = 1 - end - else - local c,v = self:ConstantExpression(true) -- need precise value here, no ptrs allowed - if c then - if not arraySize then arraySize = {} end - arraySize[#arraySize+1] = v - else - self:Error("Array size must be constant") - end - - self:ExpectToken(TOKEN.RSUBSCR) - end - end - - -- Calculate size of array - local bytesArraySize - if arraySize then - for k,v in pairs(arraySize) do - bytesArraySize = (bytesArraySize or 0) + v*varSize - end - end - - -- Add to global list - table.insert(labelsList,{ Name = varName, Type = varType, PtrLevel = pointerLevel, Size = bytesArraySize or varSize }) - - if not isStructMember then -- Do not define struct members - if self:MatchToken(TOKEN.LPAREN) then -- Define function - -- Create function entrypoint - local label - label = self:DefineLabel(varName) - self:PreviousToken() -- LPAREN - self:PreviousToken() -- Func Name - self:PreviousToken() -- Type Name - local ptrlevel = 0 - if self:MatchToken(TOKEN.TIMES) then - -- skip back until we're done with the ptr - self:PreviousToken() - while self:MatchToken(TOKEN.TIMES) do - ptrlevel = ptrlevel + 1; - self:PreviousToken() - self:PreviousToken() - end - end - if not self:MatchToken(TOKEN.IDENT) then - self:MatchToken(TOKEN.TYPE) -- If it's not an IDENT (struct/user defined) it should be a generic type - end - local returnType = self.TokenData - self.CurrentToken = self.CurrentToken + ptrlevel -- return to present. - self:MatchToken(TOKEN.IDENT) - local funcName = self.TokenData - self.CurFunction = {Name = funcName, ReturnType = returnType, ReturnPtrLevel = ptrlevel} - self:NextToken() - label.Type = "Pointer" - label.Defined = true - - -- Make all further leaves parented to this label - self.CurrentParentLabel = label - - -- Create label leaf - label.Leaf = self:NewLeaf() - label.Leaf.Opcode = "LABEL" - label.Leaf.Label = label - self:AddLeafToTail(label.Leaf) --isInlined - - -- Define a function - local _,functionVariables = nil,{} - - self:BlockStart() - if not self:MatchToken(TOKEN.RPAREN) then - _,functionVariables = self:DefineVariable(true) - self:ExpectToken(TOKEN.RPAREN) - - -- Add comments about function into assembly listing - if self.Settings.GenerateComments then - for i=1,#functionVariables do - label.Leaf.Comment = (label.Leaf.Comment or "")..(functionVariables[i].Name) - if i < #functionVariables then label.Leaf.Comment = label.Leaf.Comment.."," end - end - end - end - - -- Forward declaration, mess up label name - if isForwardDecl then - local newName = label.Name.."@" - for i=1,#functionVariables do - newName = newName..functionVariables[i].Name..functionVariables[i].Type - if i < #functionVariables then - newName = newName.."_" - end - end - self:RedefineLabel(label.Name,newName) - end - - -- Generate comment if required - if self.Settings.GenerateComments then label.Leaf.Comment = varName.."("..(label.Leaf.Comment or "")..")" end - self:ExpectToken(TOKEN.LBRACKET) - return true,functionVariables,varName,varType,pointerLevel - else -- Define variable - -- Check if there's an initializer - local initializerLeaves,initializerValues - if self:MatchToken(TOKEN.EQUAL) then - if not self.LocalLabels then -- Check rules for global init - if self:MatchToken(TOKEN.LBRACKET) then -- Array initializer - if not bytesArraySize then self:Error("Cannot initialize value: not an array") end - - initializerValues = {} - while not self:MatchToken(TOKEN.RBRACKET) do - local c,v = self:ConstantExpression(true) - if not c - then self:Error("Cannot have expressions in global initializers") - else table.insert(initializerValues,v) - end - self:MatchToken(TOKEN.COMMA) - end - else -- Single initializer - if bytesArraySize then self:Error("Cannot initialize value: is an array") end - - local c,v = self:ConstantExpression(true) - if not c then - -- initializerLeaves = { self:Expression() } - self:Error("Cannot have expressions in global initializers") - else - initializerValues = { v } - end - end - else -- Local init always an expression - if self:MatchToken(TOKEN.LBRACKET) then -- Array initializer - if not bytesArraySize then self:Error("Cannot initialize value: not an array") end - - initializerLeaves = {} - while not self:MatchToken(TOKEN.RBRACKET) do - table.insert(initializerLeaves,self:Expression()) - self:MatchToken(TOKEN.COMMA) - end - - if #initializerLeaves > 256 then - self:Error("Too much local variable initializers") - end - else - if bytesArraySize then self:Error("Cannot initialize value: is an array") end - initializerLeaves = { self:Expression() } - end - end - end - - -- Define a variable - if self.LocalLabels then -- check if var is local - local label = self:DefineLabel(varName,true) - - if isRegisterDecl then - label.Type = "Register" - label.Value = self:DeclareRegisterVariable() - self.RegisterIdentities[varName] = label.Value - if isStruct then self:Error("Cannot hold structure variables in registers - yet") end - else - label.Type = "Stack" - if isStruct and (pointerLevel > 0) then label.PointerToStruct = true end - end - label.Defined = true - if varType == 5 then label.ForceType = "vector" end - if isStruct then label.Struct = varType end - - -- If label has associated array size, mark it as an array - if bytesArraySize then label.Array = bytesArraySize end - - if not isRegisterDecl then - if not isFunctionParam then - -- Add a new local variable (stack pointer increments) - self.StackPointer = self.StackPointer - (bytesArraySize or varSize) - label.StackOffset = self.StackPointer - else - -- Add a new function variable - self.ParameterPointer = self.ParameterPointer + (bytesArraySize or varSize) - label.StackOffset = self.ParameterPointer - end - end - - -- Initialize local variable - if isRegisterDecl then - if initializerLeaves then - local movLeaf = self:NewLeaf() - movLeaf.Opcode = "mov" - movLeaf.Operands[1] = { Register = label.Value } - movLeaf.Operands[2] = initializerLeaves[1] - movLeaf.ExplictAssign = true - self:AddLeafToTail(movLeaf) - end - else - if initializerLeaves then - for i=1,#initializerLeaves do -- FIXME: find a nicer way to initialize - local movLeaf = self:NewLeaf() - movLeaf.Opcode = "mov" - movLeaf.Operands[1] = { Stack = label.StackOffset+i-1 } - movLeaf.Operands[2] = initializerLeaves[i] - movLeaf.ExplictAssign = true - self:AddLeafToTail(movLeaf) - end - for i=#initializerLeaves+1,bytesArraySize or 1 do - local movLeaf = self:NewLeaf() - movLeaf.Opcode = "mov" - movLeaf.Operands[1] = { Stack = label.StackOffset+i-1 } - movLeaf.Operands[2] = { Constant = 0 } - movLeaf.ExplictAssign = true - self:AddLeafToTail(movLeaf) - end - end - end - else - -- Define a new global variable - local label = self:DefineLabel(varName) - - if isRegisterDecl then - label.Type = "Register" - label.Value = self:DeclareRegisterVariable() - self.RegisterIdentities[varName] = label.Value - else - label.Type = "Variable" - if isStruct and (pointerLevel > 0) then label.PointerToStruct = true end - end - label.Defined = true - if varType == 5 then label.ForceType = "vector" end - if isStruct then label.Struct = varType end - - -- If label has associated array size, mark it as an array - if bytesArraySize then label.Array = bytesArraySize end - - -- Create initialization leaf - label.Leaf = self:NewLeaf() - label.Leaf.ParentLabel = self.CurrentParentLabel or label - label.Leaf.Opcode = "DATA" - if initializerValues then - label.Leaf.Data = initializerValues - label.Leaf.ZeroPadding = (bytesArraySize or varSize) - #initializerValues - else - label.Leaf.ZeroPadding = bytesArraySize or varSize - end - label.Leaf.Label = label - self:AddLeafToTail(label.Leaf) - end - end - else -- Struct member - -- Do nothing right now - end - - if not self:MatchToken(TOKEN.COMMA) then - return true,labelsList - else --int x, char y, float z - local nextToken,structName = self:PeekToken(0,true) - if (nextToken == TOKEN.IDENT) and (self.Structs[structName]) then - self:MatchToken(TOKEN.IDENT) - local structData = self.Structs[structName] - varType = self.TokenData - varSize = 0 - isStruct = true - elseif self:MatchToken(TOKEN.TYPE) then - varType = self.TokenData - varSize = 1 - if varType == 5 then varSize = 4 end - isStruct = false - end - end - end + local varType,varSize,isStruct + if self:MatchToken(TOKEN.IDENT) then -- Define structure + varType = self.TokenData + varSize = 0 -- Depends on pointer level + isStruct = true + WireTextEditor.Modes.ZCPU.keywordsTable[string.upper(varType)] = true -- Add the new struct to the keywords table. + else -- Define variable + self:ExpectToken(TOKEN.TYPE) + varType = self.TokenData + varSize = 1 + if varType == 5 then varSize = 4 end + end + + -- Variable labels list + local labelsList = {} + + -- Parse all variables to define + while true do + -- Get pointer level (0, *, **, ***, etc) + local pointerLevel = 0 + while self:MatchToken(TOKEN.TIMES) do pointerLevel = pointerLevel + 1 end + + -- Fix structure size + if isStruct then + if pointerLevel > 0 + then varSize = 1 + else varSize = self.StructSize[varType] + end + end + + -- Get variable name + self:ExpectToken(TOKEN.IDENT) + local varName = self.TokenData + + -- Try to read information about array size, stuff + local arraySize + while self:MatchToken(TOKEN.LSUBSCR) do -- varname[] + if self:MatchToken(TOKEN.RSUBSCR) then -- varname[] + if isFunctionParam then -- just a pointer to an array + pointerLevel = 1 + end + else + local c,v = self:ConstantExpression(true) -- need precise value here, no ptrs allowed + if c then + if not arraySize then arraySize = {} end + arraySize[#arraySize+1] = v + else + self:Error("Array size must be constant") + end + + self:ExpectToken(TOKEN.RSUBSCR) + end + end + + -- Calculate size of array + local bytesArraySize + if arraySize then + for k,v in pairs(arraySize) do + bytesArraySize = (bytesArraySize or 0) + v*varSize + end + end + + -- Add to global list + table.insert(labelsList,{ Name = varName, Type = varType, PtrLevel = pointerLevel, Size = bytesArraySize or varSize }) + + if not isStructMember then -- Do not define struct members + if self:MatchToken(TOKEN.LPAREN) then -- Define function + -- Create function entrypoint + local label + label = self:DefineLabel(varName) + self:PreviousToken() -- LPAREN + self:PreviousToken() -- Func Name + self:PreviousToken() -- Type Name + local ptrlevel = 0 + if self:MatchToken(TOKEN.TIMES) then + -- skip back until we're done with the ptr + self:PreviousToken() + while self:MatchToken(TOKEN.TIMES) do + ptrlevel = ptrlevel + 1; + self:PreviousToken() + self:PreviousToken() + end + end + if not self:MatchToken(TOKEN.IDENT) then + self:MatchToken(TOKEN.TYPE) -- If it's not an IDENT (struct/user defined) it should be a generic type + end + local returnType = self.TokenData + self.CurrentToken = self.CurrentToken + ptrlevel -- return to present. + self:MatchToken(TOKEN.IDENT) + local funcName = self.TokenData + self.CurFunction = {Name = funcName, ReturnType = returnType, ReturnPtrLevel = ptrlevel} + self:NextToken() + label.Type = "Pointer" + label.Defined = true + + -- Make all further leaves parented to this label + self.CurrentParentLabel = label + + -- Create label leaf + label.Leaf = self:NewLeaf() + label.Leaf.Opcode = "LABEL" + label.Leaf.Label = label + self:AddLeafToTail(label.Leaf) --isInlined + + -- Define a function + local _,functionVariables = nil,{} + + self:BlockStart() + if not self:MatchToken(TOKEN.RPAREN) then + _,functionVariables = self:DefineVariable(true) + self:ExpectToken(TOKEN.RPAREN) + + -- Add comments about function into assembly listing + if self.Settings.GenerateComments then + for i=1,#functionVariables do + label.Leaf.Comment = (label.Leaf.Comment or "")..(functionVariables[i].Name) + if i < #functionVariables then label.Leaf.Comment = label.Leaf.Comment.."," end + end + end + end + + -- Forward declaration, mess up label name + if isForwardDecl then + local newName = label.Name.."@" + for i=1,#functionVariables do + newName = newName..functionVariables[i].Name..functionVariables[i].Type + if i < #functionVariables then + newName = newName.."_" + end + end + self:RedefineLabel(label.Name,newName) + end + + -- Generate comment if required + if self.Settings.GenerateComments then label.Leaf.Comment = varName.."("..(label.Leaf.Comment or "")..")" end + self:ExpectToken(TOKEN.LBRACKET) + return true,functionVariables,varName,varType,pointerLevel + else -- Define variable + -- Check if there's an initializer + local initializerLeaves,initializerValues + if self:MatchToken(TOKEN.EQUAL) then + if not self.LocalLabels then -- Check rules for global init + if self:MatchToken(TOKEN.LBRACKET) then -- Array initializer + if not bytesArraySize then self:Error("Cannot initialize value: not an array") end + + initializerValues = {} + while not self:MatchToken(TOKEN.RBRACKET) do + local c,v = self:ConstantExpression(true) + if not c + then self:Error("Cannot have expressions in global initializers") + else table.insert(initializerValues,v) + end + self:MatchToken(TOKEN.COMMA) + end + else -- Single initializer + if bytesArraySize then self:Error("Cannot initialize value: is an array") end + + local c,v = self:ConstantExpression(true) + if not c then + -- initializerLeaves = { self:Expression() } + self:Error("Cannot have expressions in global initializers") + else + initializerValues = { v } + end + end + else -- Local init always an expression + if self:MatchToken(TOKEN.LBRACKET) then -- Array initializer + if not bytesArraySize then self:Error("Cannot initialize value: not an array") end + + initializerLeaves = {} + while not self:MatchToken(TOKEN.RBRACKET) do + table.insert(initializerLeaves,self:Expression()) + self:MatchToken(TOKEN.COMMA) + end + + if #initializerLeaves > 256 then + self:Error("Too much local variable initializers") + end + else + if bytesArraySize then self:Error("Cannot initialize value: is an array") end + initializerLeaves = { self:Expression() } + end + end + end + + -- Define a variable + if self.LocalLabels then -- check if var is local + local label = self:DefineLabel(varName,true) + + if isRegisterDecl then + label.Type = "Register" + label.Value = self:DeclareRegisterVariable() + self.RegisterIdentities[varName] = label.Value + if isStruct then self:Error("Cannot hold structure variables in registers - yet") end + else + label.Type = "Stack" + if isStruct and (pointerLevel > 0) then label.PointerToStruct = true end + end + label.Defined = true + if varType == 5 then label.ForceType = "vector" end + if isStruct then label.Struct = varType end + + -- If label has associated array size, mark it as an array + if bytesArraySize then label.Array = bytesArraySize end + + if not isRegisterDecl then + if not isFunctionParam then + -- Add a new local variable (stack pointer increments) + self.StackPointer = self.StackPointer - (bytesArraySize or varSize) + label.StackOffset = self.StackPointer + else + -- Add a new function variable + self.ParameterPointer = self.ParameterPointer + (bytesArraySize or varSize) + label.StackOffset = self.ParameterPointer + end + end + + -- Initialize local variable + if isRegisterDecl then + if initializerLeaves then + local movLeaf = self:NewLeaf() + movLeaf.Opcode = "mov" + movLeaf.Operands[1] = { Register = label.Value } + movLeaf.Operands[2] = initializerLeaves[1] + movLeaf.ExplictAssign = true + self:AddLeafToTail(movLeaf) + end + else + if initializerLeaves then + for i=1,#initializerLeaves do -- FIXME: find a nicer way to initialize + local movLeaf = self:NewLeaf() + movLeaf.Opcode = "mov" + movLeaf.Operands[1] = { Stack = label.StackOffset+i-1 } + movLeaf.Operands[2] = initializerLeaves[i] + movLeaf.ExplictAssign = true + self:AddLeafToTail(movLeaf) + end + for i=#initializerLeaves+1,bytesArraySize or 1 do + local movLeaf = self:NewLeaf() + movLeaf.Opcode = "mov" + movLeaf.Operands[1] = { Stack = label.StackOffset+i-1 } + movLeaf.Operands[2] = { Constant = 0 } + movLeaf.ExplictAssign = true + self:AddLeafToTail(movLeaf) + end + end + end + else + -- Define a new global variable + local label = self:DefineLabel(varName) + + if isRegisterDecl then + label.Type = "Register" + label.Value = self:DeclareRegisterVariable() + self.RegisterIdentities[varName] = label.Value + else + label.Type = "Variable" + if isStruct and (pointerLevel > 0) then label.PointerToStruct = true end + end + label.Defined = true + if varType == 5 then label.ForceType = "vector" end + if isStruct then label.Struct = varType end + + -- If label has associated array size, mark it as an array + if bytesArraySize then label.Array = bytesArraySize end + + -- Create initialization leaf + label.Leaf = self:NewLeaf() + label.Leaf.ParentLabel = self.CurrentParentLabel or label + label.Leaf.Opcode = "DATA" + if initializerValues then + label.Leaf.Data = initializerValues + label.Leaf.ZeroPadding = (bytesArraySize or varSize) - #initializerValues + else + label.Leaf.ZeroPadding = bytesArraySize or varSize + end + label.Leaf.Label = label + self:AddLeafToTail(label.Leaf) + end + end + else -- Struct member + -- Do nothing right now + end + + if not self:MatchToken(TOKEN.COMMA) then + return true,labelsList + else --int x, char y, float z + local nextToken,structName = self:PeekToken(0,true) + if (nextToken == TOKEN.IDENT) and (self.Structs[structName]) then + self:MatchToken(TOKEN.IDENT) + local structData = self.Structs[structName] + varType = self.TokenData + varSize = 0 + isStruct = true + elseif self:MatchToken(TOKEN.TYPE) then + varType = self.TokenData + varSize = 1 + if varType == 5 then varSize = 4 end + isStruct = false + end + end + end end -------------------------------------------------------------------------------- -- Compile a single statement function HCOMP:Statement() local TOKEN,TOKENSET = self.TOKEN,self.TOKENSET - -- Parse code for absolute labels and define (LABEL:) - if self.CurrentToken == 1 then + -- Parse code for absolute labels and define (LABEL:) + if self.CurrentToken == 1 then while not(self:MatchToken(TOKEN.EOF)) do - if self:MatchToken(TOKEN.IDENT) then - if(self:PeekToken() == TOKEN.DCOLON) then - local label = self:DefineLabel(self.TokenData) - label.Type = "Pointer" - label.Defined = true - else - self:PreviousToken() - end - end - self:NextToken() - end + if self:MatchToken(TOKEN.IDENT) then + if(self:PeekToken() == TOKEN.DCOLON) then + local label = self:DefineLabel(self.TokenData) + label.Type = "Pointer" + label.Defined = true + else + self:PreviousToken() + end + end + self:NextToken() + end self.CurrentToken = 1 - end - - - -- Parse end of line colon - if self:MatchToken(TOKEN.COLON) then return true end - - -- Check for EOF - if self:MatchToken(TOKEN.EOF) then return false end - - -- Parse variable/function definition - local exportSymbol = self:MatchToken(TOKEN.EXPORT) - local inlineFunction = self:MatchToken(TOKEN.INLINE) - local forwardFunction = self:MatchToken(TOKEN.FORWARD) - local registerValue = self:MatchToken(TOKEN.LREGISTER) - - if self:PeekToken() == TOKEN.TYPE then - if inlineFunction then - self.GenerateInlineFunction = true - self.InlineFunctionCode = {} - end - - local isDefined,variableList,functionName,returnType,returnPtrLevel = self:DefineVariable(false,forwardFunction,registerValue) - if isDefined then - if functionName then - self.Functions[functionName] = { - FunctionName = functionName, - Parameters = variableList, - ReturnType = returnType, - ReturnPtrLevel = returnPtrLevel, - } - if exportSymbol then - self.ExportedSymbols[functionName] = self.Functions[functionName] - end - if inlineFunction then - self.GenerateInlineFunction = functionName - end - else - if exportSymbol then - self:Error("Exporting variables not supported right now by the compiler") - end - end - end - - if inlineFunction and (not functionName) then - self:Error("Can only inline functions") - end - if forwardFunction and (not functionName) then - self:Error("Can only forward-declare functions") - end - return isDefined - end - - -- Peek structure definition - local nextToken,structName = self:PeekToken(0,true) - if (nextToken == TOKEN.IDENT) and (self.Structs[structName]) then - self:DefineVariable() - return true - end - - if inlineFunction or exportSymbol or forwardFunction or registerValue then - self:Error("Function definition or symbol definition expected") - end - - -- Parse preserve/zap - if self:MatchToken(TOKEN.PRESERVE) or self:MatchToken(TOKEN.ZAP) then - local tokenType = self.TokenType - if self.BlockDepth > 0 then - while self:MatchToken(TOKEN.REGISTER) or self:MatchToken(TOKEN.IDENT) do - -- Don't error on catching a variable being used near a zap/preserve - if self:MatchToken(TOKENSET.OPERATORS) then - -- move back 2 tokens and then re-parse this - self:PreviousToken() - self:PreviousToken() - return self:Statement() - end - if self.TokenType == TOKEN.IDENT then - if self.RegisterIdentities[self.TokenData] then - if tokenType == TOKEN.PRESERVE then - self:Error("Trying to preserve a register variable") - end - if self.RegisterIdentities[self.TokenData] == -1 then - self:Error("Trying to zap variable twice") - end - self.BusyRegisters[self.RegisterIdentities[self.TokenData]] = tokenType == TOKEN.PRESERVE - self.RegisterIdentities[self.TokenData] = -1 -- so we can error on attempted use after zap - if self:MatchToken(TOKEN.MINUS) then - self:Error("Cannot zap ranges using register variables") - end - else - if tokenType == TOKEN.PRESERVE then - self:Error("Trying to preserve a variable") - end - self:Error("Trying to zap a non register variable") - end - end - if self.TokenType == TOKEN.REGISTER then - local prevTokenData = self.TokenData - self.BusyRegisters[self.TokenData] = tokenType == TOKEN.PRESERVE - if self:MatchToken(TOKEN.MINUS) then -- match range of registers - if self:MatchToken(TOKEN.REGISTER) or self:MatchToken(TOKEN.IDENT) then - if self.TokenType == TOKEN.IDENT then - self:Error("Cannot zap ranges using register variables") - end - if prevTokenData > self.TokenData then - self:Error("Start of register range is greater than end of register range") - end - for i=prevTokenData,self.TokenData do - if self.RegisterName[i] ~= "N/A" then - self.BusyRegisters[i] = tokenType == TOKEN.PRESERVE - end - end - else - self:Error("Invalid register range") - end - end - end - self:MatchToken(TOKEN.COMMA) - end - self:MatchToken(TOKEN.COLON) - return true - else - self:Error("Can only zap/preserve registers inside functions/local blocks") - end - end - - -- Parse assembly instruction - if self:MatchToken(TOKEN.OPCODE) then return self:Opcode() end - - -- Parse STRUCT macro - if self:MatchToken(TOKEN.STRUCT) then - self:ExpectToken(TOKEN.IDENT) - local structName = self.TokenData - - -- Create structure - self.Structs[structName] = {} - self.StructSize[structName] = 0 - - -- Populate structure - self:ExpectToken(TOKEN.LBRACKET) - while (not self:MatchToken(TOKEN.RBRACKET)) and (not self:MatchToken(TOKEN.EOF)) do - local _,variableList = self:DefineVariable(false,false,false,true) - for _,variableData in ipairs(variableList) do - variableData.Offset = self.StructSize[structName] - self.Structs[structName][variableData.Name] = variableData - self.StructSize[structName] = self.StructSize[structName] + variableData.Size - end - self:ExpectToken(TOKEN.COLON) - end - return true - end - - -- Parse VECTOR macro - if self:MatchToken(TOKEN.VECTOR) then - if self.BlockDepth > 0 then - self:Warning("Defining a vector inside a function block might cause issues") - end - - -- Vector type (VEC2F, etc) - local vectorType = self.TokenData - - -- Vector name - self:ExpectToken(TOKEN.IDENT) - local vectorName = self.TokenData - - -- Create leaf and label for vector name - local vectorNameLabelLeaf = self:NewLeaf() - vectorNameLabelLeaf.Opcode = "LABEL" - - local vectorNameLabel = self:DefineLabel(vectorName) - vectorNameLabel.Type = "Pointer" - vectorNameLabel.Defined = true - vectorNameLabel.Leaf = vectorNameLabelLeaf - vectorNameLabel.DebugAsVector = #VectorSyntax[vectorType] - vectorNameLabelLeaf.Label = vectorNameLabel - self:AddLeafToTail(vectorNameLabelLeaf) - - -- Create leaves for all vector labels and their data - local vectorLeaves = {} - for index,labelNames in pairs(VectorSyntax[vectorType]) do - -- Create leaves for labels - for labelIndex,labelName in pairs(labelNames) do - local vectorLabelLeaf = self:NewLeaf() - vectorLabelLeaf.Opcode = "LABEL" - - local vectorLabel = self:GetLabel(vectorName.."."..labelName) - vectorLabel.Type = "Pointer" - vectorLabel.Defined = true - vectorLabel.Leaf = vectorLabelLeaf - vectorLabelLeaf.Label = vectorLabel - self:AddLeafToTail(vectorLabelLeaf) - end - - -- Create leaf for data - vectorLeaves[index] = self:NewLeaf() - vectorLeaves[index].Opcode = "DATA" - vectorLeaves[index].Data = { 0 } - self:AddLeafToTail(vectorLeaves[index]) - - if vectorType == "COLOR" then - vectorLeaves[index].Data = { 255 } - end - end - - -- Parse initialization - self.MostLikelyConstantExpression = true - if self:MatchToken(TOKEN.COMMA) then - for index,labelNames in pairs(VectorSyntax[vectorType]) do - local c,v,e = self:ConstantExpression(false) - if c then - vectorLeaves[index].Data[1] = v or e - else - self:Error("Vector initialization must be constant") - end - - if (index == #VectorSyntax[vectorType]) and self:MatchToken(TOKEN.COMMA) then - self:Error("Too much values for intialization") - end - if (index < #VectorSyntax[vectorType]) and (not self:MatchToken(TOKEN.COMMA)) then - return true - end - end - end - self.MostLikelyConstantExpression = false - return true - end - - -- Parse DATA macro - if self:MatchToken(TOKEN.DATA) then - local jmpLeaf = self:NewLeaf() - jmpLeaf.Opcode = "jmp" - jmpLeaf.Operands[1] = { - Constant = {{ Type = TOKEN.IDENT, Data = "_code", Position = self:CurrentSourcePosition() }} - } - self:AddLeafToTail(jmpLeaf) - return true - end - - -- Parse CODE macro - if self:MatchToken(TOKEN.CODE) then - local label = self:DefineLabel("_code") - label.Type = "Pointer" - - label.Leaf = self:NewLeaf() - label.Leaf.Opcode = "LABEL" - label.Leaf.Label = label - self:AddLeafToTail(label.Leaf) - return true - end - - -- Parse ORG macro - if self:MatchToken(TOKEN.ORG) then - -- org x - local markerLeaf = self:NewLeaf() - markerLeaf.Opcode = "MARKER" - - local c,v = self:ConstantExpression(true) - if c then markerLeaf.SetWritePointer = v - else self:Error("ORG offset must be constant") end - - self:AddLeafToTail(markerLeaf) - return true - end - - -- Parse OFFSET macro - if self:MatchToken(TOKEN.OFFSET) then - -- offset x - local markerLeaf = self:NewLeaf() - markerLeaf.Opcode = "MARKER" - - local c,v = self:ConstantExpression(true) - if c then markerLeaf.SetPointerOffset = v - else self:Error("OFFSET offset must be constant") end - - self:AddLeafToTail(markerLeaf) - return true - end - - -- Parse DB macro - if self:MatchToken(TOKEN.DB) then - -- db 1,... - self.IgnoreStringInExpression = true - self.MostLikelyConstantExpression = true - local dbLeaf = self:NewLeaf() - dbLeaf.Opcode = "DATA" - dbLeaf.Data = {} - local c,v,e = self:ConstantExpression(false) - while c or (self:PeekToken() == TOKEN.STRING) do - -- Insert data into leaf - if self:MatchToken(TOKEN.STRING) then - table.insert(dbLeaf.Data,self.TokenData) - else - table.insert(dbLeaf.Data,v or e) - end - - -- Only keep parsing if next token is comma - if self:MatchToken(TOKEN.COMMA) then - c,v,e = self:ConstantExpression(false) - else - c = false - end - end - self.IgnoreStringInExpression = false - self.MostLikelyConstantExpression = false - - self:AddLeafToTail(dbLeaf) - return true - end - - -- Parse STRING macro - if self:MatchToken(TOKEN.STRALLOC) then - -- string name,1,... - self:ExpectToken(TOKEN.IDENT) - - -- Create leaf and label for vector name - local stringNameLabelLeaf = self:NewLeaf() - stringNameLabelLeaf.Opcode = "LABEL" - - local stringNameLabel = self:DefineLabel(self.TokenData) - stringNameLabel.Type = "Pointer" - stringNameLabel.Defined = true - stringNameLabel.Leaf = stringNameLabelLeaf - stringNameLabelLeaf.Label = stringNameLabel - self:AddLeafToTail(stringNameLabelLeaf) - self:ExpectToken(TOKEN.COMMA) - - self.IgnoreStringInExpression = true - self.MostLikelyConstantExpression = true - local stringLeaf = self:NewLeaf() - stringLeaf.Opcode = "DATA" - stringLeaf.Data = {} - local c,v,e = self:ConstantExpression(false) - while c or (self:PeekToken() == TOKEN.STRING) do - -- Insert data into leaf - if self:MatchToken(TOKEN.STRING) then - table.insert(stringLeaf.Data,self.TokenData) - else - table.insert(stringLeaf.Data,v or e) - end - - -- Only keep parsing if next token is comma - if self:MatchToken(TOKEN.COMMA) then - c,v,e = self:ConstantExpression(false) - else - c = false - end - end - table.insert(stringLeaf.Data,0) - self.IgnoreStringInExpression = false - self.MostLikelyConstantExpression = false - - self:AddLeafToTail(stringLeaf) - return true - end - - -- Parse DEFINE macro - if self:MatchToken(TOKEN.DEFINE) then - -- define label,value - self:ExpectToken(TOKEN.IDENT) - local defineLabel = self:DefineLabel(self.TokenData) - defineLabel.Type = "Pointer" - defineLabel.Defined = true - - self:ExpectToken(TOKEN.COMMA) - - self.MostLikelyConstantExpression = true - local c,v,e = self:ConstantExpression(false) - if c then - if v then - defineLabel.Value = v - else - defineLabel.Expression = e - end - else - self:Error("Define value must be constant") - end - self.MostLikelyConstantExpression = false - - return true - end - - -- Parse ALLOC macro - if self:MatchToken(TOKEN.ALLOC) then - -- alloc label,size,value - -- alloc label,value - -- alloc label - -- alloc size - local allocLeaf = self:NewLeaf() - local allocLabel,allocSize,allocValue = nil,1,0 - local expectSize = false - allocLeaf.Opcode = "DATA" - - -- Add a label to this alloc - if self:MatchToken(TOKEN.IDENT) then - allocLabel = self:DefineLabel(self.TokenData) - allocLabel.Type = "Pointer" - allocLabel.Defined = true - allocLabel.DebugAsVariable = true - - allocLabel.Leaf = allocLeaf - allocLeaf.Label = allocLabel - - if self:MatchToken(TOKEN.COMMA) then expectSize = true end - end - - -- Read size - self.MostLikelyConstantExpression = true - if (not allocLabel) or (expectSize) then - local c,v = self:ConstantExpression(true) -- need precise value here, no ptrs allowed - if c then allocSize = v - else self:Error("Alloc size must be constant") end - end - - if allocLabel and expectSize then - if self:MatchToken(TOKEN.COMMA) then - local c,v = self:ConstantExpression(true) -- need precise value here, no ptrs allowed - if c then allocValue = v - else self:Error("Alloc value must be constant") end - else - allocValue = allocSize - allocSize = 1 - end - end - self.MostLikelyConstantExpression = false - - -- Initialize alloc - allocLeaf.ZeroPadding = allocSize - self:AddLeafToTail(allocLeaf) - return true - end - - - - - - -- Parse RETURN - if self:MatchToken(TOKEN.RETURN) and self.HeadLeaf then - if not self:MatchToken(TOKEN.COLON) then - local returnExpression = self:Expression() - local copyLargeVariable = false - if self.StructSize[self.CurFunction.ReturnType] then - copyLargeVariable = true -- Converts this to automatically return ptr to large variable - end - local returnLeaf = self:NewLeaf() - local preReturnLeaf,postReturnLeaf - if copyLargeVariable then - local finalReturnExpression = returnExpression - returnLeaf.Opcode = "mov" - returnLeaf.Operands[1] = { Register = 1 } - if returnExpression.Stack then - finalReturnExpression = { Register = 8 } -- EBP, the stack frame register, where the stack ptr is local to - postReturnLeaf = self:NewOpcode("add",returnLeaf.Operands[1],{Constant = returnExpression.Stack}) - -- MOV EAX,EBP - -- ADD EAX,(stack-pos) - end - returnLeaf.Operands[2] = finalReturnExpression - returnLeaf.ExplictAssign = true - else - returnLeaf.Opcode = "mov" - returnLeaf.Operands[1] = { Register = 1 } - returnLeaf.Operands[2] = returnExpression - returnLeaf.ExplictAssign = true - end - if preReturnLeaf then - self:AddLeafToTail(preReturnLeaf) - end - self:AddLeafToTail(returnLeaf) - if postReturnLeaf then - self:AddLeafToTail(postReturnLeaf) - end - end - self:MatchToken(TOKEN.COLON) - - -- Check if this is the last return in the function + end + + + -- Parse end of line colon + if self:MatchToken(TOKEN.COLON) then return true end + + -- Check for EOF + if self:MatchToken(TOKEN.EOF) then return false end + + -- Parse variable/function definition + local exportSymbol = self:MatchToken(TOKEN.EXPORT) + local inlineFunction = self:MatchToken(TOKEN.INLINE) + local forwardFunction = self:MatchToken(TOKEN.FORWARD) + local registerValue = self:MatchToken(TOKEN.LREGISTER) + + if self:PeekToken() == TOKEN.TYPE then + if inlineFunction then + self.GenerateInlineFunction = true + self.InlineFunctionCode = {} + end + + local isDefined,variableList,functionName,returnType,returnPtrLevel = self:DefineVariable(false,forwardFunction,registerValue) + if isDefined then + if functionName then + self.Functions[functionName] = { + FunctionName = functionName, + Parameters = variableList, + ReturnType = returnType, + ReturnPtrLevel = returnPtrLevel, + } + if exportSymbol then + self.ExportedSymbols[functionName] = self.Functions[functionName] + end + if inlineFunction then + self.GenerateInlineFunction = functionName + end + else + if exportSymbol then + self:Error("Exporting variables not supported right now by the compiler") + end + end + end + + if inlineFunction and (not functionName) then + self:Error("Can only inline functions") + end + if forwardFunction and (not functionName) then + self:Error("Can only forward-declare functions") + end + return isDefined + end + + -- Peek structure definition + local nextToken,structName = self:PeekToken(0,true) + if (nextToken == TOKEN.IDENT) and (self.Structs[structName]) then + self:DefineVariable() + return true + end + + if inlineFunction or exportSymbol or forwardFunction or registerValue then + self:Error("Function definition or symbol definition expected") + end + + -- Parse preserve/zap + if self:MatchToken(TOKEN.PRESERVE) or self:MatchToken(TOKEN.ZAP) then + local tokenType = self.TokenType + if self.BlockDepth > 0 then + while self:MatchToken(TOKEN.REGISTER) or self:MatchToken(TOKEN.IDENT) do + -- Don't error on catching a variable being used near a zap/preserve + if self:MatchToken(TOKENSET.OPERATORS) then + -- move back 2 tokens and then re-parse this + self:PreviousToken() + self:PreviousToken() + return self:Statement() + end + if self.TokenType == TOKEN.IDENT then + if self.RegisterIdentities[self.TokenData] then + if tokenType == TOKEN.PRESERVE then + self:Error("Trying to preserve a register variable") + end + if self.RegisterIdentities[self.TokenData] == -1 then + self:Error("Trying to zap variable twice") + end + self.BusyRegisters[self.RegisterIdentities[self.TokenData]] = tokenType == TOKEN.PRESERVE + self.RegisterIdentities[self.TokenData] = -1 -- so we can error on attempted use after zap + if self:MatchToken(TOKEN.MINUS) then + self:Error("Cannot zap ranges using register variables") + end + else + if tokenType == TOKEN.PRESERVE then + self:Error("Trying to preserve a variable") + end + self:Error("Trying to zap a non register variable") + end + end + if self.TokenType == TOKEN.REGISTER then + local prevTokenData = self.TokenData + self.BusyRegisters[self.TokenData] = tokenType == TOKEN.PRESERVE + if self:MatchToken(TOKEN.MINUS) then -- match range of registers + if self:MatchToken(TOKEN.REGISTER) or self:MatchToken(TOKEN.IDENT) then + if self.TokenType == TOKEN.IDENT then + self:Error("Cannot zap ranges using register variables") + end + if prevTokenData > self.TokenData then + self:Error("Start of register range is greater than end of register range") + end + for i=prevTokenData,self.TokenData do + if self.RegisterName[i] ~= "N/A" then + self.BusyRegisters[i] = tokenType == TOKEN.PRESERVE + end + end + else + self:Error("Invalid register range") + end + end + end + self:MatchToken(TOKEN.COMMA) + end + self:MatchToken(TOKEN.COLON) + return true + else + self:Error("Can only zap/preserve registers inside functions/local blocks") + end + end + + -- Parse assembly instruction + if self:MatchToken(TOKEN.OPCODE) then return self:Opcode() end + + -- Parse STRUCT macro + if self:MatchToken(TOKEN.STRUCT) then + self:ExpectToken(TOKEN.IDENT) + local structName = self.TokenData + + -- Create structure + self.Structs[structName] = {} + self.StructSize[structName] = 0 + + -- Populate structure + self:ExpectToken(TOKEN.LBRACKET) + while (not self:MatchToken(TOKEN.RBRACKET)) and (not self:MatchToken(TOKEN.EOF)) do + local _,variableList = self:DefineVariable(false,false,false,true) + for _,variableData in ipairs(variableList) do + variableData.Offset = self.StructSize[structName] + self.Structs[structName][variableData.Name] = variableData + self.StructSize[structName] = self.StructSize[structName] + variableData.Size + end + self:ExpectToken(TOKEN.COLON) + end + return true + end + + -- Parse VECTOR macro + if self:MatchToken(TOKEN.VECTOR) then + if self.BlockDepth > 0 then + self:Warning("Defining a vector inside a function block might cause issues") + end + + -- Vector type (VEC2F, etc) + local vectorType = self.TokenData + + -- Vector name + self:ExpectToken(TOKEN.IDENT) + local vectorName = self.TokenData + + -- Create leaf and label for vector name + local vectorNameLabelLeaf = self:NewLeaf() + vectorNameLabelLeaf.Opcode = "LABEL" + + local vectorNameLabel = self:DefineLabel(vectorName) + vectorNameLabel.Type = "Pointer" + vectorNameLabel.Defined = true + vectorNameLabel.Leaf = vectorNameLabelLeaf + vectorNameLabel.DebugAsVector = #VectorSyntax[vectorType] + vectorNameLabelLeaf.Label = vectorNameLabel + self:AddLeafToTail(vectorNameLabelLeaf) + + -- Create leaves for all vector labels and their data + local vectorLeaves = {} + for index,labelNames in pairs(VectorSyntax[vectorType]) do + -- Create leaves for labels + for labelIndex,labelName in pairs(labelNames) do + local vectorLabelLeaf = self:NewLeaf() + vectorLabelLeaf.Opcode = "LABEL" + + local vectorLabel = self:GetLabel(vectorName.."."..labelName) + vectorLabel.Type = "Pointer" + vectorLabel.Defined = true + vectorLabel.Leaf = vectorLabelLeaf + vectorLabelLeaf.Label = vectorLabel + self:AddLeafToTail(vectorLabelLeaf) + end + + -- Create leaf for data + vectorLeaves[index] = self:NewLeaf() + vectorLeaves[index].Opcode = "DATA" + vectorLeaves[index].Data = { 0 } + self:AddLeafToTail(vectorLeaves[index]) + + if vectorType == "COLOR" then + vectorLeaves[index].Data = { 255 } + end + end + + -- Parse initialization + self.MostLikelyConstantExpression = true + if self:MatchToken(TOKEN.COMMA) then + for index,labelNames in pairs(VectorSyntax[vectorType]) do + local c,v,e = self:ConstantExpression(false) + if c then + vectorLeaves[index].Data[1] = v or e + else + self:Error("Vector initialization must be constant") + end + + if (index == #VectorSyntax[vectorType]) and self:MatchToken(TOKEN.COMMA) then + self:Error("Too much values for intialization") + end + if (index < #VectorSyntax[vectorType]) and (not self:MatchToken(TOKEN.COMMA)) then + return true + end + end + end + self.MostLikelyConstantExpression = false + return true + end + + -- Parse DATA macro + if self:MatchToken(TOKEN.DATA) then + local jmpLeaf = self:NewLeaf() + jmpLeaf.Opcode = "jmp" + jmpLeaf.Operands[1] = { + Constant = {{ Type = TOKEN.IDENT, Data = "_code", Position = self:CurrentSourcePosition() }} + } + self:AddLeafToTail(jmpLeaf) + return true + end + + -- Parse CODE macro + if self:MatchToken(TOKEN.CODE) then + local label = self:DefineLabel("_code") + label.Type = "Pointer" + + label.Leaf = self:NewLeaf() + label.Leaf.Opcode = "LABEL" + label.Leaf.Label = label + self:AddLeafToTail(label.Leaf) + return true + end + + -- Parse ORG macro + if self:MatchToken(TOKEN.ORG) then + -- org x + local markerLeaf = self:NewLeaf() + markerLeaf.Opcode = "MARKER" + + local c,v = self:ConstantExpression(true) + if c then markerLeaf.SetWritePointer = v + else self:Error("ORG offset must be constant") end + + self:AddLeafToTail(markerLeaf) + return true + end + + -- Parse OFFSET macro + if self:MatchToken(TOKEN.OFFSET) then + -- offset x + local markerLeaf = self:NewLeaf() + markerLeaf.Opcode = "MARKER" + + local c,v = self:ConstantExpression(true) + if c then markerLeaf.SetPointerOffset = v + else self:Error("OFFSET offset must be constant") end + + self:AddLeafToTail(markerLeaf) + return true + end + + -- Parse DB macro + if self:MatchToken(TOKEN.DB) then + -- db 1,... + self.IgnoreStringInExpression = true + self.MostLikelyConstantExpression = true + local dbLeaf = self:NewLeaf() + dbLeaf.Opcode = "DATA" + dbLeaf.Data = {} + local c,v,e = self:ConstantExpression(false) + while c or (self:PeekToken() == TOKEN.STRING) do + -- Insert data into leaf + if self:MatchToken(TOKEN.STRING) then + table.insert(dbLeaf.Data,self.TokenData) + else + table.insert(dbLeaf.Data,v or e) + end + + -- Only keep parsing if next token is comma + if self:MatchToken(TOKEN.COMMA) then + c,v,e = self:ConstantExpression(false) + else + c = false + end + end + self.IgnoreStringInExpression = false + self.MostLikelyConstantExpression = false + + self:AddLeafToTail(dbLeaf) + return true + end + + -- Parse STRING macro + if self:MatchToken(TOKEN.STRALLOC) then + -- string name,1,... + self:ExpectToken(TOKEN.IDENT) + + -- Create leaf and label for vector name + local stringNameLabelLeaf = self:NewLeaf() + stringNameLabelLeaf.Opcode = "LABEL" + + local stringNameLabel = self:DefineLabel(self.TokenData) + stringNameLabel.Type = "Pointer" + stringNameLabel.Defined = true + stringNameLabel.Leaf = stringNameLabelLeaf + stringNameLabelLeaf.Label = stringNameLabel + self:AddLeafToTail(stringNameLabelLeaf) + self:ExpectToken(TOKEN.COMMA) + + self.IgnoreStringInExpression = true + self.MostLikelyConstantExpression = true + local stringLeaf = self:NewLeaf() + stringLeaf.Opcode = "DATA" + stringLeaf.Data = {} + local c,v,e = self:ConstantExpression(false) + while c or (self:PeekToken() == TOKEN.STRING) do + -- Insert data into leaf + if self:MatchToken(TOKEN.STRING) then + table.insert(stringLeaf.Data,self.TokenData) + else + table.insert(stringLeaf.Data,v or e) + end + + -- Only keep parsing if next token is comma + if self:MatchToken(TOKEN.COMMA) then + c,v,e = self:ConstantExpression(false) + else + c = false + end + end + table.insert(stringLeaf.Data,0) + self.IgnoreStringInExpression = false + self.MostLikelyConstantExpression = false + + self:AddLeafToTail(stringLeaf) + return true + end + + -- Parse DEFINE macro + if self:MatchToken(TOKEN.DEFINE) then + -- define label,value + self:ExpectToken(TOKEN.IDENT) + local defineLabel = self:DefineLabel(self.TokenData) + defineLabel.Type = "Pointer" + defineLabel.Defined = true + + self:ExpectToken(TOKEN.COMMA) + + self.MostLikelyConstantExpression = true + local c,v,e = self:ConstantExpression(false) + if c then + if v then + defineLabel.Value = v + else + defineLabel.Expression = e + end + else + self:Error("Define value must be constant") + end + self.MostLikelyConstantExpression = false + + return true + end + + -- Parse ALLOC macro + if self:MatchToken(TOKEN.ALLOC) then + -- alloc label,size,value + -- alloc label,value + -- alloc label + -- alloc size + local allocLeaf = self:NewLeaf() + local allocLabel,allocSize,allocValue = nil,1,0 + local expectSize = false + allocLeaf.Opcode = "DATA" + + -- Add a label to this alloc + if self:MatchToken(TOKEN.IDENT) then + allocLabel = self:DefineLabel(self.TokenData) + allocLabel.Type = "Pointer" + allocLabel.Defined = true + allocLabel.DebugAsVariable = true + + allocLabel.Leaf = allocLeaf + allocLeaf.Label = allocLabel + + if self:MatchToken(TOKEN.COMMA) then expectSize = true end + end + + -- Read size + self.MostLikelyConstantExpression = true + if (not allocLabel) or (expectSize) then + local c,v = self:ConstantExpression(true) -- need precise value here, no ptrs allowed + if c then allocSize = v + else self:Error("Alloc size must be constant") end + end + + if allocLabel and expectSize then + if self:MatchToken(TOKEN.COMMA) then + local c,v = self:ConstantExpression(true) -- need precise value here, no ptrs allowed + if c then allocValue = v + else self:Error("Alloc value must be constant") end + else + allocValue = allocSize + allocSize = 1 + end + end + self.MostLikelyConstantExpression = false + + -- Initialize alloc + allocLeaf.ZeroPadding = allocSize + self:AddLeafToTail(allocLeaf) + return true + end + + + + + + -- Parse RETURN + if self:MatchToken(TOKEN.RETURN) and self.HeadLeaf then + if not self:MatchToken(TOKEN.COLON) then + local returnExpression = self:Expression() + local copyLargeVariable = false + if self.StructSize[self.CurFunction.ReturnType] then + copyLargeVariable = true -- Converts this to automatically return ptr to large variable + end + local returnLeaf = self:NewLeaf() + local preReturnLeaf,postReturnLeaf + if copyLargeVariable then + local finalReturnExpression = returnExpression + returnLeaf.Opcode = "mov" + returnLeaf.Operands[1] = { Register = 1 } + if returnExpression.Stack then + finalReturnExpression = { Register = 8 } -- EBP, the stack frame register, where the stack ptr is local to + postReturnLeaf = self:NewOpcode("add",returnLeaf.Operands[1],{Constant = returnExpression.Stack}) + -- MOV EAX,EBP + -- ADD EAX,(stack-pos) + end + returnLeaf.Operands[2] = finalReturnExpression + returnLeaf.ExplictAssign = true + else + returnLeaf.Opcode = "mov" + returnLeaf.Operands[1] = { Register = 1 } + returnLeaf.Operands[2] = returnExpression + returnLeaf.ExplictAssign = true + end + if preReturnLeaf then + self:AddLeafToTail(preReturnLeaf) + end + self:AddLeafToTail(returnLeaf) + if postReturnLeaf then + self:AddLeafToTail(postReturnLeaf) + end + end + self:MatchToken(TOKEN.COLON) + + -- Check if this is the last return in the function -- if self:MatchToken(TOKEN.RBRACKET) then -- if self.BlockDepth > 0 then -- self:BlockEnd() @@ -1262,360 +1262,360 @@ function HCOMP:Statement() local TOKEN,TOKENSET = self.TOKEN,self.TOKENSET -- end - if not self.GenerateInlineFunction then - -- Create leaf for exiting local scope - local leaveLeaf = self:NewLeaf() - leaveLeaf.Opcode = "leave" - if (self.StackPointer ~= 0) or - (self.ParameterPointer ~= 0) or - (self.Settings.AlwaysEnterLeave) then - self:AddLeafToTail(leaveLeaf) - end - - -- Create leaf for returning from call - local retLeaf = self:NewLeaf() - retLeaf.Opcode = "ret" - self:AddLeafToTail(retLeaf) - end - - return true - end - - -- Parse IF syntax - if self:MatchToken(TOKEN.IF) then - -- Parse condition - self:ExpectToken(TOKEN.LPAREN) - local firstToken = self.CurrentToken - self:SaveParserState() - local conditionLeaf = self:Expression() - local conditionText = "if ("..self:PrintTokens(self:GetSavedTokens(firstToken))..")" - self:ExpectToken(TOKEN.RPAREN) - - -- Enter the IF block - local needBlock = self:MatchToken(TOKEN.LBRACKET) - self:BlockStart("IF") - - -- Calculate condition - local cmpLeaf = self:NewLeaf() - cmpLeaf.Opcode = "cmp" - cmpLeaf.Operands[1] = { Constant = 0 } - cmpLeaf.Operands[2] = conditionLeaf - cmpLeaf.Comment = conditionText - self:AddLeafToTail(cmpLeaf) - - -- Create label for conditional break (if condition is false) - local conditionalBreakLeaf = self:NewLeaf() - local conditionalBreak = self:GetTempLabel() - conditionalBreakLeaf.Opcode = "LABEL" - conditionalBreak.Type = "Pointer" - conditionalBreak.Leaf = conditionalBreakLeaf - conditionalBreakLeaf.Label = conditionalBreak - self.SpecialLeaf[#self.SpecialLeaf].ConditionalBreak = conditionalBreakLeaf + if not self.GenerateInlineFunction then + -- Create leaf for exiting local scope + local leaveLeaf = self:NewLeaf() + leaveLeaf.Opcode = "leave" + if (self.StackPointer ~= 0) or + (self.ParameterPointer ~= 0) or + (self.Settings.AlwaysEnterLeave) then + self:AddLeafToTail(leaveLeaf) + end + + -- Create leaf for returning from call + local retLeaf = self:NewLeaf() + retLeaf.Opcode = "ret" + self:AddLeafToTail(retLeaf) + end + + return true + end + + -- Parse IF syntax + if self:MatchToken(TOKEN.IF) then + -- Parse condition + self:ExpectToken(TOKEN.LPAREN) + local firstToken = self.CurrentToken + self:SaveParserState() + local conditionLeaf = self:Expression() + local conditionText = "if ("..self:PrintTokens(self:GetSavedTokens(firstToken))..")" + self:ExpectToken(TOKEN.RPAREN) + + -- Enter the IF block + local needBlock = self:MatchToken(TOKEN.LBRACKET) + self:BlockStart("IF") + + -- Calculate condition + local cmpLeaf = self:NewLeaf() + cmpLeaf.Opcode = "cmp" + cmpLeaf.Operands[1] = { Constant = 0 } + cmpLeaf.Operands[2] = conditionLeaf + cmpLeaf.Comment = conditionText + self:AddLeafToTail(cmpLeaf) + + -- Create label for conditional break (if condition is false) + local conditionalBreakLeaf = self:NewLeaf() + local conditionalBreak = self:GetTempLabel() + conditionalBreakLeaf.Opcode = "LABEL" + conditionalBreak.Type = "Pointer" + conditionalBreak.Leaf = conditionalBreakLeaf + conditionalBreakLeaf.Label = conditionalBreak + self.SpecialLeaf[#self.SpecialLeaf].ConditionalBreak = conditionalBreakLeaf -- self:AddLeafToTail(conditionalBreakLeaf) - -- Generate conditional jump over the block - local jumpLeaf = self:NewLeaf() - jumpLeaf.Opcode = "jge" - jumpLeaf.Operands[1] = { PointerToLabel = conditionalBreakLeaf.Label } - self:AddLeafToTail(jumpLeaf) + -- Generate conditional jump over the block + local jumpLeaf = self:NewLeaf() + jumpLeaf.Opcode = "jge" + jumpLeaf.Operands[1] = { PointerToLabel = conditionalBreakLeaf.Label } + self:AddLeafToTail(jumpLeaf) - if not needBlock then - -- Parse next statement if dont need a block - self:Statement() + if not needBlock then + -- Parse next statement if dont need a block + self:Statement() - -- End the IF block early - self:BlockEnd() + -- End the IF block early + self:BlockEnd() - -- Add exit label - self:AddLeafToTail(conditionalBreakLeaf) + -- Add exit label + self:AddLeafToTail(conditionalBreakLeaf) - -- Check for out-of-block ELSE - if self:MatchToken(TOKEN.ELSE) then - self:ParseElse(false) - end + -- Check for out-of-block ELSE + if self:MatchToken(TOKEN.ELSE) then + self:ParseElse(false) + end -- else -- self:AddLeafToTail(conditionalBreak) -- self.SpecialLeaf[#self.SpecialLeaf].ConditionalBreak = jumpLeaf - end - - return true - end - - -- Parse WHILE syntax - if self:MatchToken(TOKEN.WHILE) then - local returnLabel - - -- Parse condition - self:ExpectToken(TOKEN.LPAREN) - local firstToken = self.CurrentToken - self:SaveParserState() - local conditionLeaf = self:Expression() - local conditionText = "if ("..self:PrintTokens(self:GetSavedTokens(firstToken)) - self:ExpectToken(TOKEN.RPAREN) - - -- Enter the WHILE block - local needBlock = self:MatchToken(TOKEN.LBRACKET) - if needBlock then - self:BlockStart("WHILE") - end - - if not needBlock then - -- Generate return label - local returnLabelLeaf = self:NewLeaf() - returnLabel = self:GetTempLabel() - returnLabelLeaf.Opcode = "LABEL" - returnLabel.Type = "Pointer" - returnLabel.Leaf = returnLabelLeaf - returnLabelLeaf.Label = returnLabel - self:AddLeafToTail(returnLabelLeaf) - end - - -- Calculate condition - local cmpLeaf = self:NewLeaf() - cmpLeaf.Opcode = "cmp" - cmpLeaf.Operands[1] = { Constant = 0 } - cmpLeaf.Operands[2] = conditionLeaf - cmpLeaf.Comment = conditionText - self:AddLeafToTail(cmpLeaf) - - if not needBlock then - -- Generate conditional jump over the block - local jumpOverLabelLeaf = self:NewLeaf() - local jumpOverLabel = self:GetTempLabel() - jumpOverLabelLeaf.Opcode = "LABEL" - jumpOverLabel.Type = "Pointer" - jumpOverLabel.Leaf = jumpOverLabelLeaf - jumpOverLabelLeaf.Label = jumpOverLabel - - local jumpOverLeaf = self:NewLeaf() - jumpOverLeaf.Opcode = "jz" - jumpOverLeaf.Operands[1] = { PointerToLabel = jumpOverLabel } - self:AddLeafToTail(jumpOverLeaf) - - -- Parse next statement if dont need a block - self:Statement() - - -- Generate the jump back leaf - local jumpBackLeaf = self:NewLeaf() - jumpBackLeaf.Opcode = "jmp" - jumpBackLeaf.Operands[1] = { PointerToLabel = returnLabel } - self:AddLeafToTail(jumpBackLeaf) - - -- Add exit label - self:AddLeafToTail(jumpOverLabelLeaf) - else - -- Generate conditional jump over the block - local jumpOverLeaf = self:NewLeaf() - jumpOverLeaf.Opcode = "jz" - jumpOverLeaf.Operands[1] = { PointerToLabel = self.SpecialLeaf[#self.SpecialLeaf].Break.Label } - self:AddLeafToTail(jumpOverLeaf) - - -- Set the jump back leaf - self.SpecialLeaf[#self.SpecialLeaf].JumpBack.Opcode = "jmp" - self.SpecialLeaf[#self.SpecialLeaf].JumpBack.Operands[1] = { PointerToLabel = self.SpecialLeaf[#self.SpecialLeaf].Continue.Label } - end - - return true - end - - -- Parse FOR syntax - if self:MatchToken(TOKEN.FOR) then - local returnLabel - - -- Parse syntax - self:ExpectToken(TOKEN.LPAREN) - local initLeaf = self:Expression() - initLeaf.Comment = "init loop" - self:ExpectToken(TOKEN.COLON) - local conditionLeaf = self:Expression() - conditionLeaf.Comment = "condition" - self:ExpectToken(TOKEN.COLON) - local stepLeaf = self:Expression() - stepLeaf.Comment = "loop step" - self:ExpectToken(TOKEN.RPAREN) - - self:AddLeafToTail(initLeaf) - - -- Save stepLeaf for inlining continue - self.CurrentStepLeaf = stepLeaf - - -- Enter the FOR block - local needBlock = self:MatchToken(TOKEN.LBRACKET) - if needBlock then - self:BlockStart("FOR") - end - - if not needBlock then - -- Generate return label - local returnLabelLeaf = self:NewLeaf() - returnLabel = self:GetTempLabel() - returnLabelLeaf.Opcode = "LABEL" - returnLabel.Type = "Pointer" - returnLabel.Leaf = returnLabelLeaf - returnLabelLeaf.Label = returnLabel - self:AddLeafToTail(returnLabelLeaf) - end - - -- Calculate condition - local cmpLeaf = self:NewLeaf() - cmpLeaf.Opcode = "cmp" - cmpLeaf.Operands[1] = { Constant = 0 } - cmpLeaf.Operands[2] = conditionLeaf - self:AddLeafToTail(cmpLeaf) - - if not needBlock then - -- Generate conditional jump over the block - local jumpOverLabelLeaf = self:NewLeaf() - local jumpOverLabel = self:GetTempLabel() - jumpOverLabelLeaf.Opcode = "LABEL" - jumpOverLabel.Type = "Pointer" - jumpOverLabel.Leaf = jumpOverLabelLeaf - jumpOverLabelLeaf.Label = jumpOverLabel - - local jumpOverLeaf = self:NewLeaf() - jumpOverLeaf.Opcode = "jz" - jumpOverLeaf.Operands[1] = { PointerToLabel = jumpOverLabel } - self:AddLeafToTail(jumpOverLeaf) - - -- Parse next statement if dont need a block - self:Statement() - - -- Generate the jump back leaf - local jumpBackLeaf = self:NewLeaf() - jumpBackLeaf.Opcode = "jmp" - jumpBackLeaf.Operands[1] = { PointerToLabel = returnLabel } - self:AddLeafToTail(stepLeaf) - self:AddLeafToTail(jumpBackLeaf) - - -- Add exit label - self:AddLeafToTail(jumpOverLabelLeaf) - else - -- Generate conditional jump over the block - local jumpOverLeaf = self:NewLeaf() - jumpOverLeaf.Opcode = "jz" - jumpOverLeaf.Operands[1] = { PointerToLabel = self.SpecialLeaf[#self.SpecialLeaf].Break.Label } - self:AddLeafToTail(jumpOverLeaf) - - -- Set the jump back leaf - self.SpecialLeaf[#self.SpecialLeaf].JumpBack.Opcode = "jmp" - self.SpecialLeaf[#self.SpecialLeaf].JumpBack.Operands[1] = { PointerToLabel = self.SpecialLeaf[#self.SpecialLeaf].Continue.Label } - self.SpecialLeaf[#self.SpecialLeaf].JumpBack.PreviousLeaf = stepLeaf - end - - return true - end - - -- Parse CONTINUE - if self:MatchToken(TOKEN.CONTINUE) then - if (self.BlockDepth > 0) and (self.CurrentContinueLeaf) then - local jumpBackLeaf = self:NewLeaf() - jumpBackLeaf.Opcode = "jmp" - jumpBackLeaf.Operands[1] = { PointerToLabel = self.CurrentContinueLeaf.Label } - - if (self.CurrentStepLeaf) then - self:AddLeafToTail(self.CurrentStepLeaf) - end - - self:AddLeafToTail(jumpBackLeaf) - return true - else - self:Error("Nowhere to continue here") - end - end - - -- Parse BREAK - if self:MatchToken(TOKEN.BREAK) then - if (self.BlockDepth > 0) and (self.CurrentBreakLeaf) then - local jumpLeaf = self:NewLeaf() - jumpLeaf.Opcode = "jmp" - jumpLeaf.Operands[1] = { PointerToLabel = self.CurrentBreakLeaf.Label } - self:AddLeafToTail(jumpLeaf) - return true - else - self:Error("Nowhere to break from here") - end - end - - -- Parse GOTO - if self:MatchToken(TOKEN.GOTO) then - local gotoExpression = self:Expression() - - local jumpLeaf = self:NewLeaf() - jumpLeaf.Opcode = "jmp" - jumpLeaf.Operands[1] = gotoExpression - self:AddLeafToTail(jumpLeaf) - return true - end - - -- Parse block open bracket - if self:MatchToken(TOKEN.LBRACKET) then - self:BlockStart("LBLOCK") - return true - end - - -- Parse block close bracket - if self:MatchToken(TOKEN.RBRACKET) then - if self.BlockDepth > 0 then - local blockType = self.BlockType[#self.BlockType] - if (blockType == "IF") and self:MatchToken(TOKEN.ELSE) then -- Add ELSE block, IF remains in stack - self:ParseElse(true) - else - if blockType == "IF" then -- FIXME: It kind of is redundant - self:AddLeafToTail(self.SpecialLeaf[#self.SpecialLeaf].ConditionalBreak) - end - self:BlockEnd() - end - return true - else - self:Error("Unexpected bracket") - end - end - - -- Parse possible label definition - local firstToken = self.CurrentToken - self:SaveParserState() - - if self:MatchToken(TOKEN.IDENT) then - if (self:PeekToken() == TOKEN.COMMA) then - -- Label definition for sure - while true do - local label = self:DefineLabel(self.TokenData) - label.Type = "Pointer" - label.Defined = true - - label.Leaf = self:NewLeaf() - label.Leaf.Opcode = "LABEL" - label.Leaf.Label = label - self:AddLeafToTail(label.Leaf) - - self:MatchToken(TOKEN.COMMA) - if not self:MatchToken(TOKEN.IDENT) then break end - end - self:MatchToken(TOKEN.COLON) - return true - elseif (self:PeekToken() == TOKEN.DCOLON) then - local label = self:GetLabel(self.TokenData) - label.Leaf = self:NewLeaf() - label.Leaf.Opcode = "LABEL" - label.Leaf.Label = label - self:AddLeafToTail(label.Leaf) - self:ExpectToken(TOKEN.DCOLON) - return true + end + + return true + end + + -- Parse WHILE syntax + if self:MatchToken(TOKEN.WHILE) then + local returnLabel + + -- Parse condition + self:ExpectToken(TOKEN.LPAREN) + local firstToken = self.CurrentToken + self:SaveParserState() + local conditionLeaf = self:Expression() + local conditionText = "if ("..self:PrintTokens(self:GetSavedTokens(firstToken)) + self:ExpectToken(TOKEN.RPAREN) + + -- Enter the WHILE block + local needBlock = self:MatchToken(TOKEN.LBRACKET) + if needBlock then + self:BlockStart("WHILE") + end + + if not needBlock then + -- Generate return label + local returnLabelLeaf = self:NewLeaf() + returnLabel = self:GetTempLabel() + returnLabelLeaf.Opcode = "LABEL" + returnLabel.Type = "Pointer" + returnLabel.Leaf = returnLabelLeaf + returnLabelLeaf.Label = returnLabel + self:AddLeafToTail(returnLabelLeaf) + end + + -- Calculate condition + local cmpLeaf = self:NewLeaf() + cmpLeaf.Opcode = "cmp" + cmpLeaf.Operands[1] = { Constant = 0 } + cmpLeaf.Operands[2] = conditionLeaf + cmpLeaf.Comment = conditionText + self:AddLeafToTail(cmpLeaf) + + if not needBlock then + -- Generate conditional jump over the block + local jumpOverLabelLeaf = self:NewLeaf() + local jumpOverLabel = self:GetTempLabel() + jumpOverLabelLeaf.Opcode = "LABEL" + jumpOverLabel.Type = "Pointer" + jumpOverLabel.Leaf = jumpOverLabelLeaf + jumpOverLabelLeaf.Label = jumpOverLabel + + local jumpOverLeaf = self:NewLeaf() + jumpOverLeaf.Opcode = "jz" + jumpOverLeaf.Operands[1] = { PointerToLabel = jumpOverLabel } + self:AddLeafToTail(jumpOverLeaf) + + -- Parse next statement if dont need a block + self:Statement() + + -- Generate the jump back leaf + local jumpBackLeaf = self:NewLeaf() + jumpBackLeaf.Opcode = "jmp" + jumpBackLeaf.Operands[1] = { PointerToLabel = returnLabel } + self:AddLeafToTail(jumpBackLeaf) + + -- Add exit label + self:AddLeafToTail(jumpOverLabelLeaf) + else + -- Generate conditional jump over the block + local jumpOverLeaf = self:NewLeaf() + jumpOverLeaf.Opcode = "jz" + jumpOverLeaf.Operands[1] = { PointerToLabel = self.SpecialLeaf[#self.SpecialLeaf].Break.Label } + self:AddLeafToTail(jumpOverLeaf) + + -- Set the jump back leaf + self.SpecialLeaf[#self.SpecialLeaf].JumpBack.Opcode = "jmp" + self.SpecialLeaf[#self.SpecialLeaf].JumpBack.Operands[1] = { PointerToLabel = self.SpecialLeaf[#self.SpecialLeaf].Continue.Label } + end + + return true + end + + -- Parse FOR syntax + if self:MatchToken(TOKEN.FOR) then + local returnLabel + + -- Parse syntax + self:ExpectToken(TOKEN.LPAREN) + local initLeaf = self:Expression() + initLeaf.Comment = "init loop" + self:ExpectToken(TOKEN.COLON) + local conditionLeaf = self:Expression() + conditionLeaf.Comment = "condition" + self:ExpectToken(TOKEN.COLON) + local stepLeaf = self:Expression() + stepLeaf.Comment = "loop step" + self:ExpectToken(TOKEN.RPAREN) + + self:AddLeafToTail(initLeaf) + + -- Save stepLeaf for inlining continue + self.CurrentStepLeaf = stepLeaf + + -- Enter the FOR block + local needBlock = self:MatchToken(TOKEN.LBRACKET) + if needBlock then + self:BlockStart("FOR") + end + + if not needBlock then + -- Generate return label + local returnLabelLeaf = self:NewLeaf() + returnLabel = self:GetTempLabel() + returnLabelLeaf.Opcode = "LABEL" + returnLabel.Type = "Pointer" + returnLabel.Leaf = returnLabelLeaf + returnLabelLeaf.Label = returnLabel + self:AddLeafToTail(returnLabelLeaf) + end + + -- Calculate condition + local cmpLeaf = self:NewLeaf() + cmpLeaf.Opcode = "cmp" + cmpLeaf.Operands[1] = { Constant = 0 } + cmpLeaf.Operands[2] = conditionLeaf + self:AddLeafToTail(cmpLeaf) + + if not needBlock then + -- Generate conditional jump over the block + local jumpOverLabelLeaf = self:NewLeaf() + local jumpOverLabel = self:GetTempLabel() + jumpOverLabelLeaf.Opcode = "LABEL" + jumpOverLabel.Type = "Pointer" + jumpOverLabel.Leaf = jumpOverLabelLeaf + jumpOverLabelLeaf.Label = jumpOverLabel + + local jumpOverLeaf = self:NewLeaf() + jumpOverLeaf.Opcode = "jz" + jumpOverLeaf.Operands[1] = { PointerToLabel = jumpOverLabel } + self:AddLeafToTail(jumpOverLeaf) + + -- Parse next statement if dont need a block + self:Statement() + + -- Generate the jump back leaf + local jumpBackLeaf = self:NewLeaf() + jumpBackLeaf.Opcode = "jmp" + jumpBackLeaf.Operands[1] = { PointerToLabel = returnLabel } + self:AddLeafToTail(stepLeaf) + self:AddLeafToTail(jumpBackLeaf) + + -- Add exit label + self:AddLeafToTail(jumpOverLabelLeaf) + else + -- Generate conditional jump over the block + local jumpOverLeaf = self:NewLeaf() + jumpOverLeaf.Opcode = "jz" + jumpOverLeaf.Operands[1] = { PointerToLabel = self.SpecialLeaf[#self.SpecialLeaf].Break.Label } + self:AddLeafToTail(jumpOverLeaf) + + -- Set the jump back leaf + self.SpecialLeaf[#self.SpecialLeaf].JumpBack.Opcode = "jmp" + self.SpecialLeaf[#self.SpecialLeaf].JumpBack.Operands[1] = { PointerToLabel = self.SpecialLeaf[#self.SpecialLeaf].Continue.Label } + self.SpecialLeaf[#self.SpecialLeaf].JumpBack.PreviousLeaf = stepLeaf + end + + return true + end + + -- Parse CONTINUE + if self:MatchToken(TOKEN.CONTINUE) then + if (self.BlockDepth > 0) and (self.CurrentContinueLeaf) then + local jumpBackLeaf = self:NewLeaf() + jumpBackLeaf.Opcode = "jmp" + jumpBackLeaf.Operands[1] = { PointerToLabel = self.CurrentContinueLeaf.Label } + + if (self.CurrentStepLeaf) then + self:AddLeafToTail(self.CurrentStepLeaf) + end + + self:AddLeafToTail(jumpBackLeaf) + return true + else + self:Error("Nowhere to continue here") + end + end + + -- Parse BREAK + if self:MatchToken(TOKEN.BREAK) then + if (self.BlockDepth > 0) and (self.CurrentBreakLeaf) then + local jumpLeaf = self:NewLeaf() + jumpLeaf.Opcode = "jmp" + jumpLeaf.Operands[1] = { PointerToLabel = self.CurrentBreakLeaf.Label } + self:AddLeafToTail(jumpLeaf) + return true + else + self:Error("Nowhere to break from here") + end + end + + -- Parse GOTO + if self:MatchToken(TOKEN.GOTO) then + local gotoExpression = self:Expression() + + local jumpLeaf = self:NewLeaf() + jumpLeaf.Opcode = "jmp" + jumpLeaf.Operands[1] = gotoExpression + self:AddLeafToTail(jumpLeaf) + return true + end + + -- Parse block open bracket + if self:MatchToken(TOKEN.LBRACKET) then + self:BlockStart("LBLOCK") + return true + end + + -- Parse block close bracket + if self:MatchToken(TOKEN.RBRACKET) then + if self.BlockDepth > 0 then + local blockType = self.BlockType[#self.BlockType] + if (blockType == "IF") and self:MatchToken(TOKEN.ELSE) then -- Add ELSE block, IF remains in stack + self:ParseElse(true) + else + if blockType == "IF" then -- FIXME: It kind of is redundant + self:AddLeafToTail(self.SpecialLeaf[#self.SpecialLeaf].ConditionalBreak) + end + self:BlockEnd() + end + return true + else + self:Error("Unexpected bracket") + end + end + + -- Parse possible label definition + local firstToken = self.CurrentToken + self:SaveParserState() + + if self:MatchToken(TOKEN.IDENT) then + if (self:PeekToken() == TOKEN.COMMA) then + -- Label definition for sure + while true do + local label = self:DefineLabel(self.TokenData) + label.Type = "Pointer" + label.Defined = true + + label.Leaf = self:NewLeaf() + label.Leaf.Opcode = "LABEL" + label.Leaf.Label = label + self:AddLeafToTail(label.Leaf) + + self:MatchToken(TOKEN.COMMA) + if not self:MatchToken(TOKEN.IDENT) then break end + end + self:MatchToken(TOKEN.COLON) + return true + elseif (self:PeekToken() == TOKEN.DCOLON) then + local label = self:GetLabel(self.TokenData) + label.Leaf = self:NewLeaf() + label.Leaf.Opcode = "LABEL" + label.Leaf.Label = label + self:AddLeafToTail(label.Leaf) + self:ExpectToken(TOKEN.DCOLON) + return true else - self:RestoreParserState() - end - end - - -- If nothing else, must be some kind of an expression - local expressionLeaf = self:Expression() - self:AddLeafToTail(expressionLeaf) - - -- Add expression to leaf comment - if self.Settings.GenerateComments then - expressionLeaf.Comment = self:PrintTokens(self:GetSavedTokens(firstToken)) - end - - -- Skip a colon - self:MatchToken(TOKEN.COLON) - return true + self:RestoreParserState() + end + end + + -- If nothing else, must be some kind of an expression + local expressionLeaf = self:Expression() + self:AddLeafToTail(expressionLeaf) + + -- Add expression to leaf comment + if self.Settings.GenerateComments then + expressionLeaf.Comment = self:PrintTokens(self:GetSavedTokens(firstToken)) + end + + -- Skip a colon + self:MatchToken(TOKEN.COLON) + return true end diff --git a/lua/wire/client/hlzasm/hc_tokenizer.lua b/lua/wire/client/hlzasm/hc_tokenizer.lua index 480e7271ae..08b78d5538 100644 --- a/lua/wire/client/hlzasm/hc_tokenizer.lua +++ b/lua/wire/client/hlzasm/hc_tokenizer.lua @@ -72,7 +72,7 @@ HCOMP.TOKEN_TEXT["STRUCT"] = {{"HLZASM"},{"STRUCT"}} HCOMP.TOKEN_TEXT["DB"] = {{"ZASM","HLZASM"},{"DB"}} HCOMP.TOKEN_TEXT["ALLOC"] = {{"ZASM","HLZASM"},{"ALLOC"}} HCOMP.TOKEN_TEXT["VECTOR"] = {{"ZASM","HLZASM"},{"SCALAR","VECTOR1F","VECTOR2F","UV","VECTOR3F", - "VECTOR4F","COLOR","VEC1F","VEC2F","VEC3F","VEC4F","MATRIX"}} + "VECTOR4F","COLOR","VEC1F","VEC2F","VEC3F","VEC4F","MATRIX"}} HCOMP.TOKEN_TEXT["STRALLOC"] = {{"ZASM","HLZASM"},{"STRING"}} HCOMP.TOKEN_TEXT["DB"] = {{"ZASM","HLZASM"},{"DB"}} @@ -100,12 +100,12 @@ HCOMP.TOKEN_TEXT["EOF"] = {{"ZASM","HLZASM"},{}} -- end of file -- Add ZCPU ports for port=0,1023 do - HCOMP.TOKEN_TEXT["REGISTER"][2][1024+port] = "PORT"..port + HCOMP.TOKEN_TEXT["REGISTER"][2][1024+port] = "PORT"..port end -- Add extended registers for reg=0,31 do - HCOMP.TOKEN_TEXT["REGISTER"][2][96+reg] = "R"..reg + HCOMP.TOKEN_TEXT["REGISTER"][2][96+reg] = "R"..reg end @@ -119,72 +119,72 @@ HCOMP.TOKEN_NAME = {} HCOMP.TOKEN_NAME2 = {} local IDX = 1 for tokenName,tokenData in pairs(HCOMP.TOKEN_TEXT) do - HCOMP.TOKEN[tokenName] = IDX - HCOMP.TOKEN_NAME[IDX] = tokenName - HCOMP.TOKEN_NAME2[IDX] = {} - - for k,v in pairs(tokenData[2]) do - HCOMP.TOKEN_NAME2[IDX][k] = v - end - IDX = IDX + 1 + HCOMP.TOKEN[tokenName] = IDX + HCOMP.TOKEN_NAME[IDX] = tokenName + HCOMP.TOKEN_NAME2[IDX] = {} + + for k,v in pairs(tokenData[2]) do + HCOMP.TOKEN_NAME2[IDX][k] = v + end + IDX = IDX + 1 end HCOMP.TOKENSET.OPERATORS = { - HCOMP.TOKEN.LPAREN, - HCOMP.TOKEN.RPAREN, - HCOMP.TOKEN.LSUBSCR, - HCOMP.TOKEN.RSUBSCR, - HCOMP.TOKEN.TIMES, - HCOMP.TOKEN.SLASH, - HCOMP.TOKEN.MODULUS, - HCOMP.TOKEN.PLUS, - HCOMP.TOKEN.MINUS, - HCOMP.TOKEN.AND, - HCOMP.TOKEN.OR, - HCOMP.TOKEN.XOR, - HCOMP.TOKEN.POWER, - HCOMP.TOKEN.INC, - HCOMP.TOKEN.DEC, - HCOMP.TOKEN.SHL, - HCOMP.TOKEN.SHR, - HCOMP.TOKEN.EQL, - HCOMP.TOKEN.NEQ, - HCOMP.TOKEN.LEQ, - HCOMP.TOKEN.LSS, - HCOMP.TOKEN.GEQ, - HCOMP.TOKEN.GTR, - HCOMP.TOKEN.NOT, - HCOMP.TOKEN.EQUAL, - HCOMP.TOKEN.LAND, - HCOMP.TOKEN.LOR, - HCOMP.TOKEN.EQLADD, - HCOMP.TOKEN.EQLSUB, - HCOMP.TOKEN.EQLMUL, - HCOMP.TOKEN.EQLDIV, - HCOMP.TOKEN.DOT + HCOMP.TOKEN.LPAREN, + HCOMP.TOKEN.RPAREN, + HCOMP.TOKEN.LSUBSCR, + HCOMP.TOKEN.RSUBSCR, + HCOMP.TOKEN.TIMES, + HCOMP.TOKEN.SLASH, + HCOMP.TOKEN.MODULUS, + HCOMP.TOKEN.PLUS, + HCOMP.TOKEN.MINUS, + HCOMP.TOKEN.AND, + HCOMP.TOKEN.OR, + HCOMP.TOKEN.XOR, + HCOMP.TOKEN.POWER, + HCOMP.TOKEN.INC, + HCOMP.TOKEN.DEC, + HCOMP.TOKEN.SHL, + HCOMP.TOKEN.SHR, + HCOMP.TOKEN.EQL, + HCOMP.TOKEN.NEQ, + HCOMP.TOKEN.LEQ, + HCOMP.TOKEN.LSS, + HCOMP.TOKEN.GEQ, + HCOMP.TOKEN.GTR, + HCOMP.TOKEN.NOT, + HCOMP.TOKEN.EQUAL, + HCOMP.TOKEN.LAND, + HCOMP.TOKEN.LOR, + HCOMP.TOKEN.EQLADD, + HCOMP.TOKEN.EQLSUB, + HCOMP.TOKEN.EQLMUL, + HCOMP.TOKEN.EQLDIV, + HCOMP.TOKEN.DOT } HCOMP.TOKENSET.ASSIGNMENT = { - HCOMP.TOKEN.EQUAL, - HCOMP.TOKEN.EQLADD, - HCOMP.TOKEN.EQLSUB, - HCOMP.TOKEN.EQLMUL, - HCOMP.TOKEN.EQLDIV + HCOMP.TOKEN.EQUAL, + HCOMP.TOKEN.EQLADD, + HCOMP.TOKEN.EQLSUB, + HCOMP.TOKEN.EQLMUL, + HCOMP.TOKEN.EQLDIV } -- Create lookup tables for faster parsing HCOMP.PARSER_LOOKUP = {} for symID,symList in pairs(HCOMP.TOKEN_TEXT) do - for _,languageName in pairs(symList[1]) do - HCOMP.PARSER_LOOKUP[languageName] = HCOMP.PARSER_LOOKUP[languageName] or {} - for symSubID,symText in pairs(symList[2]) do - if symID == "VECTOR" then -- Special case for vector symbols - HCOMP.PARSER_LOOKUP[languageName][symText] = { symText, HCOMP.TOKEN[symID] } - else - HCOMP.PARSER_LOOKUP[languageName][symText] = { symSubID, HCOMP.TOKEN[symID] } - end - end - end + for _,languageName in pairs(symList[1]) do + HCOMP.PARSER_LOOKUP[languageName] = HCOMP.PARSER_LOOKUP[languageName] or {} + for symSubID,symText in pairs(symList[2]) do + if symID == "VECTOR" then -- Special case for vector symbols + HCOMP.PARSER_LOOKUP[languageName][symText] = { symText, HCOMP.TOKEN[symID] } + else + HCOMP.PARSER_LOOKUP[languageName][symText] = { symSubID, HCOMP.TOKEN[symID] } + end + end + end end @@ -192,46 +192,46 @@ end HCOMP.PARSER_SYMBOLS = {} HCOMP.PARSER_DBCHAR = {} for symID,symList in pairs(HCOMP.TOKEN_TEXT) do - local languages = symList[1] - local symText = symList[2][1] or "" - if #symText == 2 then - local char1 = string.sub(symText,1,1) - local char2 = string.sub(symText,2,2) - for _,lang in pairs(languages) do - HCOMP.PARSER_DBCHAR[lang] = HCOMP.PARSER_DBCHAR[lang] or {} - HCOMP.PARSER_DBCHAR[lang][char1] = HCOMP.PARSER_DBCHAR[lang][char1] or {} - HCOMP.PARSER_DBCHAR[lang][char1][char2] = true - end - end - if #symText == 1 then - for _,lang in pairs(languages) do - HCOMP.PARSER_SYMBOLS[lang] = HCOMP.PARSER_SYMBOLS[lang] or {} - HCOMP.PARSER_SYMBOLS[lang][symText] = true - end - end + local languages = symList[1] + local symText = symList[2][1] or "" + if #symText == 2 then + local char1 = string.sub(symText,1,1) + local char2 = string.sub(symText,2,2) + for _,lang in pairs(languages) do + HCOMP.PARSER_DBCHAR[lang] = HCOMP.PARSER_DBCHAR[lang] or {} + HCOMP.PARSER_DBCHAR[lang][char1] = HCOMP.PARSER_DBCHAR[lang][char1] or {} + HCOMP.PARSER_DBCHAR[lang][char1][char2] = true + end + end + if #symText == 1 then + for _,lang in pairs(languages) do + HCOMP.PARSER_SYMBOLS[lang] = HCOMP.PARSER_SYMBOLS[lang] or {} + HCOMP.PARSER_SYMBOLS[lang][symText] = true + end + end 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 + -- 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 } - end - end + 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() @@ -239,39 +239,39 @@ HCOMP:RegenerateTokenizerOpcodes() -------------------------------------------------------------------------------- -- Skip a single file in input function HCOMP:nextFile() - table.remove(self.Code,1) - if not self.Code[1] then - self.Code[1] = { Text = "", Line = 1, Col = 1, File = "internal error", NextCharPos = 1 } - end + table.remove(self.Code,1) + if not self.Code[1] then + self.Code[1] = { Text = "", Line = 1, Col = 1, File = "internal error", NextCharPos = 1 } + end end -- Return next character function HCOMP:getChar() - local pos = self.Code[1].NextCharPos - local char = string.sub(self.Code[1].Text,pos,pos) - if char == "" then - self:nextFile() - char = string.sub(self.Code[1].Text,pos,pos) - end - return char + local pos = self.Code[1].NextCharPos + local char = string.sub(self.Code[1].Text,pos,pos) + if char == "" then + self:nextFile() + char = string.sub(self.Code[1].Text,pos,pos) + end + return char end -- Skip current char function HCOMP:nextChar() - local code = self.Code[1] - local pos = code.NextCharPos - if pos > #code.Text then - self:nextFile() - else - local char = string.sub(code.Text,pos,pos) - if char == "\n" then - code.Line = code.Line + 1 - code.Col = 1 - else - code.Col = code.Col + 1 - end - code.NextCharPos = pos + 1 - end + local code = self.Code[1] + local pos = code.NextCharPos + if pos > #code.Text then + self:nextFile() + else + local char = string.sub(code.Text,pos,pos) + if char == "\n" then + code.Line = code.Line + 1 + code.Col = 1 + else + code.Col = code.Col + 1 + end + code.NextCharPos = pos + 1 + end end @@ -279,213 +279,213 @@ end -------------------------------------------------------------------------------- -- Tokenize the code function HCOMP:Tokenize() local TOKEN = self.TOKEN - -- Skip whitespaces - while (self:getChar() == " ") or - (self:getChar() == "\t") or - (self:getChar() == "\n") or + -- Skip whitespaces + while (self:getChar() == " ") or + (self:getChar() == "\t") or + (self:getChar() == "\n") or (self:getChar() == "\r") do self:nextChar() end - -- Read token position - local tokenPosition = { Line = self.Code[1].Line, - Col = self.Code[1].Col, - File = self.Code[1].File, - ParentFile = self.Code[1].ParentFile } - - -- Check for end of file - if self:getChar() == "" then - table.insert(self.Tokens,{ - Type = TOKEN.EOF, - Data = nil, - Position = tokenPosition, - }) - return false - end - - -- Is it a preprocessor macro - if (self.Code[1].Col == 1) and (self:getChar() == "#") then - local macroLine = "" - while (self:getChar() ~= "") and (self:getChar() ~= "\n") do - macroLine = macroLine .. self:getChar() - self:nextChar() - end - - -- Parse it - self:ParsePreprocessMacro(macroLine,tokenPosition) - return true - end - - -- If still inside IFDEF, do not parse what follows - if self.IFDEFLevel[#self.IFDEFLevel] == true then - self:nextChar() - return true - end - - -- Is it a string - if (self:getChar() == "'") or (self:getChar() == "\"") then - local stringType = self:getChar() - self:nextChar() -- Skip leading character - - local fetchString = "" - while self.Code[1].NextCharPos <= #self.Code[1].Text and self:getChar() ~= stringType do - - if self:getChar() == "\\" then - self:nextChar() - if self:getChar() == "'" then fetchString = fetchString .. "'" - elseif self:getChar() == "\"" then fetchString = fetchString .. "\"" - elseif self:getChar() == "a" then fetchString = fetchString .. "\a" - elseif self:getChar() == "b" then fetchString = fetchString .. "\b" + -- Read token position + local tokenPosition = { Line = self.Code[1].Line, + Col = self.Code[1].Col, + File = self.Code[1].File, + ParentFile = self.Code[1].ParentFile } + + -- Check for end of file + if self:getChar() == "" then + table.insert(self.Tokens,{ + Type = TOKEN.EOF, + Data = nil, + Position = tokenPosition, + }) + return false + end + + -- Is it a preprocessor macro + if (self.Code[1].Col == 1) and (self:getChar() == "#") then + local macroLine = "" + while (self:getChar() ~= "") and (self:getChar() ~= "\n") do + macroLine = macroLine .. self:getChar() + self:nextChar() + end + + -- Parse it + self:ParsePreprocessMacro(macroLine,tokenPosition) + return true + end + + -- If still inside IFDEF, do not parse what follows + if self.IFDEFLevel[#self.IFDEFLevel] == true then + self:nextChar() + return true + end + + -- Is it a string + if (self:getChar() == "'") or (self:getChar() == "\"") then + local stringType = self:getChar() + self:nextChar() -- Skip leading character + + local fetchString = "" + while self.Code[1].NextCharPos <= #self.Code[1].Text and self:getChar() ~= stringType do + + if self:getChar() == "\\" then + self:nextChar() + if self:getChar() == "'" then fetchString = fetchString .. "'" + elseif self:getChar() == "\"" then fetchString = fetchString .. "\"" + elseif self:getChar() == "a" then fetchString = fetchString .. "\a" + elseif self:getChar() == "b" then fetchString = fetchString .. "\b" -- elseif self:getChar() == "c" then fetchString = fetchString .. "\c" - elseif self:getChar() == "f" then fetchString = fetchString .. "\f" - elseif self:getChar() == "r" then fetchString = fetchString .. "\r" - elseif self:getChar() == "n" then fetchString = fetchString .. "\n" - elseif self:getChar() == "t" then fetchString = fetchString .. "\t" - elseif self:getChar() == "v" then fetchString = fetchString .. "\v" - elseif self:getChar() == "0" then fetchString = fetchString .. "\0" - end - self:nextChar() - elseif self:getChar() == "\n" then - self:Error("Missing terminating " .. stringType .. " character", - tokenPosition.Line,tokenPosition.Col,tokenPosition.File) - else - fetchString = fetchString .. self:getChar() - self:nextChar() - end - end - self:nextChar() -- Skip trailing character - - if (stringType == "'") and (#fetchString == 1) then - table.insert(self.Tokens,{ - Type = TOKEN.CHAR, - Data = string.byte(fetchString), - Position = tokenPosition, - }) - else - --if stringType == "'" then - -- self:Warning("Using character definition syntax for defining a string - might cause problems") - --end - table.insert(self.Tokens,{ - Type = TOKEN.STRING, - Data = fetchString, - Position = tokenPosition, - }) - end - return true - end - - -- Fetch entire token - local token = "" - while string.find(self:getChar(),"[%w_.@]") do - token = token .. self:getChar() - self:nextChar() - end - - -- Check if token was redefined - if (token ~= "") and (self.Defines[token]) then - if token == "__FILE__" then - table.insert(self.Tokens,{ - Type = TOKEN.STRING, - Data = tokenPosition.File, - Position = tokenPosition, - }) - return true - elseif token == "__LINE__" then - table.insert(self.Tokens,{ - Type = TOKEN.STRING, - Data = tostring(tokenPosition.Line), - Position = tokenPosition, - }) - return true - else - token = self.Defines[token] - end - end - - local is_symbol = false - - -- If no alphanumeric token fetched, try to fetch the special-character ones - if token == "" then - token = self:getChar() - self:nextChar() - - if HCOMP.PARSER_DBCHAR[self.Settings.CurrentLanguage][token] and HCOMP.PARSER_DBCHAR[self.Settings.CurrentLanguage][token][self:getChar()] then - local curChar = self:getChar() - token = token .. curChar - self:nextChar() - if token == "//" then -- Line comment - while (self:getChar() ~= "") and (self:getChar() ~= "\n") do self:nextChar() end - return true - elseif token == "/*" then -- Block comment open - while self:getChar() ~= "" do - local curChar = self:getChar() - self:nextChar() - if (curChar == "*") and (self:getChar() == "/") then - self:nextChar() - return true - end - end - - -- Error in tokenizing - self:Error("Comment block not closed (reached end of file)", - tokenPosition.Line,tokenPosition.Col,tokenPosition.File) - return true - elseif token == "*/" then -- Block comment end (returns error token) - table.insert(self.Tokens,{ - Type = TOKEN.COMMENT3, - Position = tokenPosition, - }) - return true - end - - -- Else it's a two-character symbol token - is_symbol = true - - elseif HCOMP.PARSER_SYMBOLS[self.Settings.CurrentLanguage][token] then - -- It's a one-character symbol token - is_symbol = true + elseif self:getChar() == "f" then fetchString = fetchString .. "\f" + elseif self:getChar() == "r" then fetchString = fetchString .. "\r" + elseif self:getChar() == "n" then fetchString = fetchString .. "\n" + elseif self:getChar() == "t" then fetchString = fetchString .. "\t" + elseif self:getChar() == "v" then fetchString = fetchString .. "\v" + elseif self:getChar() == "0" then fetchString = fetchString .. "\0" + end + self:nextChar() + elseif self:getChar() == "\n" then + self:Error("Missing terminating " .. stringType .. " character", + tokenPosition.Line,tokenPosition.Col,tokenPosition.File) + else + fetchString = fetchString .. self:getChar() + self:nextChar() + end + end + self:nextChar() -- Skip trailing character + + if (stringType == "'") and (#fetchString == 1) then + table.insert(self.Tokens,{ + Type = TOKEN.CHAR, + Data = string.byte(fetchString), + Position = tokenPosition, + }) + else + --if stringType == "'" then + -- self:Warning("Using character definition syntax for defining a string - might cause problems") + --end + table.insert(self.Tokens,{ + Type = TOKEN.STRING, + Data = fetchString, + Position = tokenPosition, + }) + end + return true + end + + -- Fetch entire token + local token = "" + while string.find(self:getChar(),"[%w_.@]") do + token = token .. self:getChar() + self:nextChar() + end + + -- Check if token was redefined + if (token ~= "") and (self.Defines[token]) then + if token == "__FILE__" then + table.insert(self.Tokens,{ + Type = TOKEN.STRING, + Data = tokenPosition.File, + Position = tokenPosition, + }) + return true + elseif token == "__LINE__" then + table.insert(self.Tokens,{ + Type = TOKEN.STRING, + Data = tostring(tokenPosition.Line), + Position = tokenPosition, + }) + return true + else + token = self.Defines[token] + end + end + + local is_symbol = false + + -- If no alphanumeric token fetched, try to fetch the special-character ones + if token == "" then + token = self:getChar() + self:nextChar() + + if HCOMP.PARSER_DBCHAR[self.Settings.CurrentLanguage][token] and HCOMP.PARSER_DBCHAR[self.Settings.CurrentLanguage][token][self:getChar()] then + local curChar = self:getChar() + token = token .. curChar + self:nextChar() + if token == "//" then -- Line comment + while (self:getChar() ~= "") and (self:getChar() ~= "\n") do self:nextChar() end + return true + elseif token == "/*" then -- Block comment open + while self:getChar() ~= "" do + local curChar = self:getChar() + self:nextChar() + if (curChar == "*") and (self:getChar() == "/") then + self:nextChar() + return true + end + end + + -- Error in tokenizing + self:Error("Comment block not closed (reached end of file)", + tokenPosition.Line,tokenPosition.Col,tokenPosition.File) + return true + elseif token == "*/" then -- Block comment end (returns error token) + table.insert(self.Tokens,{ + Type = TOKEN.COMMENT3, + Position = tokenPosition, + }) + return true + end + + -- Else it's a two-character symbol token + is_symbol = true + + elseif HCOMP.PARSER_SYMBOLS[self.Settings.CurrentLanguage][token] then + -- It's a one-character symbol token + is_symbol = true else - -- We have no idea what this is (it's not an identifier character, nor a recognized symbol) - self:Error("Unknown character '"..token.."'", - tokenPosition.Line,tokenPosition.Col,tokenPosition.File) - end - end - - assert(token ~= "") - - -- Determine which token it is - local tokenLookupTable = self.PARSER_LOOKUP[self.Settings.CurrentLanguage][string.upper(token)] - if tokenLookupTable then - table.insert(self.Tokens,{ - Type = tokenLookupTable[2], - Data = tokenLookupTable[1], - Position = tokenPosition, - }) - return true - end - - if is_symbol then - -- If we get here something is weird, because why would a symbol be in PARSER_DBCHARS or PARSER_SYMBOLS but not in PARSER_LOOKUP? - self:Error("Unknown symbol '"..token.."'", - tokenPosition.Line,tokenPosition.Col,tokenPosition.File) - end - - -- Maybe its a number - if tonumber(token) then - table.insert(self.Tokens,{ - Type = TOKEN.NUMBER, - Data = tonumber(token), - Position = tokenPosition, - }) - return true - end - - -- Wow it must have been ident afterall - table.insert(self.Tokens,{ - Type = TOKEN.IDENT, - Data = token, - Position = tokenPosition, - }) - return true + -- We have no idea what this is (it's not an identifier character, nor a recognized symbol) + self:Error("Unknown character '"..token.."'", + tokenPosition.Line,tokenPosition.Col,tokenPosition.File) + end + end + + assert(token ~= "") + + -- Determine which token it is + local tokenLookupTable = self.PARSER_LOOKUP[self.Settings.CurrentLanguage][string.upper(token)] + if tokenLookupTable then + table.insert(self.Tokens,{ + Type = tokenLookupTable[2], + Data = tokenLookupTable[1], + Position = tokenPosition, + }) + return true + end + + if is_symbol then + -- If we get here something is weird, because why would a symbol be in PARSER_DBCHARS or PARSER_SYMBOLS but not in PARSER_LOOKUP? + self:Error("Unknown symbol '"..token.."'", + tokenPosition.Line,tokenPosition.Col,tokenPosition.File) + end + + -- Maybe its a number + if tonumber(token) then + table.insert(self.Tokens,{ + Type = TOKEN.NUMBER, + Data = tonumber(token), + Position = tokenPosition, + }) + return true + end + + -- Wow it must have been ident afterall + table.insert(self.Tokens,{ + Type = TOKEN.IDENT, + Data = token, + Position = tokenPosition, + }) + return true end @@ -494,36 +494,36 @@ end -------------------------------------------------------------------------------- -- Print a string of tokens as an expression function HCOMP:PrintTokens(tokenList) - local text = "" - if not istable(tokenList) then error("[global 1:1] Internal error 516 ("..tokenList..")") end - - for _,token in ipairs(tokenList) do - if (token.Type == self.TOKEN.NUMBER) or - (token.Type == self.TOKEN.OPCODE) then - text = text..token.Data - elseif token.Type == self.TOKEN.IDENT then - if self.Settings.GenerateLibrary then - if not self.LabelLookup[token.Data] then - self.LabelLookup[token.Data] = "_"..self.LabelLookupCounter - self.LabelLookupCounter = self.LabelLookupCounter + 1 - end - text = text..self.LabelLookup[token.Data] - else - text = text..token.Data - end - elseif token.Type == self.TOKEN.STRING then - text = text.."\""..token.Data.."\"" - elseif token.Type == self.TOKEN.CHAR then - if token.Data >= 32 then - text = text.."'"..string.char(token.Data).."'" - else - text = text.."'\\"..token.Data.."'" - end - else - text = text..(self.TOKEN_NAME2[token.Type][token.Data or 1] or "") - end - end - return text + local text = "" + if not istable(tokenList) then error("[global 1:1] Internal error 516 ("..tokenList..")") end + + for _,token in ipairs(tokenList) do + if (token.Type == self.TOKEN.NUMBER) or + (token.Type == self.TOKEN.OPCODE) then + text = text..token.Data + elseif token.Type == self.TOKEN.IDENT then + if self.Settings.GenerateLibrary then + if not self.LabelLookup[token.Data] then + self.LabelLookup[token.Data] = "_"..self.LabelLookupCounter + self.LabelLookupCounter = self.LabelLookupCounter + 1 + end + text = text..self.LabelLookup[token.Data] + else + text = text..token.Data + end + elseif token.Type == self.TOKEN.STRING then + text = text.."\""..token.Data.."\"" + elseif token.Type == self.TOKEN.CHAR then + if token.Data >= 32 then + text = text.."'"..string.char(token.Data).."'" + else + text = text.."'\\"..token.Data.."'" + end + else + text = text..(self.TOKEN_NAME2[token.Type][token.Data or 1] or "") + end + end + return text end @@ -532,19 +532,19 @@ end -------------------------------------------------------------------------------- -- Expects next token to be tok, otherwise will raise an error function HCOMP:ExpectToken(tok) - if not self.Tokens[self.CurrentToken] then - if tok == self.TOKEN.EOF then - self:Error("Expected "..HCOMP.TOKEN_NAME[tok]..", got "..HCOMP.TOKEN_NAME[self.TOKEN.EOF].." instead") - end - end - - if self.Tokens[self.CurrentToken].Type == tok then - self.TokenType = self.Tokens[self.CurrentToken].Type - self.TokenData = self.Tokens[self.CurrentToken].Data - self.CurrentToken = self.CurrentToken + 1 - else - self:Error("Expected "..HCOMP.TOKEN_NAME[tok]..", got "..HCOMP.TOKEN_NAME[self.Tokens[self.CurrentToken].Type].." instead") - end + if not self.Tokens[self.CurrentToken] then + if tok == self.TOKEN.EOF then + self:Error("Expected "..HCOMP.TOKEN_NAME[tok]..", got "..HCOMP.TOKEN_NAME[self.TOKEN.EOF].." instead") + end + end + + if self.Tokens[self.CurrentToken].Type == tok then + self.TokenType = self.Tokens[self.CurrentToken].Type + self.TokenData = self.Tokens[self.CurrentToken].Data + self.CurrentToken = self.CurrentToken + 1 + else + self:Error("Expected "..HCOMP.TOKEN_NAME[tok]..", got "..HCOMP.TOKEN_NAME[self.Tokens[self.CurrentToken].Type].." instead") + end end @@ -552,26 +552,26 @@ end -- Returns true and skips a token if it matches this one function HCOMP:MatchToken(tok) - if istable(tok) then -- Match against a table of tokens - for _,token in pairs(tok) do - if self:MatchToken(token) then - return true - end - end - return false - end - if not self.Tokens[self.CurrentToken] then - return tok == self.TOKEN.EOF - end - - if self.Tokens[self.CurrentToken].Type == tok then - self.TokenType = self.Tokens[self.CurrentToken].Type - self.TokenData = self.Tokens[self.CurrentToken].Data - self.CurrentToken = self.CurrentToken + 1 - return true - else - return false - end + if istable(tok) then -- Match against a table of tokens + for _,token in pairs(tok) do + if self:MatchToken(token) then + return true + end + end + return false + end + if not self.Tokens[self.CurrentToken] then + return tok == self.TOKEN.EOF + end + + if self.Tokens[self.CurrentToken].Type == tok then + self.TokenType = self.Tokens[self.CurrentToken].Type + self.TokenData = self.Tokens[self.CurrentToken].Data + self.CurrentToken = self.CurrentToken + 1 + return true + else + return false + end end @@ -579,27 +579,27 @@ end -- Go to next token function HCOMP:NextToken() - self.CurrentToken = self.CurrentToken + 1 + self.CurrentToken = self.CurrentToken + 1 end -- Go to previous token function HCOMP:PreviousToken() - self.CurrentToken = self.CurrentToken - 1 + self.CurrentToken = self.CurrentToken - 1 end -- Returns next token type. Looks forward into stream if offset is specified function HCOMP:PeekToken(offset,extended) - if self.Tokens[self.CurrentToken+(offset or 0)] then - if extended then - return self.Tokens[self.CurrentToken+(offset or 0)].Type, - self.Tokens[self.CurrentToken+(offset or 0)].Data - else - return self.Tokens[self.CurrentToken+(offset or 0)].Type - end - else - return self.TOKEN.EOF - end + if self.Tokens[self.CurrentToken+(offset or 0)] then + if extended then + return self.Tokens[self.CurrentToken+(offset or 0)].Type, + self.Tokens[self.CurrentToken+(offset or 0)].Data + else + return self.Tokens[self.CurrentToken+(offset or 0)].Type + end + else + return self.TOKEN.EOF + end end @@ -607,7 +607,7 @@ end -- Store current parser state (so code could be reparsed again later) function HCOMP:SaveParserState() - self.SavedToken = self.CurrentToken + self.SavedToken = self.CurrentToken end @@ -616,12 +616,12 @@ end -- Get all tokens between saved state and current state. This is used for -- reparsing expressions during resolve stage. function HCOMP:GetSavedTokens(firstToken) - local savedTokens = {} - for tokenIdx = firstToken or self.SavedToken,self.CurrentToken-1 do - table.insert(savedTokens,self.Tokens[tokenIdx]) - end - savedTokens.TokenList = true - return savedTokens + local savedTokens = {} + for tokenIdx = firstToken or self.SavedToken,self.CurrentToken-1 do + table.insert(savedTokens,self.Tokens[tokenIdx]) + end + savedTokens.TokenList = true + return savedTokens end @@ -630,12 +630,12 @@ end -- Restore parser state. Can accept a list of tokens and restore state to that -- (see GetSavedTokens()) function HCOMP:RestoreParserState(tokenList) - if tokenList then - self.Tokens = tokenList - self.CurrentToken = 1 - else - self.CurrentToken = self.SavedToken - end + if tokenList then + self.Tokens = tokenList + self.CurrentToken = 1 + else + self.CurrentToken = self.SavedToken + end end @@ -643,12 +643,12 @@ end -- Returns current position in source file function HCOMP:CurrentSourcePosition() - if self.CurrentToken then - if self.Tokens[self.CurrentToken-1] then - return self.Tokens[self.CurrentToken-1].Position - end - elseif self.FileName then - return { Line = 1, Col = 1, File = self.FileName, ParentFile = 'HL-ZASM'} - end - return { Line = 1, Col = 1, File = "HL-ZASM", ParentFile = "HL-ZASM"} + if self.CurrentToken then + if self.Tokens[self.CurrentToken-1] then + return self.Tokens[self.CurrentToken-1].Position + end + elseif self.FileName then + return { Line = 1, Col = 1, File = self.FileName, ParentFile = 'HL-ZASM'} + end + return { Line = 1, Col = 1, File = "HL-ZASM", ParentFile = "HL-ZASM"} end diff --git a/lua/wire/client/text_editor/modes/zcpu.lua b/lua/wire/client/text_editor/modes/zcpu.lua index 787d035881..b6f155d56b 100644 --- a/lua/wire/client/text_editor/modes/zcpu.lua +++ b/lua/wire/client/text_editor/modes/zcpu.lua @@ -4,79 +4,79 @@ local string_gsub = string.gsub local draw_WordBox = draw.WordBox local EDITOR = { - UseValidator = true, - Validator = CPULib.Validate, - E2HelperCategory = "ZASM", -- As an override, makes GPU and SPU use the ZCPU helper too + UseValidator = true, + Validator = CPULib.Validate, + E2HelperCategory = "ZASM", -- As an override, makes GPU and SPU use the ZCPU helper too } -- CPU hint box local oldpos, haschecked = {0,0}, false function EDITOR:Think() - local caret = self:CursorToCaret() - local startpos, word = self:getWordStart( caret, true ) - - if word and word ~= "" then - if not haschecked then - oldpos = {startpos[1],startpos[2]} - haschecked = true - timer.Simple(0.3,function() - if not self then return end - if not self.CursorToCaret then return end - local caret = self:CursorToCaret() - local startpos, word = self:getWordStart( caret, true ) - if startpos[1] == oldpos[1] and startpos[2] == oldpos[2] then - self.CurrentVarValue = { startpos, word } - end - end) - elseif (oldpos[1] ~= startpos[1] or oldpos[2] ~= startpos[2]) and haschecked then - haschecked = false - self.CurrentVarValue = nil - oldpos = {0,0} - end - else - self.CurrentVarValue = nil - haschecked = false - oldpos = {0,0} - end + local caret = self:CursorToCaret() + local startpos, word = self:getWordStart( caret, true ) + + if word and word ~= "" then + if not haschecked then + oldpos = {startpos[1],startpos[2]} + haschecked = true + timer.Simple(0.3,function() + if not self then return end + if not self.CursorToCaret then return end + local caret = self:CursorToCaret() + local startpos, word = self:getWordStart( caret, true ) + if startpos[1] == oldpos[1] and startpos[2] == oldpos[2] then + self.CurrentVarValue = { startpos, word } + end + end) + elseif (oldpos[1] ~= startpos[1] or oldpos[2] ~= startpos[2]) and haschecked then + haschecked = false + self.CurrentVarValue = nil + oldpos = {0,0} + end + else + self.CurrentVarValue = nil + haschecked = false + oldpos = {0,0} + end end local colors = { - ["normal"] = { Color(255, 255, 136), false}, - ["opcode"] = { Color(255, 136, 0), false}, - ["comment"] = { Color(128, 128, 128), false}, - ["register"] = { Color(255, 255, 136), false}, - ["number"] = { Color(232, 232, 0), false}, - ["string"] = { Color(255, 136, 136), false}, - ["filename"] = { Color(232, 232, 232), false}, - ["label"] = { Color(255, 255, 176), false}, - ["keyword"] = { Color(255, 136, 0), false}, - ["memref"] = { Color(232, 232, 0), false}, - ["pmacro"] = { Color(136, 136, 255), false}, - ["error"] = { Color(240, 96, 96), false}, - -- ["compare"] = { Color(255, 186, 40), true}, + ["normal"] = { Color(255, 255, 136), false}, + ["opcode"] = { Color(255, 136, 0), false}, + ["comment"] = { Color(128, 128, 128), false}, + ["register"] = { Color(255, 255, 136), false}, + ["number"] = { Color(232, 232, 0), false}, + ["string"] = { Color(255, 136, 136), false}, + ["filename"] = { Color(232, 232, 232), false}, + ["label"] = { Color(255, 255, 176), false}, + ["keyword"] = { Color(255, 136, 0), false}, + ["memref"] = { Color(232, 232, 0), false}, + ["pmacro"] = { Color(136, 136, 255), false}, + ["error"] = { Color(240, 96, 96), false}, + -- ["compare"] = { Color(255, 186, 40), true}, } -- Build lookup table for opcodes local opcodeTable = {} for k,v in pairs(CPULib.InstructionTable) do - if v.Mnemonic ~= "RESERVED" then - opcodeTable[v.Mnemonic] = true - end + if v.Mnemonic ~= "RESERVED" then + opcodeTable[v.Mnemonic] = true + end end local function RemoveInstructions(indexes) - for _,v in ipairs(indexes) do - opcodeTable[CPULib.InstructionTable[v].Mnemonic] = nil - end + for _,v in ipairs(indexes) do + opcodeTable[CPULib.InstructionTable[v].Mnemonic] = nil + end end local function CreateInstructions(indexes) - for _,v in ipairs(indexes) do - local inst = CPULib.InstructionTable[v] - if inst.Mnemonic ~= "RESERVED" then - opcodeTable[inst.Mnemonic] = true - end - end + for _,v in ipairs(indexes) do + local inst = CPULib.InstructionTable[v] + if inst.Mnemonic ~= "RESERVED" then + opcodeTable[inst.Mnemonic] = true + end + end end table.insert(CPULib.RemoveInstructionHooks,RemoveInstructions) @@ -84,18 +84,18 @@ table.insert(CPULib.CreateInstructionHooks,CreateInstructions) -- Build lookup table for keywords local keywordsList = { - "GOTO","FOR","IF","ELSE","WHILE","DO","SWITCH","CASE","CONST","RETURN","BREAK", - "CONTINUE","EXPORT","INLINE","FORWARD","REGISTER","DB","ALLOC","SCALAR","VECTOR1F", - "VECTOR2F","UV","VECTOR3F","VECTOR4F","COLOR","VEC1F","VEC2F","VEC3F","VEC4F","MATRIX", - "STRING","DB","DEFINE","CODE","DATA","ORG","OFFSET","INT48","FLOAT","CHAR","VOID", - "INT","FLOAT","CHAR","VOID","PRESERVE","ZAP","STRUCT","VECTOR" + "GOTO","FOR","IF","ELSE","WHILE","DO","SWITCH","CASE","CONST","RETURN","BREAK", + "CONTINUE","EXPORT","INLINE","FORWARD","REGISTER","DB","ALLOC","SCALAR","VECTOR1F", + "VECTOR2F","UV","VECTOR3F","VECTOR4F","COLOR","VEC1F","VEC2F","VEC3F","VEC4F","MATRIX", + "STRING","DB","DEFINE","CODE","DATA","ORG","OFFSET","INT48","FLOAT","CHAR","VOID", + "INT","FLOAT","CHAR","VOID","PRESERVE","ZAP","STRUCT","VECTOR" } EDITOR.keywordsTable = {} local keywordsTable = EDITOR.keywordsTable for k,v in pairs(keywordsList) do - keywordsTable[v] = true + keywordsTable[v] = true end -- For checking the keywords to remove structs in the editor. @@ -103,291 +103,291 @@ EDITOR.keywordsTableOriginal = table.Copy(keywordsTable) -- Build lookup table for registers local registersTable = { - EAX = true,EBX = true,ECX = true,EDX = true,ESI = true,EDI = true, - ESP = true,EBP = true,CS = true,SS = true,DS = true,ES = true,GS = true, - FS = true,KS = true,LS = true + EAX = true,EBX = true,ECX = true,EDX = true,ESI = true,EDI = true, + ESP = true,EBP = true,CS = true,SS = true,DS = true,ES = true,GS = true, + FS = true,KS = true,LS = true } for reg=0,31 do registersTable["R"..reg] = true end for port=0,1023 do registersTable["PORT"..port] = true end -- Build lookup table for macros local macroTable = { - ["PRAGMA"] = true, - ["INCLUDE"] = true, - ["#INCLUDE##"] = true, - ["DEFINE"] = true, - ["IFDEF"] = true, - ["IFNDEF"] = true, - ["ENDIF"] = true, - ["ELSE"] = true, - ["UNDEF"] = true, + ["PRAGMA"] = true, + ["INCLUDE"] = true, + ["#INCLUDE##"] = true, + ["DEFINE"] = true, + ["IFDEF"] = true, + ["IFNDEF"] = true, + ["ENDIF"] = true, + ["ELSE"] = true, + ["UNDEF"] = true, } function EDITOR:CommentSelection(removecomment) - local comment_char = "//" - if removecomment then - -- shift-TAB with a selection -- - local tmp = string_gsub("\n"..self:GetSelection(), "\n"..comment_char, "\n") - - -- makes sure that the first line is outdented - self:SetSelection(tmp:sub(2)) - else - -- plain TAB with a selection -- - self:SetSelection(comment_char .. self:GetSelection():gsub("\n", "\n"..comment_char)) - end + local comment_char = "//" + if removecomment then + -- shift-TAB with a selection -- + local tmp = string_gsub("\n"..self:GetSelection(), "\n"..comment_char, "\n") + + -- makes sure that the first line is outdented + self:SetSelection(tmp:sub(2)) + else + -- plain TAB with a selection -- + self:SetSelection(comment_char .. self:GetSelection():gsub("\n", "\n"..comment_char)) + end end function EDITOR:BlockCommentSelection(removecomment) - local sel_start, sel_caret = self:MakeSelection( self:Selection() ) - local str = self:GetSelection() - if removecomment then - if str:find( "^/%*" ) and str:find( "%*/$" ) then - self:SetSelection( str:gsub( "^/%*(.+)%*/$", "%1" ) ) - - sel_caret[2] = sel_caret[2] - 2 - end - else - self:SetSelection( "/*" .. str .. "*/" ) - - if sel_caret[1] == sel_start[1] then - sel_caret[2] = sel_caret[2] + 4 - else - sel_caret[2] = sel_caret[2] + 2 - end - end - return { sel_start, sel_caret } + local sel_start, sel_caret = self:MakeSelection( self:Selection() ) + local str = self:GetSelection() + if removecomment then + if str:find( "^/%*" ) and str:find( "%*/$" ) then + self:SetSelection( str:gsub( "^/%*(.+)%*/$", "%1" ) ) + + sel_caret[2] = sel_caret[2] - 2 + end + else + self:SetSelection( "/*" .. str .. "*/" ) + + if sel_caret[1] == sel_start[1] then + sel_caret[2] = sel_caret[2] + 4 + else + sel_caret[2] = sel_caret[2] + 2 + end + end + return { sel_start, sel_caret } end function EDITOR:ShowContextHelp(word) - E2Helper.Show() - if E2Helper.Modes then - E2Helper:SetMode("ZASM") - else - E2Helper.UseCPU(self:GetParent().EditorType) - end - E2Helper.Show(word) + E2Helper.Show() + if E2Helper.Modes then + E2Helper:SetMode("ZASM") + else + E2Helper.UseCPU(self:GetParent().EditorType) + end + E2Helper.Show(word) end function EDITOR:ResetTokenizer(row) - if row == self.Scroll[1] then - -- As above, but for HL-ZASM: Check whether the line self.Scroll[1] starts within a block comment. - self.blockcomment = nil - - for k=1, self.Scroll[1]-1 do - local row = self.Rows[k] - - for match in string_gmatch(row, "[/*][/*]") do - if match == "//" then - -- single line comment start; skip remainder of line - break - elseif match == "/*" then - self.blockcomment = true - elseif match == "*/" then - self.blockcomment = nil - end - end - end - end + if row == self.Scroll[1] then + -- As above, but for HL-ZASM: Check whether the line self.Scroll[1] starts within a block comment. + self.blockcomment = nil + + for k=1, self.Scroll[1]-1 do + local row = self.Rows[k] + + for match in string_gmatch(row, "[/*][/*]") do + if match == "//" then + -- single line comment start; skip remainder of line + break + elseif match == "/*" then + self.blockcomment = true + elseif match == "*/" then + self.blockcomment = nil + end + end + end + end end function EDITOR:SyntaxColorLine(row) - local cols = {} - self:ResetTokenizer(row) - self:NextCharacter() - - if self.blockcomment then - if self:NextPattern(".-%*/") then - self.blockcomment = nil - else - self:NextPattern(".*") - end - - cols[#cols + 1] = {self.tokendata, colors["comment"]} - end - - local isGpu = self:GetParent().EditorType == "GPU" - - -- Remember the last token to prevent structs from being highlighted. - local previousToken = "" - - while self.character do - local tokenname = "" - self.tokendata = "" - - self:NextPattern(" *") - if not self.character then break end - - if self:NextPattern("^[a-zA-Z0-9_@.]+:") then - tokenname = "label" - elseif self:NextPattern("^[a-zA-Z0-9_@.]+") then - local sstr = string.upper(self.tokendata:Trim()) - if opcodeTable[sstr] then - tokenname = "opcode" - elseif registersTable[sstr] then - tokenname = "register" - elseif keywordsTable[sstr] and (previousToken ~= "STRUCT") then -- Default to number/normal if it's declaring a struct. - tokenname = "keyword" - elseif tonumber(self.tokendata) then - tokenname = "number" - else - tokenname = "normal" - end - - previousToken = sstr - elseif (self.character == "'") or (self.character == "\"") then - tokenname = "string" - local delimiter = self.character - self:NextCharacter() - while self.character ~= delimiter do - if not self.character then tokenname = "error" break end - if self.character == "\\" then self:NextCharacter() end - self:NextCharacter() - end - self:NextCharacter() - elseif self:NextPattern("^//.*$") then - tokenname = "comment" - elseif self:NextPattern("^/%*") then -- start of a multi-line comment - --addToken("comment", self.tokendata) - self.blockcomment = true - if self:NextPattern(".-%*/") then - self.blockcomment = nil - else - self:NextPattern(".*") - end - - tokenname = "comment" - elseif self.character == "#" then - self:NextCharacter() - - if self:NextPattern("include +<") then - - cols[#cols + 1] = {self.tokendata:sub(1,-2), colors["pmacro"]} - - self.tokendata = "<" - if self:NextPattern("^[a-zA-Z0-9_/\\]+%.txt>") then - tokenname = "filename" - else - self:NextPattern(".*$") - tokenname = "normal" - end - elseif self:NextPattern("include +\"") then - - cols[#cols + 1] = {self.tokendata:sub(1,-2), colors["pmacro"]} - - self.tokendata = "\"" - if self:NextPattern("^[a-zA-Z0-9_/\\]+%.txt\"") then - tokenname = "filename" - else - self:NextPattern(".*$") - tokenname = "normal" - end - elseif self:NextPattern("^[a-zA-Z0-9_@.#]+") then - local sstr = string.sub(string.upper(self.tokendata:Trim()),2) - if macroTable[sstr] then - self:NextPattern(".*$") - tokenname = "pmacro" - else - tokenname = "memref" - end - else - tokenname = "memref" - end - elseif self.character == "[" or self.character == "]" then - self:NextCharacter() - tokenname = "memref" - else - self:NextCharacter() - tokenname = "normal" - end - - local color = colors[tokenname] - if #cols > 1 and color == cols[#cols][2] then - cols[#cols][1] = cols[#cols][1] .. self.tokendata - else - cols[#cols + 1] = {self.tokendata, color} - end - end - return cols + local cols = {} + self:ResetTokenizer(row) + self:NextCharacter() + + if self.blockcomment then + if self:NextPattern(".-%*/") then + self.blockcomment = nil + else + self:NextPattern(".*") + end + + cols[#cols + 1] = {self.tokendata, colors["comment"]} + end + + local isGpu = self:GetParent().EditorType == "GPU" + + -- Remember the last token to prevent structs from being highlighted. + local previousToken = "" + + while self.character do + local tokenname = "" + self.tokendata = "" + + self:NextPattern(" *") + if not self.character then break end + + if self:NextPattern("^[a-zA-Z0-9_@.]+:") then + tokenname = "label" + elseif self:NextPattern("^[a-zA-Z0-9_@.]+") then + local sstr = string.upper(self.tokendata:Trim()) + if opcodeTable[sstr] then + tokenname = "opcode" + elseif registersTable[sstr] then + tokenname = "register" + elseif keywordsTable[sstr] and (previousToken ~= "STRUCT") then -- Default to number/normal if it's declaring a struct. + tokenname = "keyword" + elseif tonumber(self.tokendata) then + tokenname = "number" + else + tokenname = "normal" + end + + previousToken = sstr + elseif (self.character == "'") or (self.character == "\"") then + tokenname = "string" + local delimiter = self.character + self:NextCharacter() + while self.character ~= delimiter do + if not self.character then tokenname = "error" break end + if self.character == "\\" then self:NextCharacter() end + self:NextCharacter() + end + self:NextCharacter() + elseif self:NextPattern("^//.*$") then + tokenname = "comment" + elseif self:NextPattern("^/%*") then -- start of a multi-line comment + --addToken("comment", self.tokendata) + self.blockcomment = true + if self:NextPattern(".-%*/") then + self.blockcomment = nil + else + self:NextPattern(".*") + end + + tokenname = "comment" + elseif self.character == "#" then + self:NextCharacter() + + if self:NextPattern("include +<") then + + cols[#cols + 1] = {self.tokendata:sub(1,-2), colors["pmacro"]} + + self.tokendata = "<" + if self:NextPattern("^[a-zA-Z0-9_/\\]+%.txt>") then + tokenname = "filename" + else + self:NextPattern(".*$") + tokenname = "normal" + end + elseif self:NextPattern("include +\"") then + + cols[#cols + 1] = {self.tokendata:sub(1,-2), colors["pmacro"]} + + self.tokendata = "\"" + if self:NextPattern("^[a-zA-Z0-9_/\\]+%.txt\"") then + tokenname = "filename" + else + self:NextPattern(".*$") + tokenname = "normal" + end + elseif self:NextPattern("^[a-zA-Z0-9_@.#]+") then + local sstr = string.sub(string.upper(self.tokendata:Trim()),2) + if macroTable[sstr] then + self:NextPattern(".*$") + tokenname = "pmacro" + else + tokenname = "memref" + end + else + tokenname = "memref" + end + elseif self.character == "[" or self.character == "]" then + self:NextCharacter() + tokenname = "memref" + else + self:NextCharacter() + tokenname = "normal" + end + + local color = colors[tokenname] + if #cols > 1 and color == cols[#cols][2] then + cols[#cols][1] = cols[#cols][1] .. self.tokendata + else + cols[#cols + 1] = {self.tokendata, color} + end + end + return cols end function EDITOR:PopulateMenu(menu) - if not self.chosenfile then return end - - menu:AddSpacer() - - local caretPos = self:CursorToCaret() - local IsBreakpointSet = CPULib.GetDebugBreakpoint( self.chosenfile, caretPos ) - - if not IsBreakpointSet then - menu:AddOption( "Add Breakpoint", function() - CPULib.SetDebugBreakpoint( self.chosenfile, caretPos, true ) - end) - -- menu:AddOption( "Add Conditional Breakpoint", function() - -- Derma_StringRequestNoBlur( "Add Conditional Breakpoint", "456", "123", - -- function( strTextOut ) - -- CPULib.SetDebugBreakpoint( caretPos, strTextOut ) - -- end ) - -- end) - else - menu:AddOption( "Remove Breakpoint", function() - CPULib.SetDebugBreakpoint( self.chosenfile, caretPos ) - end) - end - menu:AddOption("Display line using IP", function() - CPULib.Debugger.PreferredTracker = "IP" - CPULib.Debugger.PreferredTrackerFriendlyName = "IP" - end) - menu:AddOption("Display line using CS:IP", function() - CPULib.Debugger.PreferredTracker = "CSIP" - CPULib.Debugger.PreferredTrackerFriendlyName = "CS:IP" - end) - menu:AddOption("Display line using PHYS IP", function() - CPULib.Debugger.PreferredTracker = "PHYSIP" - CPULib.Debugger.PreferredTrackerFriendlyName = "PHYS IP" - end) - if CPULib.Debugger.FollowTracker then - menu:AddOption("Don't follow the displayed line", function() - CPULib.Debugger.FollowTracker = false - end) - else - menu:AddOption("Follow the displayed line", function() - CPULib.Debugger.FollowTracker = true - end) - end + if not self.chosenfile then return end + + menu:AddSpacer() + + local caretPos = self:CursorToCaret() + local IsBreakpointSet = CPULib.GetDebugBreakpoint( self.chosenfile, caretPos ) + + if not IsBreakpointSet then + menu:AddOption( "Add Breakpoint", function() + CPULib.SetDebugBreakpoint( self.chosenfile, caretPos, true ) + end) + -- menu:AddOption( "Add Conditional Breakpoint", function() + -- Derma_StringRequestNoBlur( "Add Conditional Breakpoint", "456", "123", + -- function( strTextOut ) + -- CPULib.SetDebugBreakpoint( caretPos, strTextOut ) + -- end ) + -- end) + else + menu:AddOption( "Remove Breakpoint", function() + CPULib.SetDebugBreakpoint( self.chosenfile, caretPos ) + end) + end + menu:AddOption("Display line using IP", function() + CPULib.Debugger.PreferredTracker = "IP" + CPULib.Debugger.PreferredTrackerFriendlyName = "IP" + end) + menu:AddOption("Display line using CS:IP", function() + CPULib.Debugger.PreferredTracker = "CSIP" + CPULib.Debugger.PreferredTrackerFriendlyName = "CS:IP" + end) + menu:AddOption("Display line using PHYS IP", function() + CPULib.Debugger.PreferredTracker = "PHYSIP" + CPULib.Debugger.PreferredTrackerFriendlyName = "PHYS IP" + end) + if CPULib.Debugger.FollowTracker then + menu:AddOption("Don't follow the displayed line", function() + CPULib.Debugger.FollowTracker = false + end) + else + menu:AddOption("Follow the displayed line", function() + CPULib.Debugger.FollowTracker = true + end) + end end function EDITOR:Paint() - -- Paint CPU debug hints - if self.CurrentVarValue then - local pos = self.CurrentVarValue[1] - local x, y = (pos[2]+2) * self.FontWidth, (pos[1]-1-self.Scroll[1]) * self.FontHeight - local txt = CPULib.GetDebugPopupText(self.CurrentVarValue[2]) - if txt then - draw_WordBox(2, x, y, txt, "E2SmallFont", Color(0,0,0,255), Color(255,255,255,255) ) - end - end - - if CPULib.DebuggerAttached then - local debugWindowText = CPULib.GetDebugWindowText() - for k,v in ipairs(debugWindowText) do - if v ~= "" then - local y = (k % 24) - local x = 15*(1 + math_floor(#debugWindowText / 24) - math_floor(k / 24)) - draw_WordBox(2, self:GetWide()-self.FontWidth*x, self.FontHeight*(-1+y), v, "E2SmallFont", Color(0,0,0,255), Color(255,255,255,255) ) - end - end - end + -- Paint CPU debug hints + if self.CurrentVarValue then + local pos = self.CurrentVarValue[1] + local x, y = (pos[2]+2) * self.FontWidth, (pos[1]-1-self.Scroll[1]) * self.FontHeight + local txt = CPULib.GetDebugPopupText(self.CurrentVarValue[2]) + if txt then + draw_WordBox(2, x, y, txt, "E2SmallFont", Color(0,0,0,255), Color(255,255,255,255) ) + end + end + + if CPULib.DebuggerAttached then + local debugWindowText = CPULib.GetDebugWindowText() + for k,v in ipairs(debugWindowText) do + if v ~= "" then + local y = (k % 24) + local x = 15*(1 + math_floor(#debugWindowText / 24) - math_floor(k / 24)) + draw_WordBox(2, self:GetWide()-self.FontWidth*x, self.FontHeight*(-1+y), v, "E2SmallFont", Color(0,0,0,255), Color(255,255,255,255) ) + end + end + end end WireTextEditor.Modes.ZCPU = EDITOR WireTextEditor.Modes.ZGPU = EDITOR local ZSPU = { - UseSoundBrowser = true + UseSoundBrowser = true } -- Proxy everything else from the ZCPU editor. local ZSPU_Meta = { - __index = function(self,key) return EDITOR[key] end + __index = function(self,key) return EDITOR[key] end } setmetatable(ZSPU,ZSPU_Meta) diff --git a/lua/wire/stools/cpu.lua b/lua/wire/stools/cpu.lua index 58fe294e4b..405eb0c93d 100644 --- a/lua/wire/stools/cpu.lua +++ b/lua/wire/stools/cpu.lua @@ -2,497 +2,497 @@ WireToolSetup.setCategory( "Chips, Gates", "Advanced" ) WireToolSetup.open( "cpu", "CPU", "gmod_wire_cpu", nil, "CPUs" ) if CLIENT then - language.Add("Tool.wire_cpu.name", "CPU Tool (Wire)") - language.Add("Tool.wire_cpu.desc", "Spawns a central processing unit") - language.Add("ToolWirecpu_Model", "Model:" ) - TOOL.Information = { - { name = "left", text = "Upload program to hispeed device" }, - { name = "right", text = "Open editor" }, - { name = "reload", text = "Attach debugger" }, - { name = "reload_shift", text = "Shift+Reload: Clear" }, - } - - WireToolSetup.setToolMenuIcon("icon16/server.png") + language.Add("Tool.wire_cpu.name", "CPU Tool (Wire)") + language.Add("Tool.wire_cpu.desc", "Spawns a central processing unit") + language.Add("ToolWirecpu_Model", "Model:" ) + TOOL.Information = { + { name = "left", text = "Upload program to hispeed device" }, + { name = "right", text = "Open editor" }, + { name = "reload", text = "Attach debugger" }, + { name = "reload_shift", text = "Shift+Reload: Clear" }, + } + + WireToolSetup.setToolMenuIcon("icon16/server.png") end WireToolSetup.BaseLang() WireToolSetup.SetupMax( 7 ) TOOL.ClientConVar = { - model = "models/cheeze/wires/cpu.mdl", - filename = "", - memorymodel = "64krom", - extensions = "", - customram = 0, - customrom = 0 + model = "models/cheeze/wires/cpu.mdl", + filename = "", + memorymodel = "64krom", + extensions = "", + customram = 0, + customrom = 0 } if CLIENT then - ------------------------------------------------------------------------------ - -- Make sure firing animation is displayed clientside - ------------------------------------------------------------------------------ - function TOOL:LeftClick() return true end - function TOOL:Reload() return true end - function TOOL:RightClick() return false end + ------------------------------------------------------------------------------ + -- Make sure firing animation is displayed clientside + ------------------------------------------------------------------------------ + function TOOL:LeftClick() return true end + function TOOL:Reload() return true end + function TOOL:RightClick() return false end end if SERVER then - util.AddNetworkString("ZCPU_RequestCode") - util.AddNetworkString("ZCPU_OpenEditor") - util.AddNetworkString("CPULib.InvalidateDebugger") - ------------------------------------------------------------------------------ - -- Reload: wipe ROM/RAM and reset memory model, or attach debugger - ------------------------------------------------------------------------------ - function TOOL:Reload(trace) - if trace.Entity:IsPlayer() then return false end - local player = self:GetOwner() - - if player:KeyDown(IN_SPEED) then - if (trace.Entity:IsValid()) and - (trace.Entity:GetClass() == "gmod_wire_cpu") then - trace.Entity:SetMemoryModel(self:GetClientInfo("memorymodel"),self:GetClientInfo("customram"),self:GetClientInfo("customrom")) - trace.Entity:FlashData({}) - net.Start("CPULib.InvalidateDebugger") net.WriteUInt(0,2) net.Send(player) - end - else - if (not trace.Entity:IsPlayer()) and - (trace.Entity:IsValid()) and - (trace.Entity:GetClass() == "gmod_wire_cpu") then - CPULib.AttachDebugger(trace.Entity,player) - CPULib.SendDebugData(trace.Entity.VM,nil,player) - net.Start("CPULib.InvalidateDebugger") net.WriteUInt(2,2) net.Send(player) - else - CPULib.AttachDebugger(nil,player) - net.Start("CPULib.InvalidateDebugger") net.WriteUInt(1,2) net.Send(player) - end - end - return true - end - - -- Left click: spawn CPU or upload current program into it - function TOOL:CheckHitOwnClass(trace) - return trace.Entity:IsValid() and (trace.Entity:GetClass() == self.WireClass or trace.Entity.WriteCell) - end - function TOOL:LeftClick_Update(trace) - CPULib.SetUploadTarget(trace.Entity, self:GetOwner()) - net.Start("ZCPU_RequestCode") net.Send(self:GetOwner()) - net.Start("CPULib.InvalidateDebugger") net.WriteUInt(0,2) net.Send(player) - end - function TOOL:MakeEnt(ply, model, Ang, trace) - local ent = WireLib.MakeWireEnt(ply, {Class = self.WireClass, Pos=trace.HitPos, Angle=Ang, Model=model}) - ent:SetMemoryModel(self:GetClientInfo("memorymodel"),self:GetClientInfo("customram"),self:GetClientInfo("customrom")) - ent:SetExtensionLoadOrder(self:GetClientInfo("extensions")) - self:LeftClick_Update(trace) - return ent - end - - - -- Right click: open editor - function TOOL:RightClick(trace) - net.Start("ZCPU_OpenEditor") net.Send(self:GetOwner()) - return true - end + util.AddNetworkString("ZCPU_RequestCode") + util.AddNetworkString("ZCPU_OpenEditor") + util.AddNetworkString("CPULib.InvalidateDebugger") + ------------------------------------------------------------------------------ + -- Reload: wipe ROM/RAM and reset memory model, or attach debugger + ------------------------------------------------------------------------------ + function TOOL:Reload(trace) + if trace.Entity:IsPlayer() then return false end + local player = self:GetOwner() + + if player:KeyDown(IN_SPEED) then + if (trace.Entity:IsValid()) and + (trace.Entity:GetClass() == "gmod_wire_cpu") then + trace.Entity:SetMemoryModel(self:GetClientInfo("memorymodel"),self:GetClientInfo("customram"),self:GetClientInfo("customrom")) + trace.Entity:FlashData({}) + net.Start("CPULib.InvalidateDebugger") net.WriteUInt(0,2) net.Send(player) + end + else + if (not trace.Entity:IsPlayer()) and + (trace.Entity:IsValid()) and + (trace.Entity:GetClass() == "gmod_wire_cpu") then + CPULib.AttachDebugger(trace.Entity,player) + CPULib.SendDebugData(trace.Entity.VM,nil,player) + net.Start("CPULib.InvalidateDebugger") net.WriteUInt(2,2) net.Send(player) + else + CPULib.AttachDebugger(nil,player) + net.Start("CPULib.InvalidateDebugger") net.WriteUInt(1,2) net.Send(player) + end + end + return true + end + + -- Left click: spawn CPU or upload current program into it + function TOOL:CheckHitOwnClass(trace) + return trace.Entity:IsValid() and (trace.Entity:GetClass() == self.WireClass or trace.Entity.WriteCell) + end + function TOOL:LeftClick_Update(trace) + CPULib.SetUploadTarget(trace.Entity, self:GetOwner()) + net.Start("ZCPU_RequestCode") net.Send(self:GetOwner()) + net.Start("CPULib.InvalidateDebugger") net.WriteUInt(0,2) net.Send(player) + end + function TOOL:MakeEnt(ply, model, Ang, trace) + local ent = WireLib.MakeWireEnt(ply, {Class = self.WireClass, Pos=trace.HitPos, Angle=Ang, Model=model}) + ent:SetMemoryModel(self:GetClientInfo("memorymodel"),self:GetClientInfo("customram"),self:GetClientInfo("customrom")) + ent:SetExtensionLoadOrder(self:GetClientInfo("extensions")) + self:LeftClick_Update(trace) + return ent + end + + + -- Right click: open editor + function TOOL:RightClick(trace) + net.Start("ZCPU_OpenEditor") net.Send(self:GetOwner()) + return true + end end if CLIENT then - ------------------------------------------------------------------------------ - -- Compiler callbacks on the compiling state - ------------------------------------------------------------------------------ - local function compile_success() - CPULib.Upload() - end - - local function compile_error(errorText) - print(errorText) - GAMEMODE:AddNotify(errorText,NOTIFY_GENERIC,7) - end - - - ------------------------------------------------------------------------------ - -- Request code to be compiled (called remotely from server) - ------------------------------------------------------------------------------ - function ZCPU_RequestCode() - if ZCPU_Editor then - CPULib.Compile(ZCPU_Editor:GetCode(),ZCPU_Editor:GetChosenFile(),compile_success,compile_error,"CPU",ZCPU_Editor.Location) - end - end - net.Receive("ZCPU_RequestCode", ZCPU_RequestCode) - - ------------------------------------------------------------------------------ - -- Open ZCPU editor - ------------------------------------------------------------------------------ - function ZCPU_OpenEditor() - if not ZCPU_Editor then - ZCPU_Editor = vgui.Create("Expression2EditorFrame") - CPULib.SetupEditor(ZCPU_Editor,"ZCPU Editor", "cpuchip", "CPU") - end - ZCPU_Editor:Open() - end - net.Receive("ZCPU_OpenEditor", ZCPU_OpenEditor) - - ------------------------------------------------------------------------------ - -- Build tool control panel - ------------------------------------------------------------------------------ - function TOOL.BuildCPanel(panel) - local Button = vgui.Create("DButton" , panel) - panel:AddPanel(Button) - Button:SetText("Online ZCPU documentation") - Button.DoClick = function(button) CPULib.ShowDocumentation("ZCPU") end - - - ---------------------------------------------------------------------------- - local currentDirectory - local FileBrowser = vgui.Create("wire_expression2_browser" , panel) - panel:AddPanel(FileBrowser) - FileBrowser:Setup("cpuchip") - FileBrowser:SetSize(235,400) - function FileBrowser:OnFileOpen(filepath, newtab) - if not ZCPU_Editor then - ZCPU_Editor = vgui.Create("Expression2EditorFrame") - CPULib.SetupEditor(ZCPU_Editor,"ZCPU Editor", "cpuchip", "CPU") - end - ZCPU_Editor:Open(filepath, nil, newtab) - end - - - ---------------------------------------------------------------------------- - local New = vgui.Create("DButton" , panel) - panel:AddPanel(New) - New:SetText("New file") - New.DoClick = function(button) - ZCPU_OpenEditor() - ZCPU_Editor:AutoSave() - ZCPU_Editor:NewScript(false) - end - panel:AddControl("Label", {Text = ""}) - - ---------------------------------------------------------------------------- - local OpenEditor = vgui.Create("DButton", panel) - panel:AddPanel(OpenEditor) - OpenEditor:SetText("Open Editor") - OpenEditor.DoClick = ZCPU_OpenEditor - - - ---------------------------------------------------------------------------- - panel:AddControl("Label", {Text = ""}) - panel:AddControl("Label", {Text = "CPU settings:"}) - - - ---------------------------------------------------------------------------- - local modelPanel = WireDermaExts.ModelSelect(panel, "wire_cpu_model", list.Get("Wire_gate_Models"), 2) - panel:AddControl("Label", {Text = ""}) - - - ---------------------------------------------------------------------------- - local memPanel = panel:AddControl("ComboBox", { - Label = "Memory model", - Options = { - ["128 bytes ROM only"] = {wire_cpu_memorymodel = "128rom"}, - ["128 bytes RAM/ROM"] = {wire_cpu_memorymodel = "128"}, - ["64KB RAM/ROM"] = {wire_cpu_memorymodel = "64krom"}, - ["64KB RAM only"] = {wire_cpu_memorymodel = "64k"}, - ["32KB RAM/ROM"] = {wire_cpu_memorymodel = "32krom"}, - ["32KB RAM only"] = {wire_cpu_memorymodel = "32k"}, - ["8KB RAM/ROM"] = {wire_cpu_memorymodel = "8krom"}, - ["8KB RAM only"] = {wire_cpu_memorymodel = "8k"}, - ["128KB RAM/ROM"] = {wire_cpu_memorymodel = "128krom"}, - ["No internal RAM/ROM"] = {wire_cpu_memorymodel = "flat"}, - ["Custom RAM/ROM"] = {wire_cpu_memorymodel = "custom"}, - } - }) - panel:AddControl("Label", {Text = "Sets the processor memory model (determines interaction with the external devices)"}) - local customMemPanel = { - panel:AddControl("Label", {Text = "Custom memory size for RAM and ROM is in pages(128 bytes)"}), - panel:AddControl("Slider", {Label = "RAM size", Command = "wire_cpu_customram", Min = 0, Max = 1024}), - panel:AddControl("Slider", {Label = "ROM size", Command = "wire_cpu_customrom", Min = 0, Max = 1024}), - } - local function formatPageSizeString(pages) - if pages >= 8 then - return string.format("KB %g",pages/8) -- padding with spaces won't help because the font isn't monospaced - else - return string.format(" B %g",pages*128) - end - end - customMemPanel[2].OnValueChanged = function(self,pages) - self:SetText("RAM size "..formatPageSizeString(pages)) - end - customMemPanel[3].OnValueChanged = function(self,pages) - self:SetText("ROM size "..formatPageSizeString(pages)) - end - - local memoryModel = GetConVar("wire_cpu_memorymodel") - function memPanel:OnSelect(index, value, data) - if data.wire_cpu_memorymodel == "custom" then - for _,i in ipairs(customMemPanel) do - i:Show() - end - else - for _,i in ipairs(customMemPanel) do - i:Hide() - end - end - -- Overriding this turns off the automatic convar write, so we have to do it ourselves. - memoryModel:SetString(data.wire_cpu_memorymodel) - -- Rebuild the layout to prevent an unsightly gap - panel:InvalidateLayout() - panel:InvalidateChildren(true) - end - memPanel:OnSelect(0,0,{wire_cpu_memorymodel = memoryModel:GetString()}) -- Simulate one on-select to correct the show/hide status - local enabledExtensionOrder = {} - local enabledExtensionLookup = {} - local extensionConvar = GetConVar("wire_cpu_extensions") - for ext in string.gmatch(extensionConvar:GetString() or "","([^;]*);") do - if CPULib.Extensions["CPU"] and CPULib.Extensions["CPU"][ext] then - enabledExtensionLookup[ext] = true - table.insert(enabledExtensionOrder,ext) - end - end - - local ExtensionPanel = vgui.Create("DListView") - local DisabledExtensionPanel = vgui.Create("DListView") - ExtensionPanel:AddColumn("Enabled Extensions") - DisabledExtensionPanel:AddColumn("Disabled Extensions") - ExtensionPanel:SetSize(235,200) - DisabledExtensionPanel:SetSize(235,200) - - if CPULib.Extensions["CPU"] then - for k,_ in pairs(CPULib.Extensions["CPU"]) do - if enabledExtensionLookup[k] then - ExtensionPanel:AddLine(k) - else - DisabledExtensionPanel:AddLine(k) - end - end - end - - local function ReloadExtensions() - local extensions = {} - for _,line in pairs(ExtensionPanel:GetLines()) do - table.insert(extensions,line:GetValue(1)) - end - extensionConvar:SetString(CPULib:ToExtensionString(extensions)) - CPULib:LoadExtensionOrder(extensions,"CPU") - end - - function ExtensionPanel:OnRowSelected(rIndex,row) - DisabledExtensionPanel:AddLine(row:GetValue(1)) - self:RemoveLine(rIndex) - ReloadExtensions() - end - - function DisabledExtensionPanel:OnRowSelected(rIndex,row) - ExtensionPanel:AddLine(row:GetValue(1)) - self:RemoveLine(rIndex) - ReloadExtensions() - end - - panel:AddItem(ExtensionPanel) - panel:AddItem(DisabledExtensionPanel) - -- Reload the extensions at least once to make sure users don't have to touch the list - -- in order to use extensions on first opening of the tool menu - ReloadExtensions() - - end - - - ------------------------------------------------------------------------------ - -- Tool screen - ------------------------------------------------------------------------------ - net.Receive("CPULib.ServerUploading", function(netlen) - CPULib.ServerUploading = net.ReadBit() ~= 0 - end) - - local fontData = - { - font = "Lucida Console", - size = 30, - weight = 1000, - antialias = true, - additive = false - } - surface.CreateFont( "ZCPUToolScreenFont", fontData ) - fontData.size = 26 - surface.CreateFont( "ZCPUToolScreenFontSmall", fontData ) - - local function outc(text,y,color) draw.DrawText(text or "","ZCPUToolScreenFont",2,32*y,color,0) end - local prevStateTime = RealTime() - local prevState = nil - local consoleHistory = { "", "", "", "", "", "" } - local stageName = {"Preprocessing","Tokenizing","Parsing","Generating","Optimizing","Resolving","Outputting"} - local stageNameShort = {"Preproc","Tokenize","Parse","Generate","Optimize","Resolve","Output"} - - local function outform(x,y,w,h,title) - surface.SetDrawColor(255, 255, 255, 255) - surface.DrawRect(x*28-3,y*32-3,w*28,h*32) - - surface.SetDrawColor(0, 0, 0, 255) - surface.DrawRect(x*28+3,y*32+3,w*28,h*32) - - surface.SetDrawColor(192, 220, 192, 255) - surface.DrawRect(x*28,y*32,w*28-3,h*32-3) - - surface.SetDrawColor(192, 192, 192, 255) - surface.DrawRect(x*28,y*32,w*28,h*32) - - if title then - surface.SetDrawColor(0, 0, 128, 255) - surface.DrawRect(x*28+4,y*32+4,w*28-4,1*32-4) - draw.DrawText(title,"ZCPUToolScreenFontSmall",x*28+4,y*32+4,Color(255,255,255,255),0) - end - end - - function CPULib.RenderCPUTool(screenIndex,toolName) - if screenIndex == 0 then - surface.SetDrawColor(0, 0, 128, 255) - surface.DrawRect(0, 0, 256, 256) - - surface.SetDrawColor(240, 240, 0, 255) - surface.DrawRect(0,0,256,32) - outc(" ToolOS r"..VERSION.." ",0,Color(0,0,0,255)) - - if CPULib.Uploading then - outc("Program size:",2,Color(255,255,255,255)) - outc(string.format("%d bytes",CPULib.TotalUploadData),3,Color(255,255,255,255)) - outc(string.format("Uploading %2d%%",100-100*CPULib.RemainingUploadData/(CPULib.TotalUploadData+1e-12)),5,Color(255,255,255,255)) - outc(string.format("%d bytes",CPULib.RemainingUploadData),6,Color(255,255,255,255)) - prevStateTime = RealTime() - elseif CPULib.ServerUploading then - outc("Program size:",2,Color(255,255,255,255)) - outc(string.format("%d bytes",#CPULib.Buffer),3,Color(255,255,255,255)) - outc("Uploading 100",5,Color(255,255,255,255)) - outc(" Standby ",6,Color(255,255,255,255)) - prevStateTime = RealTime() - elseif CPULib.Compiling then - outc(string.format("Stage %2d/7",HCOMP.Stage+1),2,Color(255,255,255,255)) - outc(stageName[HCOMP.Stage+1],3,Color(255,255,255,255)) - prevStateTime = RealTime() - else - if RealTime() - prevStateTime > 0.15 then - outc("Flash utility",1,Color(255,255,255,255)) - outc("(C) 2007-2011",2,Color(255,255,255,255)) - outc("Black Phoenix",3,Color(255,255,255,255)) - - outc(string.format("RAM: %5d KB",collectgarbage("count") or 0),7,Color(255,255,255,255)) - else - surface.SetDrawColor(0, 0, 0, 255) - surface.DrawRect(0, 0, 256, 256) - end - end - elseif screenIndex == 1 then - surface.SetDrawColor(0, 0, 0, 255) - surface.DrawRect(0, 0, 256, 256) - - surface.SetDrawColor(240, 120, 0, 255) - surface.DrawRect(16*(#toolName+1),32*0+14,256,4) - outc(toolName,0,Color(240, 120,0,255)) - outc(string.format(" RAM %5d KB",collectgarbage("count") or 0),1,Color(255,255,255,255)) - - surface.SetDrawColor(240, 120, 0, 255) - surface.DrawRect(16*(5),32*2+14,256,4) - outc("TASK",2,Color(240, 120,0,255)) - outc(" STATUS",3,Color(255,255,255,255)) - - surface.SetDrawColor(240, 120, 0, 255) - surface.DrawRect(16*(4),32*6+14,256,4) - outc("NET",6,Color(240, 120,0,255)) - if CPULib.Uploading then - outc(string.format("UP %.3f KB",CPULib.RemainingUploadData/1024),7,Color(255,255,255,255)) - outc(string.format("ROMUPL [%3d%%]",100-100*CPULib.RemainingUploadData/(CPULib.TotalUploadData+1e-12)),4,Color(255,255,255,255)) - outc("UPLMON [ OK ]",5,Color(255,255,255,255)) - elseif CPULib.ServerUploading then - outc("UPLMON [ OK ]",4,Color(255,255,255,255)) - outc("DOWN SYNC",7,Color(255,255,255,255)) - elseif CPULib.Compiling then - outc(string.format("HCOMP [%2d/7]",HCOMP.Stage),4,Color(255,255,255,255)) - outc("IDLE",7,Color(255,255,255,255)) - else - outc("IDLE",7,Color(255,255,255,255)) - end - elseif screenIndex == 2 then - surface.SetDrawColor(0, 0, 0, 255) - surface.DrawRect(0, 0, 256, 256) - - outc("TL-UNIX "..(VERSION/100),0,Color(200,200,200,255)) - - outc(consoleHistory[1],2,Color(200,200,200,255)) - outc(consoleHistory[2],3,Color(200,200,200,255)) - outc(consoleHistory[3],4,Color(200,200,200,255)) - outc(consoleHistory[4],5,Color(200,200,200,255)) - outc(consoleHistory[5],6,Color(200,200,200,255)) - outc(consoleHistory[6],7,Color(200,200,200,255)) - - if CPULib.Uploading then - if prevState ~= 0 then - consoleHistory[1] = consoleHistory[2] - consoleHistory[2] = consoleHistory[3] - consoleHistory[3] = consoleHistory[4] - consoleHistory[4] = string.lower(toolName).."@:/# upl" - end - - consoleHistory[5] = string.format(" %3d%%",100-100*CPULib.RemainingUploadData/(CPULib.TotalUploadData+1e-12)) - consoleHistory[6] = string.format(" %d B",CPULib.RemainingUploadData) - - prevState = 0 - elseif CPULib.ServerUploading then - consoleHistory[5] = " ###" - consoleHistory[6] = " 0 B" - prevState = 0 - elseif CPULib.Compiling then - if prevState ~= 1 then - consoleHistory[1] = consoleHistory[2] - consoleHistory[2] = consoleHistory[3] - consoleHistory[3] = consoleHistory[4] - consoleHistory[4] = consoleHistory[5] - consoleHistory[5] = string.lower(toolName).."@:/# hcmp" - end - consoleHistory[6] = string.format("Stage %2d/7",HCOMP.Stage+1) - prevState = 1 - else - if prevState ~= 2 then - consoleHistory[1] = consoleHistory[2] - consoleHistory[2] = consoleHistory[3] - consoleHistory[3] = consoleHistory[4] - consoleHistory[4] = consoleHistory[5] - consoleHistory[5] = consoleHistory[6] - consoleHistory[6] = string.lower(toolName).."@:/# " - end - prevState = 2 - end - elseif screenIndex == 3 then - surface.SetDrawColor(0, 128, 128, 255) - surface.DrawRect(0, 0, 256, 256) - - outform(0,7,12,1) - - outform(0,7,3,1) - outc("MENU",7,Color(0,0,0,255)) - - if CPULib.Uploading then - outform(1,1,7,5,"Upload") - outc(string.format(" %.3f kb",CPULib.RemainingUploadData/1024),3,Color(0,0,0,255)) - outc(string.format(" %3d%% done",100-100*CPULib.RemainingUploadData/(CPULib.TotalUploadData+1e-12)),4,Color(0,0,0,255)) - - outform(1,5,7,0.9) - surface.SetDrawColor(0, 0, 128, 255) - surface.DrawRect(1*28+4,5*32+4, - math.floor((7*28-4)*(1-CPULib.RemainingUploadData/(CPULib.TotalUploadData+1e-12))/14)*14, - 1*32-8) - elseif CPULib.ServerUploading then - outform(1,3,7,3,"Upload") - outc(" Standby",5,Color(0,0,0,255)) - elseif CPULib.Compiling then - outform(1,1,7,5,"HL-ZASM") - outc(string.format(" Stage %d/7",HCOMP.Stage+1),3,Color(0,0,0,255)) - outc(" "..stageNameShort[HCOMP.Stage+1],4,Color(0,0,0,255)) - else - -- - end - end - end - - function TOOL:DrawToolScreen(width, height) - local currentTime = os.date("*t") - CPULib.RenderCPUTool(currentTime.yday % 4,"CPU") - end + ------------------------------------------------------------------------------ + -- Compiler callbacks on the compiling state + ------------------------------------------------------------------------------ + local function compile_success() + CPULib.Upload() + end + + local function compile_error(errorText) + print(errorText) + GAMEMODE:AddNotify(errorText,NOTIFY_GENERIC,7) + end + + + ------------------------------------------------------------------------------ + -- Request code to be compiled (called remotely from server) + ------------------------------------------------------------------------------ + function ZCPU_RequestCode() + if ZCPU_Editor then + CPULib.Compile(ZCPU_Editor:GetCode(),ZCPU_Editor:GetChosenFile(),compile_success,compile_error,"CPU",ZCPU_Editor.Location) + end + end + net.Receive("ZCPU_RequestCode", ZCPU_RequestCode) + + ------------------------------------------------------------------------------ + -- Open ZCPU editor + ------------------------------------------------------------------------------ + function ZCPU_OpenEditor() + if not ZCPU_Editor then + ZCPU_Editor = vgui.Create("Expression2EditorFrame") + CPULib.SetupEditor(ZCPU_Editor,"ZCPU Editor", "cpuchip", "CPU") + end + ZCPU_Editor:Open() + end + net.Receive("ZCPU_OpenEditor", ZCPU_OpenEditor) + + ------------------------------------------------------------------------------ + -- Build tool control panel + ------------------------------------------------------------------------------ + function TOOL.BuildCPanel(panel) + local Button = vgui.Create("DButton" , panel) + panel:AddPanel(Button) + Button:SetText("Online ZCPU documentation") + Button.DoClick = function(button) CPULib.ShowDocumentation("ZCPU") end + + + ---------------------------------------------------------------------------- + local currentDirectory + local FileBrowser = vgui.Create("wire_expression2_browser" , panel) + panel:AddPanel(FileBrowser) + FileBrowser:Setup("cpuchip") + FileBrowser:SetSize(235,400) + function FileBrowser:OnFileOpen(filepath, newtab) + if not ZCPU_Editor then + ZCPU_Editor = vgui.Create("Expression2EditorFrame") + CPULib.SetupEditor(ZCPU_Editor,"ZCPU Editor", "cpuchip", "CPU") + end + ZCPU_Editor:Open(filepath, nil, newtab) + end + + + ---------------------------------------------------------------------------- + local New = vgui.Create("DButton" , panel) + panel:AddPanel(New) + New:SetText("New file") + New.DoClick = function(button) + ZCPU_OpenEditor() + ZCPU_Editor:AutoSave() + ZCPU_Editor:NewScript(false) + end + panel:AddControl("Label", {Text = ""}) + + ---------------------------------------------------------------------------- + local OpenEditor = vgui.Create("DButton", panel) + panel:AddPanel(OpenEditor) + OpenEditor:SetText("Open Editor") + OpenEditor.DoClick = ZCPU_OpenEditor + + + ---------------------------------------------------------------------------- + panel:AddControl("Label", {Text = ""}) + panel:AddControl("Label", {Text = "CPU settings:"}) + + + ---------------------------------------------------------------------------- + local modelPanel = WireDermaExts.ModelSelect(panel, "wire_cpu_model", list.Get("Wire_gate_Models"), 2) + panel:AddControl("Label", {Text = ""}) + + + ---------------------------------------------------------------------------- + local memPanel = panel:AddControl("ComboBox", { + Label = "Memory model", + Options = { + ["128 bytes ROM only"] = {wire_cpu_memorymodel = "128rom"}, + ["128 bytes RAM/ROM"] = {wire_cpu_memorymodel = "128"}, + ["64KB RAM/ROM"] = {wire_cpu_memorymodel = "64krom"}, + ["64KB RAM only"] = {wire_cpu_memorymodel = "64k"}, + ["32KB RAM/ROM"] = {wire_cpu_memorymodel = "32krom"}, + ["32KB RAM only"] = {wire_cpu_memorymodel = "32k"}, + ["8KB RAM/ROM"] = {wire_cpu_memorymodel = "8krom"}, + ["8KB RAM only"] = {wire_cpu_memorymodel = "8k"}, + ["128KB RAM/ROM"] = {wire_cpu_memorymodel = "128krom"}, + ["No internal RAM/ROM"] = {wire_cpu_memorymodel = "flat"}, + ["Custom RAM/ROM"] = {wire_cpu_memorymodel = "custom"}, + } + }) + panel:AddControl("Label", {Text = "Sets the processor memory model (determines interaction with the external devices)"}) + local customMemPanel = { + panel:AddControl("Label", {Text = "Custom memory size for RAM and ROM is in pages(128 bytes)"}), + panel:AddControl("Slider", {Label = "RAM size", Command = "wire_cpu_customram", Min = 0, Max = 1024}), + panel:AddControl("Slider", {Label = "ROM size", Command = "wire_cpu_customrom", Min = 0, Max = 1024}), + } + local function formatPageSizeString(pages) + if pages >= 8 then + return string.format("KB %g",pages/8) -- padding with spaces won't help because the font isn't monospaced + else + return string.format(" B %g",pages*128) + end + end + customMemPanel[2].OnValueChanged = function(self,pages) + self:SetText("RAM size "..formatPageSizeString(pages)) + end + customMemPanel[3].OnValueChanged = function(self,pages) + self:SetText("ROM size "..formatPageSizeString(pages)) + end + + local memoryModel = GetConVar("wire_cpu_memorymodel") + function memPanel:OnSelect(index, value, data) + if data.wire_cpu_memorymodel == "custom" then + for _,i in ipairs(customMemPanel) do + i:Show() + end + else + for _,i in ipairs(customMemPanel) do + i:Hide() + end + end + -- Overriding this turns off the automatic convar write, so we have to do it ourselves. + memoryModel:SetString(data.wire_cpu_memorymodel) + -- Rebuild the layout to prevent an unsightly gap + panel:InvalidateLayout() + panel:InvalidateChildren(true) + end + memPanel:OnSelect(0,0,{wire_cpu_memorymodel = memoryModel:GetString()}) -- Simulate one on-select to correct the show/hide status + local enabledExtensionOrder = {} + local enabledExtensionLookup = {} + local extensionConvar = GetConVar("wire_cpu_extensions") + for ext in string.gmatch(extensionConvar:GetString() or "","([^;]*);") do + if CPULib.Extensions["CPU"] and CPULib.Extensions["CPU"][ext] then + enabledExtensionLookup[ext] = true + table.insert(enabledExtensionOrder,ext) + end + end + + local ExtensionPanel = vgui.Create("DListView") + local DisabledExtensionPanel = vgui.Create("DListView") + ExtensionPanel:AddColumn("Enabled Extensions") + DisabledExtensionPanel:AddColumn("Disabled Extensions") + ExtensionPanel:SetSize(235,200) + DisabledExtensionPanel:SetSize(235,200) + + if CPULib.Extensions["CPU"] then + for k,_ in pairs(CPULib.Extensions["CPU"]) do + if enabledExtensionLookup[k] then + ExtensionPanel:AddLine(k) + else + DisabledExtensionPanel:AddLine(k) + end + end + end + + local function ReloadExtensions() + local extensions = {} + for _,line in pairs(ExtensionPanel:GetLines()) do + table.insert(extensions,line:GetValue(1)) + end + extensionConvar:SetString(CPULib:ToExtensionString(extensions)) + CPULib:LoadExtensionOrder(extensions,"CPU") + end + + function ExtensionPanel:OnRowSelected(rIndex,row) + DisabledExtensionPanel:AddLine(row:GetValue(1)) + self:RemoveLine(rIndex) + ReloadExtensions() + end + + function DisabledExtensionPanel:OnRowSelected(rIndex,row) + ExtensionPanel:AddLine(row:GetValue(1)) + self:RemoveLine(rIndex) + ReloadExtensions() + end + + panel:AddItem(ExtensionPanel) + panel:AddItem(DisabledExtensionPanel) + -- Reload the extensions at least once to make sure users don't have to touch the list + -- in order to use extensions on first opening of the tool menu + ReloadExtensions() + + end + + + ------------------------------------------------------------------------------ + -- Tool screen + ------------------------------------------------------------------------------ + net.Receive("CPULib.ServerUploading", function(netlen) + CPULib.ServerUploading = net.ReadBit() ~= 0 + end) + + local fontData = + { + font = "Lucida Console", + size = 30, + weight = 1000, + antialias = true, + additive = false + } + surface.CreateFont( "ZCPUToolScreenFont", fontData ) + fontData.size = 26 + surface.CreateFont( "ZCPUToolScreenFontSmall", fontData ) + + local function outc(text,y,color) draw.DrawText(text or "","ZCPUToolScreenFont",2,32*y,color,0) end + local prevStateTime = RealTime() + local prevState = nil + local consoleHistory = { "", "", "", "", "", "" } + local stageName = {"Preprocessing","Tokenizing","Parsing","Generating","Optimizing","Resolving","Outputting"} + local stageNameShort = {"Preproc","Tokenize","Parse","Generate","Optimize","Resolve","Output"} + + local function outform(x,y,w,h,title) + surface.SetDrawColor(255, 255, 255, 255) + surface.DrawRect(x*28-3,y*32-3,w*28,h*32) + + surface.SetDrawColor(0, 0, 0, 255) + surface.DrawRect(x*28+3,y*32+3,w*28,h*32) + + surface.SetDrawColor(192, 220, 192, 255) + surface.DrawRect(x*28,y*32,w*28-3,h*32-3) + + surface.SetDrawColor(192, 192, 192, 255) + surface.DrawRect(x*28,y*32,w*28,h*32) + + if title then + surface.SetDrawColor(0, 0, 128, 255) + surface.DrawRect(x*28+4,y*32+4,w*28-4,1*32-4) + draw.DrawText(title,"ZCPUToolScreenFontSmall",x*28+4,y*32+4,Color(255,255,255,255),0) + end + end + + function CPULib.RenderCPUTool(screenIndex,toolName) + if screenIndex == 0 then + surface.SetDrawColor(0, 0, 128, 255) + surface.DrawRect(0, 0, 256, 256) + + surface.SetDrawColor(240, 240, 0, 255) + surface.DrawRect(0,0,256,32) + outc(" ToolOS r"..VERSION.." ",0,Color(0,0,0,255)) + + if CPULib.Uploading then + outc("Program size:",2,Color(255,255,255,255)) + outc(string.format("%d bytes",CPULib.TotalUploadData),3,Color(255,255,255,255)) + outc(string.format("Uploading %2d%%",100-100*CPULib.RemainingUploadData/(CPULib.TotalUploadData+1e-12)),5,Color(255,255,255,255)) + outc(string.format("%d bytes",CPULib.RemainingUploadData),6,Color(255,255,255,255)) + prevStateTime = RealTime() + elseif CPULib.ServerUploading then + outc("Program size:",2,Color(255,255,255,255)) + outc(string.format("%d bytes",#CPULib.Buffer),3,Color(255,255,255,255)) + outc("Uploading 100",5,Color(255,255,255,255)) + outc(" Standby ",6,Color(255,255,255,255)) + prevStateTime = RealTime() + elseif CPULib.Compiling then + outc(string.format("Stage %2d/7",HCOMP.Stage+1),2,Color(255,255,255,255)) + outc(stageName[HCOMP.Stage+1],3,Color(255,255,255,255)) + prevStateTime = RealTime() + else + if RealTime() - prevStateTime > 0.15 then + outc("Flash utility",1,Color(255,255,255,255)) + outc("(C) 2007-2011",2,Color(255,255,255,255)) + outc("Black Phoenix",3,Color(255,255,255,255)) + + outc(string.format("RAM: %5d KB",collectgarbage("count") or 0),7,Color(255,255,255,255)) + else + surface.SetDrawColor(0, 0, 0, 255) + surface.DrawRect(0, 0, 256, 256) + end + end + elseif screenIndex == 1 then + surface.SetDrawColor(0, 0, 0, 255) + surface.DrawRect(0, 0, 256, 256) + + surface.SetDrawColor(240, 120, 0, 255) + surface.DrawRect(16*(#toolName+1),32*0+14,256,4) + outc(toolName,0,Color(240, 120,0,255)) + outc(string.format(" RAM %5d KB",collectgarbage("count") or 0),1,Color(255,255,255,255)) + + surface.SetDrawColor(240, 120, 0, 255) + surface.DrawRect(16*(5),32*2+14,256,4) + outc("TASK",2,Color(240, 120,0,255)) + outc(" STATUS",3,Color(255,255,255,255)) + + surface.SetDrawColor(240, 120, 0, 255) + surface.DrawRect(16*(4),32*6+14,256,4) + outc("NET",6,Color(240, 120,0,255)) + if CPULib.Uploading then + outc(string.format("UP %.3f KB",CPULib.RemainingUploadData/1024),7,Color(255,255,255,255)) + outc(string.format("ROMUPL [%3d%%]",100-100*CPULib.RemainingUploadData/(CPULib.TotalUploadData+1e-12)),4,Color(255,255,255,255)) + outc("UPLMON [ OK ]",5,Color(255,255,255,255)) + elseif CPULib.ServerUploading then + outc("UPLMON [ OK ]",4,Color(255,255,255,255)) + outc("DOWN SYNC",7,Color(255,255,255,255)) + elseif CPULib.Compiling then + outc(string.format("HCOMP [%2d/7]",HCOMP.Stage),4,Color(255,255,255,255)) + outc("IDLE",7,Color(255,255,255,255)) + else + outc("IDLE",7,Color(255,255,255,255)) + end + elseif screenIndex == 2 then + surface.SetDrawColor(0, 0, 0, 255) + surface.DrawRect(0, 0, 256, 256) + + outc("TL-UNIX "..(VERSION/100),0,Color(200,200,200,255)) + + outc(consoleHistory[1],2,Color(200,200,200,255)) + outc(consoleHistory[2],3,Color(200,200,200,255)) + outc(consoleHistory[3],4,Color(200,200,200,255)) + outc(consoleHistory[4],5,Color(200,200,200,255)) + outc(consoleHistory[5],6,Color(200,200,200,255)) + outc(consoleHistory[6],7,Color(200,200,200,255)) + + if CPULib.Uploading then + if prevState ~= 0 then + consoleHistory[1] = consoleHistory[2] + consoleHistory[2] = consoleHistory[3] + consoleHistory[3] = consoleHistory[4] + consoleHistory[4] = string.lower(toolName).."@:/# upl" + end + + consoleHistory[5] = string.format(" %3d%%",100-100*CPULib.RemainingUploadData/(CPULib.TotalUploadData+1e-12)) + consoleHistory[6] = string.format(" %d B",CPULib.RemainingUploadData) + + prevState = 0 + elseif CPULib.ServerUploading then + consoleHistory[5] = " ###" + consoleHistory[6] = " 0 B" + prevState = 0 + elseif CPULib.Compiling then + if prevState ~= 1 then + consoleHistory[1] = consoleHistory[2] + consoleHistory[2] = consoleHistory[3] + consoleHistory[3] = consoleHistory[4] + consoleHistory[4] = consoleHistory[5] + consoleHistory[5] = string.lower(toolName).."@:/# hcmp" + end + consoleHistory[6] = string.format("Stage %2d/7",HCOMP.Stage+1) + prevState = 1 + else + if prevState ~= 2 then + consoleHistory[1] = consoleHistory[2] + consoleHistory[2] = consoleHistory[3] + consoleHistory[3] = consoleHistory[4] + consoleHistory[4] = consoleHistory[5] + consoleHistory[5] = consoleHistory[6] + consoleHistory[6] = string.lower(toolName).."@:/# " + end + prevState = 2 + end + elseif screenIndex == 3 then + surface.SetDrawColor(0, 128, 128, 255) + surface.DrawRect(0, 0, 256, 256) + + outform(0,7,12,1) + + outform(0,7,3,1) + outc("MENU",7,Color(0,0,0,255)) + + if CPULib.Uploading then + outform(1,1,7,5,"Upload") + outc(string.format(" %.3f kb",CPULib.RemainingUploadData/1024),3,Color(0,0,0,255)) + outc(string.format(" %3d%% done",100-100*CPULib.RemainingUploadData/(CPULib.TotalUploadData+1e-12)),4,Color(0,0,0,255)) + + outform(1,5,7,0.9) + surface.SetDrawColor(0, 0, 128, 255) + surface.DrawRect(1*28+4,5*32+4, + math.floor((7*28-4)*(1-CPULib.RemainingUploadData/(CPULib.TotalUploadData+1e-12))/14)*14, + 1*32-8) + elseif CPULib.ServerUploading then + outform(1,3,7,3,"Upload") + outc(" Standby",5,Color(0,0,0,255)) + elseif CPULib.Compiling then + outform(1,1,7,5,"HL-ZASM") + outc(string.format(" Stage %d/7",HCOMP.Stage+1),3,Color(0,0,0,255)) + outc(" "..stageNameShort[HCOMP.Stage+1],4,Color(0,0,0,255)) + else + -- + end + end + end + + function TOOL:DrawToolScreen(width, height) + local currentTime = os.date("*t") + CPULib.RenderCPUTool(currentTime.yday % 4,"CPU") + end end diff --git a/lua/wire/stools/gpu.lua b/lua/wire/stools/gpu.lua index 323febbc53..121d9e10cb 100644 --- a/lua/wire/stools/gpu.lua +++ b/lua/wire/stools/gpu.lua @@ -2,243 +2,243 @@ WireToolSetup.setCategory( "Chips, Gates", "Visuals/Screens", "Advanced" ) WireToolSetup.open( "gpu", "GPU", "gmod_wire_gpu", nil, "GPUs" ) if CLIENT then - language.Add("Tool.wire_gpu.name", "GPU Tool (Wire)") - language.Add("Tool.wire_gpu.desc", "Spawns a graphics processing unit") - language.Add("ToolWiregpu_Model", "Model:" ) - TOOL.Information = { - { name = "left", text = "Upload program to hispeed device" }, - { name = "right", text = "open editor and/or attach debugger to the ZGPU" }, - { name = "reload", text = "Wipe ROM/RAM and reset memory model" }, - } - - WireToolSetup.setToolMenuIcon("icon16/monitor.png") + language.Add("Tool.wire_gpu.name", "GPU Tool (Wire)") + language.Add("Tool.wire_gpu.desc", "Spawns a graphics processing unit") + language.Add("ToolWiregpu_Model", "Model:" ) + TOOL.Information = { + { name = "left", text = "Upload program to hispeed device" }, + { name = "right", text = "open editor and/or attach debugger to the ZGPU" }, + { name = "reload", text = "Wipe ROM/RAM and reset memory model" }, + } + + WireToolSetup.setToolMenuIcon("icon16/monitor.png") end WireToolSetup.BaseLang() WireToolSetup.SetupMax( 7 ) TOOL.ClientConVar = { - model = "models/cheeze/wires/cpu.mdl", - filename = "", - memorymodel = "64k", - extensions = "" + model = "models/cheeze/wires/cpu.mdl", + filename = "", + memorymodel = "64k", + extensions = "" } if CLIENT then - ------------------------------------------------------------------------------ - -- Make sure firing animation is displayed clientside - ------------------------------------------------------------------------------ - function TOOL:LeftClick() return true end - function TOOL:Reload() return true end - function TOOL:RightClick() return false end + ------------------------------------------------------------------------------ + -- Make sure firing animation is displayed clientside + ------------------------------------------------------------------------------ + function TOOL:LeftClick() return true end + function TOOL:Reload() return true end + function TOOL:RightClick() return false end end if SERVER then - util.AddNetworkString("ZGPU_RequestCode") - util.AddNetworkString("ZGPU_OpenEditor") - ------------------------------------------------------------------------------ - -- Reload: wipe ROM/RAM and reset memory model - ------------------------------------------------------------------------------ - function TOOL:Reload(trace) - if trace.Entity:IsPlayer() then return false end - - local player = self:GetOwner() - if (trace.Entity:IsValid()) and - (trace.Entity:GetClass() == "gmod_wire_gpu") then - trace.Entity:SetMemoryModel(self:GetClientInfo("memorymodel")) - return true - end - end - - -- Left click: spawn GPU or upload current program into it - function TOOL:CheckHitOwnClass(trace) - return trace.Entity:IsValid() and (trace.Entity:GetClass() == self.WireClass or trace.Entity.WriteCell) - end - function TOOL:LeftClick_Update(trace) - CPULib.SetUploadTarget(trace.Entity, self:GetOwner()) - net.Start("ZGPU_RequestCode") net.Send(self:GetOwner()) - end - function TOOL:MakeEnt(ply, model, Ang, trace) - local ent = WireLib.MakeWireEnt(ply, {Class = self.WireClass, Pos=trace.HitPos, Angle=Ang, Model=model}) - ent:SetMemoryModel(self:GetClientInfo("memorymodel")) - ent:SetExtensionLoadOrder(self:GetClientInfo("extensions")) - self:LeftClick_Update(trace) - return ent - end - - - function TOOL:RightClick(trace) - net.Start("ZGPU_OpenEditor") net.Send(self:GetOwner()) - return true - end + util.AddNetworkString("ZGPU_RequestCode") + util.AddNetworkString("ZGPU_OpenEditor") + ------------------------------------------------------------------------------ + -- Reload: wipe ROM/RAM and reset memory model + ------------------------------------------------------------------------------ + function TOOL:Reload(trace) + if trace.Entity:IsPlayer() then return false end + + local player = self:GetOwner() + if (trace.Entity:IsValid()) and + (trace.Entity:GetClass() == "gmod_wire_gpu") then + trace.Entity:SetMemoryModel(self:GetClientInfo("memorymodel")) + return true + end + end + + -- Left click: spawn GPU or upload current program into it + function TOOL:CheckHitOwnClass(trace) + return trace.Entity:IsValid() and (trace.Entity:GetClass() == self.WireClass or trace.Entity.WriteCell) + end + function TOOL:LeftClick_Update(trace) + CPULib.SetUploadTarget(trace.Entity, self:GetOwner()) + net.Start("ZGPU_RequestCode") net.Send(self:GetOwner()) + end + function TOOL:MakeEnt(ply, model, Ang, trace) + local ent = WireLib.MakeWireEnt(ply, {Class = self.WireClass, Pos=trace.HitPos, Angle=Ang, Model=model}) + ent:SetMemoryModel(self:GetClientInfo("memorymodel")) + ent:SetExtensionLoadOrder(self:GetClientInfo("extensions")) + self:LeftClick_Update(trace) + return ent + end + + + function TOOL:RightClick(trace) + net.Start("ZGPU_OpenEditor") net.Send(self:GetOwner()) + return true + end end if CLIENT then - ------------------------------------------------------------------------------ - -- Compiler callbacks on the compiling state - ------------------------------------------------------------------------------ - local function compile_success() - CPULib.Upload() - end - - local function compile_error(errorText) - print(errorText) - GAMEMODE:AddNotify(errorText,NOTIFY_GENERIC,7) - end - - - ------------------------------------------------------------------------------ - -- Request code to be compiled (called remotely from server) - ------------------------------------------------------------------------------ - function ZGPU_RequestCode() - if ZGPU_Editor then - CPULib.Debugger.SourceTab = ZGPU_Editor:GetActiveTab() - CPULib.Compile(ZGPU_Editor:GetCode(),ZGPU_Editor:GetChosenFile(),compile_success,compile_error,"GPU",ZGPU_Editor.Location) - end - end - net.Receive("ZGPU_RequestCode", ZGPU_RequestCode) - - ------------------------------------------------------------------------------ - -- Open ZGPU editor - ------------------------------------------------------------------------------ - function ZGPU_OpenEditor() - if not ZGPU_Editor then - ZGPU_Editor = vgui.Create("Expression2EditorFrame") - CPULib.SetupEditor(ZGPU_Editor,"ZGPU Editor", "gpuchip", "GPU") - end - ZGPU_Editor:Open() - end - net.Receive("ZGPU_OpenEditor", ZGPU_OpenEditor) - - ------------------------------------------------------------------------------ - -- Build tool control panel - ------------------------------------------------------------------------------ - function TOOL.BuildCPanel(panel) - local Button = vgui.Create("DButton" , panel) - panel:AddPanel(Button) - Button:SetText("Online ZGPU documentation") - Button.DoClick = function(button) CPULib.ShowDocumentation("ZGPU") end - - - ---------------------------------------------------------------------------- - local currentDirectory - local FileBrowser = vgui.Create("wire_expression2_browser" , panel) - panel:AddPanel(FileBrowser) - FileBrowser:Setup("GPUChip") - FileBrowser:SetSize(235,400) - function FileBrowser:OnFileOpen(filepath, newtab) - if not ZGPU_Editor then - ZGPU_Editor = vgui.Create("Expression2EditorFrame") - CPULib.SetupEditor(ZGPU_Editor,"ZGPU Editor", "gpuchip", "GPU") - end - ZGPU_Editor:Open(filepath, nil, newtab) - end - - - ---------------------------------------------------------------------------- - local New = vgui.Create("DButton" , panel) - panel:AddPanel(New) - New:SetText("New file") - New.DoClick = function(button) - ZGPU_OpenEditor() - ZGPU_Editor:AutoSave() - ZGPU_Editor:NewScript(false) - end - panel:AddControl("Label", {Text = ""}) - - ---------------------------------------------------------------------------- - local OpenEditor = vgui.Create("DButton", panel) - panel:AddPanel(OpenEditor) - OpenEditor:SetText("Open Editor") - OpenEditor.DoClick = ZGPU_OpenEditor - - - ---------------------------------------------------------------------------- - local modelPanel = WireDermaExts.ModelSelect(panel, "wire_gpu_model", list.Get("WireScreenModels"), 3) - modelPanel:SetModelList(list.Get("Wire_gate_Models"),"wire_gpu_model") - panel:AddControl("Label", {Text = ""}) - - - ---------------------------------------------------------------------------- - panel:AddControl("ComboBox", { - Label = "Memory model", - Options = { - ["128K"] = {wire_gpu_memorymodel = "128k"}, - ["128K chip"] = {wire_gpu_memorymodel = "128kc"}, - ["256K"] = {wire_gpu_memorymodel = "256k"}, - ["256K chip"] = {wire_gpu_memorymodel = "256kc"}, - ["512K"] = {wire_gpu_memorymodel = "512k"}, - ["512K chip"] = {wire_gpu_memorymodel = "512kc"}, - ["1024K"] = {wire_gpu_memorymodel = "1024k"}, - ["1024K chip"] = {wire_gpu_memorymodel = "1024kc"}, - ["2048K"] = {wire_gpu_memorymodel = "2048k"}, - ["2048K chip"] = {wire_gpu_memorymodel = "2048kc"}, - - ["64K (compatibility mode)"] = {wire_gpu_memorymodel = "64k"}, - ["64K chip"] = {wire_gpu_memorymodel = "64kc"}, - } - }) - panel:AddControl("Label", {Text = "Memory model selects GPU memory size and its operation mode"}) - - local enabledExtensionOrder = {} - local enabledExtensionLookup = {} - local extensionConvar = GetConVar("wire_gpu_extensions") - for ext in string.gmatch(extensionConvar:GetString() or "","([^;]*);") do - if CPULib.Extensions["GPU"] and CPULib.Extensions["GPU"][ext] then - enabledExtensionLookup[ext] = true - table.insert(enabledExtensionOrder,ext) - end - end - - local ExtensionPanel = vgui.Create("DListView") - local DisabledExtensionPanel = vgui.Create("DListView") - ExtensionPanel:AddColumn("Enabled Extensions") - DisabledExtensionPanel:AddColumn("Disabled Extensions") - ExtensionPanel:SetSize(235,200) - DisabledExtensionPanel:SetSize(235,200) - if CPULib.Extensions["GPU"] then - for k,_ in pairs(CPULib.Extensions["GPU"]) do - if enabledExtensionLookup[k] then - ExtensionPanel:AddLine(k) - else - DisabledExtensionPanel:AddLine(k) - end - end - end - - local function ReloadExtensions() - local extensions = {} - for _,line in pairs(ExtensionPanel:GetLines()) do - table.insert(extensions,line:GetValue(1)) - end - extensionConvar:SetString(CPULib:ToExtensionString(extensions)) - CPULib:LoadExtensionOrder(extensions,"GPU") - end - - function ExtensionPanel:OnRowSelected(rIndex,row) - DisabledExtensionPanel:AddLine(row:GetValue(1)) - self:RemoveLine(rIndex) - ReloadExtensions() - end - - function DisabledExtensionPanel:OnRowSelected(rIndex,row) - ExtensionPanel:AddLine(row:GetValue(1)) - self:RemoveLine(rIndex) - ReloadExtensions() - end - - panel:AddItem(ExtensionPanel) - panel:AddItem(DisabledExtensionPanel) - -- Reload the extensions at least once to make sure users don't have to touch the list - -- in order to use extensions on first opening of the tool menu - ReloadExtensions() - - end - - ------------------------------------------------------------------------------ - -- Tool screen - ------------------------------------------------------------------------------ - function TOOL:DrawToolScreen(width, height) - CPULib.RenderCPUTool(1,"ZGPU") - end + ------------------------------------------------------------------------------ + -- Compiler callbacks on the compiling state + ------------------------------------------------------------------------------ + local function compile_success() + CPULib.Upload() + end + + local function compile_error(errorText) + print(errorText) + GAMEMODE:AddNotify(errorText,NOTIFY_GENERIC,7) + end + + + ------------------------------------------------------------------------------ + -- Request code to be compiled (called remotely from server) + ------------------------------------------------------------------------------ + function ZGPU_RequestCode() + if ZGPU_Editor then + CPULib.Debugger.SourceTab = ZGPU_Editor:GetActiveTab() + CPULib.Compile(ZGPU_Editor:GetCode(),ZGPU_Editor:GetChosenFile(),compile_success,compile_error,"GPU",ZGPU_Editor.Location) + end + end + net.Receive("ZGPU_RequestCode", ZGPU_RequestCode) + + ------------------------------------------------------------------------------ + -- Open ZGPU editor + ------------------------------------------------------------------------------ + function ZGPU_OpenEditor() + if not ZGPU_Editor then + ZGPU_Editor = vgui.Create("Expression2EditorFrame") + CPULib.SetupEditor(ZGPU_Editor,"ZGPU Editor", "gpuchip", "GPU") + end + ZGPU_Editor:Open() + end + net.Receive("ZGPU_OpenEditor", ZGPU_OpenEditor) + + ------------------------------------------------------------------------------ + -- Build tool control panel + ------------------------------------------------------------------------------ + function TOOL.BuildCPanel(panel) + local Button = vgui.Create("DButton" , panel) + panel:AddPanel(Button) + Button:SetText("Online ZGPU documentation") + Button.DoClick = function(button) CPULib.ShowDocumentation("ZGPU") end + + + ---------------------------------------------------------------------------- + local currentDirectory + local FileBrowser = vgui.Create("wire_expression2_browser" , panel) + panel:AddPanel(FileBrowser) + FileBrowser:Setup("GPUChip") + FileBrowser:SetSize(235,400) + function FileBrowser:OnFileOpen(filepath, newtab) + if not ZGPU_Editor then + ZGPU_Editor = vgui.Create("Expression2EditorFrame") + CPULib.SetupEditor(ZGPU_Editor,"ZGPU Editor", "gpuchip", "GPU") + end + ZGPU_Editor:Open(filepath, nil, newtab) + end + + + ---------------------------------------------------------------------------- + local New = vgui.Create("DButton" , panel) + panel:AddPanel(New) + New:SetText("New file") + New.DoClick = function(button) + ZGPU_OpenEditor() + ZGPU_Editor:AutoSave() + ZGPU_Editor:NewScript(false) + end + panel:AddControl("Label", {Text = ""}) + + ---------------------------------------------------------------------------- + local OpenEditor = vgui.Create("DButton", panel) + panel:AddPanel(OpenEditor) + OpenEditor:SetText("Open Editor") + OpenEditor.DoClick = ZGPU_OpenEditor + + + ---------------------------------------------------------------------------- + local modelPanel = WireDermaExts.ModelSelect(panel, "wire_gpu_model", list.Get("WireScreenModels"), 3) + modelPanel:SetModelList(list.Get("Wire_gate_Models"),"wire_gpu_model") + panel:AddControl("Label", {Text = ""}) + + + ---------------------------------------------------------------------------- + panel:AddControl("ComboBox", { + Label = "Memory model", + Options = { + ["128K"] = {wire_gpu_memorymodel = "128k"}, + ["128K chip"] = {wire_gpu_memorymodel = "128kc"}, + ["256K"] = {wire_gpu_memorymodel = "256k"}, + ["256K chip"] = {wire_gpu_memorymodel = "256kc"}, + ["512K"] = {wire_gpu_memorymodel = "512k"}, + ["512K chip"] = {wire_gpu_memorymodel = "512kc"}, + ["1024K"] = {wire_gpu_memorymodel = "1024k"}, + ["1024K chip"] = {wire_gpu_memorymodel = "1024kc"}, + ["2048K"] = {wire_gpu_memorymodel = "2048k"}, + ["2048K chip"] = {wire_gpu_memorymodel = "2048kc"}, + + ["64K (compatibility mode)"] = {wire_gpu_memorymodel = "64k"}, + ["64K chip"] = {wire_gpu_memorymodel = "64kc"}, + } + }) + panel:AddControl("Label", {Text = "Memory model selects GPU memory size and its operation mode"}) + + local enabledExtensionOrder = {} + local enabledExtensionLookup = {} + local extensionConvar = GetConVar("wire_gpu_extensions") + for ext in string.gmatch(extensionConvar:GetString() or "","([^;]*);") do + if CPULib.Extensions["GPU"] and CPULib.Extensions["GPU"][ext] then + enabledExtensionLookup[ext] = true + table.insert(enabledExtensionOrder,ext) + end + end + + local ExtensionPanel = vgui.Create("DListView") + local DisabledExtensionPanel = vgui.Create("DListView") + ExtensionPanel:AddColumn("Enabled Extensions") + DisabledExtensionPanel:AddColumn("Disabled Extensions") + ExtensionPanel:SetSize(235,200) + DisabledExtensionPanel:SetSize(235,200) + if CPULib.Extensions["GPU"] then + for k,_ in pairs(CPULib.Extensions["GPU"]) do + if enabledExtensionLookup[k] then + ExtensionPanel:AddLine(k) + else + DisabledExtensionPanel:AddLine(k) + end + end + end + + local function ReloadExtensions() + local extensions = {} + for _,line in pairs(ExtensionPanel:GetLines()) do + table.insert(extensions,line:GetValue(1)) + end + extensionConvar:SetString(CPULib:ToExtensionString(extensions)) + CPULib:LoadExtensionOrder(extensions,"GPU") + end + + function ExtensionPanel:OnRowSelected(rIndex,row) + DisabledExtensionPanel:AddLine(row:GetValue(1)) + self:RemoveLine(rIndex) + ReloadExtensions() + end + + function DisabledExtensionPanel:OnRowSelected(rIndex,row) + ExtensionPanel:AddLine(row:GetValue(1)) + self:RemoveLine(rIndex) + ReloadExtensions() + end + + panel:AddItem(ExtensionPanel) + panel:AddItem(DisabledExtensionPanel) + -- Reload the extensions at least once to make sure users don't have to touch the list + -- in order to use extensions on first opening of the tool menu + ReloadExtensions() + + end + + ------------------------------------------------------------------------------ + -- Tool screen + ------------------------------------------------------------------------------ + function TOOL:DrawToolScreen(width, height) + CPULib.RenderCPUTool(1,"ZGPU") + end end diff --git a/lua/wire/stools/spu.lua b/lua/wire/stools/spu.lua index d9f344867b..4f26f6e44c 100644 --- a/lua/wire/stools/spu.lua +++ b/lua/wire/stools/spu.lua @@ -2,225 +2,225 @@ WireToolSetup.setCategory( "Chips, Gates", "Other/Sound", "Advanced" ) WireToolSetup.open( "spu", "SPU", "gmod_wire_spu", nil, "SPUs" ) if CLIENT then - language.Add("Tool.wire_spu.name", "SPU Tool (Wire)") - language.Add("Tool.wire_spu.desc", "Spawns a sound processing unit") - language.Add("ToolWirespu_Model", "Model:" ) - TOOL.Information = { - { name = "left", text = "Create/reflash " .. TOOL.Name }, - { name = "right", text = "Open editor" }, - } - - WireToolSetup.setToolMenuIcon("icon16/server_lightning.png") + language.Add("Tool.wire_spu.name", "SPU Tool (Wire)") + language.Add("Tool.wire_spu.desc", "Spawns a sound processing unit") + language.Add("ToolWirespu_Model", "Model:" ) + TOOL.Information = { + { name = "left", text = "Create/reflash " .. TOOL.Name }, + { name = "right", text = "Open editor" }, + } + + WireToolSetup.setToolMenuIcon("icon16/server_lightning.png") end WireToolSetup.BaseLang() WireToolSetup.SetupMax( 7 ) TOOL.ClientConVar = { - model = "models/cheeze/wires/cpu.mdl", - filename = "", - extensions = "" + model = "models/cheeze/wires/cpu.mdl", + filename = "", + extensions = "" } if CLIENT then - ------------------------------------------------------------------------------ - -- Make sure firing animation is displayed clientside - ------------------------------------------------------------------------------ - function TOOL:LeftClick() return true end - function TOOL:Reload() return true end - function TOOL:RightClick() return false end + ------------------------------------------------------------------------------ + -- Make sure firing animation is displayed clientside + ------------------------------------------------------------------------------ + function TOOL:LeftClick() return true end + function TOOL:Reload() return true end + function TOOL:RightClick() return false end end if SERVER then - util.AddNetworkString("ZSPU_RequestCode") - util.AddNetworkString("ZSPU_OpenEditor") - ------------------------------------------------------------------------------ - -- Reload: wipe ROM/RAM and reset memory model - ------------------------------------------------------------------------------ - function TOOL:Reload(trace) - if trace.Entity:IsPlayer() then return false end - - local player = self:GetOwner() - if (trace.Entity:IsValid()) and - (trace.Entity:GetClass() == "gmod_wire_spu") then - trace.Entity:SetMemoryModel(self:GetClientInfo("memorymodel")) - return true - end - end - - -- Left click: spawn SPU or upload current program into it - function TOOL:CheckHitOwnClass(trace) - return trace.Entity:IsValid() and (trace.Entity:GetClass() == self.WireClass or trace.Entity.WriteCell) - end - function TOOL:LeftClick_Update(trace) - CPULib.SetUploadTarget(trace.Entity, self:GetOwner()) - net.Start("ZSPU_RequestCode") net.Send(self:GetOwner()) - end - function TOOL:MakeEnt(ply, model, Ang, trace) - local ent = WireLib.MakeWireEnt(ply, {Class = self.WireClass, Pos=trace.HitPos, Angle=Ang, Model=model}) - ent:SetMemoryModel(self:GetClientInfo("memorymodel")) - ent:SetExtensionLoadOrder(self:GetClientInfo("extensions")) - self:LeftClick_Update(trace) - return ent - end - - - function TOOL:RightClick(trace) - net.Start("ZSPU_OpenEditor") net.Send(self:GetOwner()) - return true - end + util.AddNetworkString("ZSPU_RequestCode") + util.AddNetworkString("ZSPU_OpenEditor") + ------------------------------------------------------------------------------ + -- Reload: wipe ROM/RAM and reset memory model + ------------------------------------------------------------------------------ + function TOOL:Reload(trace) + if trace.Entity:IsPlayer() then return false end + + local player = self:GetOwner() + if (trace.Entity:IsValid()) and + (trace.Entity:GetClass() == "gmod_wire_spu") then + trace.Entity:SetMemoryModel(self:GetClientInfo("memorymodel")) + return true + end + end + + -- Left click: spawn SPU or upload current program into it + function TOOL:CheckHitOwnClass(trace) + return trace.Entity:IsValid() and (trace.Entity:GetClass() == self.WireClass or trace.Entity.WriteCell) + end + function TOOL:LeftClick_Update(trace) + CPULib.SetUploadTarget(trace.Entity, self:GetOwner()) + net.Start("ZSPU_RequestCode") net.Send(self:GetOwner()) + end + function TOOL:MakeEnt(ply, model, Ang, trace) + local ent = WireLib.MakeWireEnt(ply, {Class = self.WireClass, Pos=trace.HitPos, Angle=Ang, Model=model}) + ent:SetMemoryModel(self:GetClientInfo("memorymodel")) + ent:SetExtensionLoadOrder(self:GetClientInfo("extensions")) + self:LeftClick_Update(trace) + return ent + end + + + function TOOL:RightClick(trace) + net.Start("ZSPU_OpenEditor") net.Send(self:GetOwner()) + return true + end end if CLIENT then - ------------------------------------------------------------------------------ - -- Compiler callbacks on the compiling state - ------------------------------------------------------------------------------ - local function compile_success() - CPULib.Upload() - end - - local function compile_error(errorText) - GAMEMODE:AddNotify(errorText,NOTIFY_GENERIC,7) - end - - - ------------------------------------------------------------------------------ - -- Request code to be compiled (called remotely from server) - ------------------------------------------------------------------------------ - function ZSPU_RequestCode() - if ZSPU_Editor then - CPULib.Debugger.SourceTab = ZSPU_Editor:GetActiveTab() - CPULib.Compile(ZSPU_Editor:GetCode(),ZSPU_Editor:GetChosenFile(),compile_success,compile_error,"SPU",ZSPU_Editor.Location) - end - end - net.Receive("ZSPU_RequestCode", ZSPU_RequestCode) - - ------------------------------------------------------------------------------ - -- Open ZSPU editor - ------------------------------------------------------------------------------ - function ZSPU_OpenEditor() - if not ZSPU_Editor then - ZSPU_Editor = vgui.Create("Expression2EditorFrame") - CPULib.SetupEditor(ZSPU_Editor,"ZSPU Editor", "spuchip", "SPU") - end - ZSPU_Editor:Open() - end - net.Receive("ZSPU_OpenEditor", ZSPU_OpenEditor) - - ------------------------------------------------------------------------------ - -- Build tool control panel - ------------------------------------------------------------------------------ - function TOOL.BuildCPanel(panel) - local Button = vgui.Create("DButton" , panel) - panel:AddPanel(Button) - Button:SetText("Online ZSPU documentation") - Button.DoClick = function(button) CPULib.ShowDocumentation("ZSPU") end - - local Button = vgui.Create("DButton" , panel) - panel:AddPanel(Button) - Button:SetText("Open Sound Browser") - Button.DoClick = function() - RunConsoleCommand("wire_sound_browser_open") - end - - - ---------------------------------------------------------------------------- - local currentDirectory - local FileBrowser = vgui.Create("wire_expression2_browser" , panel) - panel:AddPanel(FileBrowser) - FileBrowser:Setup("spuchip") - FileBrowser:SetSize(235,400) + ------------------------------------------------------------------------------ + -- Compiler callbacks on the compiling state + ------------------------------------------------------------------------------ + local function compile_success() + CPULib.Upload() + end + + local function compile_error(errorText) + GAMEMODE:AddNotify(errorText,NOTIFY_GENERIC,7) + end + + + ------------------------------------------------------------------------------ + -- Request code to be compiled (called remotely from server) + ------------------------------------------------------------------------------ + function ZSPU_RequestCode() + if ZSPU_Editor then + CPULib.Debugger.SourceTab = ZSPU_Editor:GetActiveTab() + CPULib.Compile(ZSPU_Editor:GetCode(),ZSPU_Editor:GetChosenFile(),compile_success,compile_error,"SPU",ZSPU_Editor.Location) + end + end + net.Receive("ZSPU_RequestCode", ZSPU_RequestCode) + + ------------------------------------------------------------------------------ + -- Open ZSPU editor + ------------------------------------------------------------------------------ + function ZSPU_OpenEditor() + if not ZSPU_Editor then + ZSPU_Editor = vgui.Create("Expression2EditorFrame") + CPULib.SetupEditor(ZSPU_Editor,"ZSPU Editor", "spuchip", "SPU") + end + ZSPU_Editor:Open() + end + net.Receive("ZSPU_OpenEditor", ZSPU_OpenEditor) + + ------------------------------------------------------------------------------ + -- Build tool control panel + ------------------------------------------------------------------------------ + function TOOL.BuildCPanel(panel) + local Button = vgui.Create("DButton" , panel) + panel:AddPanel(Button) + Button:SetText("Online ZSPU documentation") + Button.DoClick = function(button) CPULib.ShowDocumentation("ZSPU") end + + local Button = vgui.Create("DButton" , panel) + panel:AddPanel(Button) + Button:SetText("Open Sound Browser") + Button.DoClick = function() + RunConsoleCommand("wire_sound_browser_open") + end + + + ---------------------------------------------------------------------------- + local currentDirectory + local FileBrowser = vgui.Create("wire_expression2_browser" , panel) + panel:AddPanel(FileBrowser) + FileBrowser:Setup("spuchip") + FileBrowser:SetSize(235,400) function FileBrowser:OnFileOpen(filepath, newtab) - if not ZSPU_Editor then - ZSPU_Editor = vgui.Create("Expression2EditorFrame") - CPULib.SetupEditor(ZSPU_Editor,"ZSPU Editor", "spuchip", "SPU") - end - ZSPU_Editor:Open(filepath, nil, newtab) - end - - - ---------------------------------------------------------------------------- - local New = vgui.Create("DButton" , panel) - panel:AddPanel(New) - New:SetText("New file") - New.DoClick = function(button) - ZSPU_OpenEditor() - ZSPU_Editor:AutoSave() - ZSPU_Editor:NewScript(false) - end - panel:AddControl("Label", {Text = ""}) - - ---------------------------------------------------------------------------- - local OpenEditor = vgui.Create("DButton", panel) - panel:AddPanel(OpenEditor) - OpenEditor:SetText("Open Editor") - OpenEditor.DoClick = ZSPU_OpenEditor - - - ---------------------------------------------------------------------------- - WireDermaExts.ModelSelect(panel, "wire_spu_model", list.Get("Wire_gate_Models"), 2) - panel:AddControl("Label", {Text = ""}) - - local enabledExtensionOrder = {} - local enabledExtensionLookup = {} - local extensionConvar = GetConVar("wire_spu_extensions") - for ext in string.gmatch(extensionConvar:GetString() or "","([^;]*);") do - if CPULib.Extensions["SPU"] and CPULib.Extensions["SPU"][ext] then - enabledExtensionLookup[ext] = true - table.insert(enabledExtensionOrder,ext) - end - end - - local ExtensionPanel = vgui.Create("DListView") - local DisabledExtensionPanel = vgui.Create("DListView") - ExtensionPanel:AddColumn("Enabled Extensions") - DisabledExtensionPanel:AddColumn("Disabled Extensions") - ExtensionPanel:SetSize(235,200) - DisabledExtensionPanel:SetSize(235,200) - if CPULib.Extensions["SPU"] then - for k,_ in pairs(CPULib.Extensions["SPU"]) do - if enabledExtensionLookup[k] then - ExtensionPanel:AddLine(k) - else - DisabledExtensionPanel:AddLine(k) - end - end - end - - local function ReloadExtensions() - local extensions = {} - for _,line in pairs(ExtensionPanel:GetLines()) do - table.insert(extensions,line:GetValue(1)) - end - extensionConvar:SetString(CPULib:ToExtensionString(extensions)) - CPULib:LoadExtensionOrder(extensions,"SPU") - end - - function ExtensionPanel:OnRowSelected(rIndex,row) - DisabledExtensionPanel:AddLine(row:GetValue(1)) - self:RemoveLine(rIndex) - ReloadExtensions() - end - - function DisabledExtensionPanel:OnRowSelected(rIndex,row) - ExtensionPanel:AddLine(row:GetValue(1)) - self:RemoveLine(rIndex) - ReloadExtensions() - end - - panel:AddItem(ExtensionPanel) - panel:AddItem(DisabledExtensionPanel) - -- Reload the extensions at least once to make sure users don't have to touch the list - -- in order to use extensions on first opening of the tool menu - ReloadExtensions() - - end - - ------------------------------------------------------------------------------ - -- Tool screen - ------------------------------------------------------------------------------ - function TOOL:DrawToolScreen(width, height) - CPULib.RenderCPUTool(1,"ZSPU") - end + if not ZSPU_Editor then + ZSPU_Editor = vgui.Create("Expression2EditorFrame") + CPULib.SetupEditor(ZSPU_Editor,"ZSPU Editor", "spuchip", "SPU") + end + ZSPU_Editor:Open(filepath, nil, newtab) + end + + + ---------------------------------------------------------------------------- + local New = vgui.Create("DButton" , panel) + panel:AddPanel(New) + New:SetText("New file") + New.DoClick = function(button) + ZSPU_OpenEditor() + ZSPU_Editor:AutoSave() + ZSPU_Editor:NewScript(false) + end + panel:AddControl("Label", {Text = ""}) + + ---------------------------------------------------------------------------- + local OpenEditor = vgui.Create("DButton", panel) + panel:AddPanel(OpenEditor) + OpenEditor:SetText("Open Editor") + OpenEditor.DoClick = ZSPU_OpenEditor + + + ---------------------------------------------------------------------------- + WireDermaExts.ModelSelect(panel, "wire_spu_model", list.Get("Wire_gate_Models"), 2) + panel:AddControl("Label", {Text = ""}) + + local enabledExtensionOrder = {} + local enabledExtensionLookup = {} + local extensionConvar = GetConVar("wire_spu_extensions") + for ext in string.gmatch(extensionConvar:GetString() or "","([^;]*);") do + if CPULib.Extensions["SPU"] and CPULib.Extensions["SPU"][ext] then + enabledExtensionLookup[ext] = true + table.insert(enabledExtensionOrder,ext) + end + end + + local ExtensionPanel = vgui.Create("DListView") + local DisabledExtensionPanel = vgui.Create("DListView") + ExtensionPanel:AddColumn("Enabled Extensions") + DisabledExtensionPanel:AddColumn("Disabled Extensions") + ExtensionPanel:SetSize(235,200) + DisabledExtensionPanel:SetSize(235,200) + if CPULib.Extensions["SPU"] then + for k,_ in pairs(CPULib.Extensions["SPU"]) do + if enabledExtensionLookup[k] then + ExtensionPanel:AddLine(k) + else + DisabledExtensionPanel:AddLine(k) + end + end + end + + local function ReloadExtensions() + local extensions = {} + for _,line in pairs(ExtensionPanel:GetLines()) do + table.insert(extensions,line:GetValue(1)) + end + extensionConvar:SetString(CPULib:ToExtensionString(extensions)) + CPULib:LoadExtensionOrder(extensions,"SPU") + end + + function ExtensionPanel:OnRowSelected(rIndex,row) + DisabledExtensionPanel:AddLine(row:GetValue(1)) + self:RemoveLine(rIndex) + ReloadExtensions() + end + + function DisabledExtensionPanel:OnRowSelected(rIndex,row) + ExtensionPanel:AddLine(row:GetValue(1)) + self:RemoveLine(rIndex) + ReloadExtensions() + end + + panel:AddItem(ExtensionPanel) + panel:AddItem(DisabledExtensionPanel) + -- Reload the extensions at least once to make sure users don't have to touch the list + -- in order to use extensions on first opening of the tool menu + ReloadExtensions() + + end + + ------------------------------------------------------------------------------ + -- Tool screen + ------------------------------------------------------------------------------ + function TOOL:DrawToolScreen(width, height) + CPULib.RenderCPUTool(1,"ZSPU") + end end diff --git a/lua/wire/zvm/tests/benchmarks/benchmark_page_bios.lua b/lua/wire/zvm/tests/benchmarks/benchmark_page_bios.lua index 14abedca41..19b1832d9b 100644 --- a/lua/wire/zvm/tests/benchmarks/benchmark_page_bios.lua +++ b/lua/wire/zvm/tests/benchmarks/benchmark_page_bios.lua @@ -191,7 +191,7 @@ pmem_find_requester_stackless: pmem_find_requester_loop_stackless: INC ESI CPUSET 25,[ESI] - DEC ESI + DEC ESI JE pmem_stackless_skip_ptable JNZ page_in_ptable_stackless // if allocated, check it out pmem_find_requester_stackless_page_in_ptable_return: @@ -479,7 +479,7 @@ DB 0,0 // 128-255 2 DB 0,0 // 256-383 3 DB 0,0 // 384-511 4 DB 0,0 // 512-639 5 -DB 0,0 // 640-767 6 +DB 0,0 // 640-767 6 DB 0,0 // 768-895 7 DB 0,0 // 896-1023 8 DB 0,0 // 1024-1151 9 diff --git a/lua/wire/zvm/tests/benchmarks/benchmark_zasm_assembler.lua b/lua/wire/zvm/tests/benchmarks/benchmark_zasm_assembler.lua index 05c127f07e..bf84bfca26 100644 --- a/lua/wire/zvm/tests/benchmarks/benchmark_zasm_assembler.lua +++ b/lua/wire/zvm/tests/benchmarks/benchmark_zasm_assembler.lua @@ -22,7 +22,7 @@ void main() { assemble(textToAssemble, ASMOutput) timer end port0 = end - start -} +} void assemble(char asm, float output) { char word = asm @@ -157,7 +157,7 @@ float isRegister(char* x) { k += registers[k + 1] + 3 i++ } - + if(i == 20) { return 0 } else { @@ -194,7 +194,7 @@ float isInstruction(char* x) { k += lookup[k + 1] + 4 i++ } - + if(i == maxItems) { return 0 } else { @@ -225,7 +225,7 @@ DB 1002, 5, "PORT2",0 DB 1003, 5, "PORT3",0 optable: -DB +DB inst_lookup_A, inst_lookup_B, inst_lookup_C, diff --git a/lua/wire/zvm/tests/ifdefs.lua b/lua/wire/zvm/tests/ifdefs.lua index 5095d006dd..418e8dccd5 100644 --- a/lua/wire/zvm/tests/ifdefs.lua +++ b/lua/wire/zvm/tests/ifdefs.lua @@ -7,16 +7,16 @@ Test.Files = { #ifdef x #define z #pragma cpuname Test X - ALLOC 1 + ALLOC 1 #ifdef y #ifdef x #pragma cpuname Test X and Y #endif - ALLOC 2 + ALLOC 2 #endif //not x -#else - ALLOC 4 +#else + ALLOC 4 #endif #ifdef y @@ -27,7 +27,7 @@ ALLOC 8 #endif /* #ifdef y - ALLOC 8 + ALLOC 8 #endif */ //above comment intentional for making sure ifdef handler doesn't skip into the middle of a comment diff --git a/lua/wire/zvm/tests/virtualiobus.lua b/lua/wire/zvm/tests/virtualiobus.lua index 97b3834428..21ad8e4bb2 100644 --- a/lua/wire/zvm/tests/virtualiobus.lua +++ b/lua/wire/zvm/tests/virtualiobus.lua @@ -4,7 +4,7 @@ function Test.Run(CPU,TestSuite) TestSuite:Deploy(CPU, "MOV PORT0,1 MOV R0,PORT0", Test.CompileError) local IOBus = TestSuite.CreateVirtualIOBus(4) -- get external IO device of size 4 TestSuite.Initialize(CPU, nil, IOBus) -- reinitialize the CPU with the IOBus - + IOBus.InPorts[0] = 24 CPU.Clk = 1 for i = 0, 16 do diff --git a/lua/wire/zvm/zvm_core.lua b/lua/wire/zvm/zvm_core.lua index 0bbdca01ae..0f72f08e4f 100644 --- a/lua/wire/zvm/zvm_core.lua +++ b/lua/wire/zvm/zvm_core.lua @@ -6,7 +6,7 @@ ZVM = {} -- TODO: Remove microcode debugging, it's not possible to access normally. if not SERVER and not CLIENT then - ZVM.MicrocodeDebug = true + ZVM.MicrocodeDebug = true end @@ -25,26 +25,26 @@ include("wire/zvm/zvm_data.lua") -------------------------------------------------------------------------------- -- Emit "microcode" to the output stream if ZVM.MicrocodeDebug then -- Debug microcode generator - local pad = 0 - function ZVM:Emit(text) - -- TODO: Remove microcode debugging, it's not possible to access normally. - if string.find(text,"end") and (not string.find(text,"if")) - then pad = pad - 1 end - - if string.find(text,"elseif") or string.find(text,"else") - then self.EmitBlock = self.EmitBlock..string.rep(" ",pad-1)..text.."\n" - else self.EmitBlock = self.EmitBlock..string.rep(" ",pad)..text.."\n" - end - - if (string.find(text,"if") or string.find(text,"for")) - and (not string.find(text,"elseif")) - and (not string.find(text,"end")) - then pad = pad + 1 end - end + local pad = 0 + function ZVM:Emit(text) + -- TODO: Remove microcode debugging, it's not possible to access normally. + if string.find(text,"end") and (not string.find(text,"if")) + then pad = pad - 1 end + + if string.find(text,"elseif") or string.find(text,"else") + then self.EmitBlock = self.EmitBlock..string.rep(" ",pad-1)..text.."\n" + else self.EmitBlock = self.EmitBlock..string.rep(" ",pad)..text.."\n" + end + + if (string.find(text,"if") or string.find(text,"for")) + and (not string.find(text,"elseif")) + and (not string.find(text,"end")) + then pad = pad + 1 end + end else - function ZVM:Emit(...) - self.EmitBlock = self.EmitBlock..string.format(...).."\n" - end + function ZVM:Emit(...) + self.EmitBlock = self.EmitBlock..string.format(...).."\n" + end end @@ -53,26 +53,26 @@ end -------------------------------------------------------------------------------- -- Start new dynamic precompile block function ZVM:Dyn_StartBlock() - self.EmitBlock = "" - self.EmitRegisterChanged = {} - self.EmitOperand = { "0", "0" } - self.EmitExpression = {} + self.EmitBlock = "" + self.EmitRegisterChanged = {} + self.EmitOperand = { "0", "0" } + self.EmitExpression = {} - -- This instruction requires an interrupt check after being used - self.EmitNeedInterruptCheck = false - -- Operand RM function to be used for the operand - self.EmitOperandRM = {} - -- Operand byte to be used (replaces $BYTE) - self.EmitOperandByte = {} - -- Operand segment prefix to be used (replaces $SEG) - self.EmitOperandSegment = {} + -- This instruction requires an interrupt check after being used + self.EmitNeedInterruptCheck = false + -- Operand RM function to be used for the operand + self.EmitOperandRM = {} + -- Operand byte to be used (replaces $BYTE) + self.EmitOperandByte = {} + -- Operand segment prefix to be used (replaces $SEG) + self.EmitOperandSegment = {} - -- Mark local registers - self:Emit("local EAX,EBX,ECX,EDX,ESI,EDI,ESP,EBP,OP1,OP2") - self:Emit("local R0, R1, R2, R3, R4, R5, R6, R7") - self:Emit("local R8, R9,R10,R11,R12,R13,R14,R15") - self:Emit("local R16,R17,R18,R19,R20,R21,R22,R23") - self:Emit("local R24,R25,R26,R27,R28,R29,R30,R31") + -- Mark local registers + self:Emit("local EAX,EBX,ECX,EDX,ESI,EDI,ESP,EBP,OP1,OP2") + self:Emit("local R0, R1, R2, R3, R4, R5, R6, R7") + self:Emit("local R8, R9,R10,R11,R12,R13,R14,R15") + self:Emit("local R16,R17,R18,R19,R20,R21,R22,R23") + self:Emit("local R24,R25,R26,R27,R28,R29,R30,R31") end @@ -81,28 +81,28 @@ end -------------------------------------------------------------------------------- -- Load/fetch operand (by RM) function ZVM:Dyn_LoadOperand(OP,RM) - if self.OperandReadFunctions[RM] then - local preEmit - if self.ReadInvolvedRegisterLookup[RM] and - self.EmitRegisterChanged[self.ReadInvolvedRegisterLookup[RM]] then - -- Available local value for this register - preEmit = self.OperandFastReadFunctions[RM] - else - preEmit = self.OperandReadFunctions[RM] - end + if self.OperandReadFunctions[RM] then + local preEmit + if self.ReadInvolvedRegisterLookup[RM] and + self.EmitRegisterChanged[self.ReadInvolvedRegisterLookup[RM]] then + -- Available local value for this register + preEmit = self.OperandFastReadFunctions[RM] + else + preEmit = self.OperandReadFunctions[RM] + end - -- Make sure segment register is global - self:Dyn_EmitForceRegisterGlobal(self.EmitOperandSegment[OP]) + -- Make sure segment register is global + self:Dyn_EmitForceRegisterGlobal(self.EmitOperandSegment[OP]) - -- Generate operand text - preEmit = string.gsub(preEmit,"$BYTE",self.EmitOperandByte[OP] or "0") - preEmit = string.gsub(preEmit,"$SEG","VM."..(self.EmitOperandSegment[OP] or "DS")) - self.EmitOperand[OP] = preEmit + -- Generate operand text + preEmit = string.gsub(preEmit,"$BYTE",self.EmitOperandByte[OP] or "0") + preEmit = string.gsub(preEmit,"$SEG","VM."..(self.EmitOperandSegment[OP] or "DS")) + self.EmitOperand[OP] = preEmit - if self.NeedInterruptCheck[RM] then self.EmitNeedInterruptCheck = true end - end + if self.NeedInterruptCheck[RM] then self.EmitNeedInterruptCheck = true end + end - self.EmitOperandRM[OP] = RM + self.EmitOperandRM[OP] = RM end @@ -111,34 +111,34 @@ end -------------------------------------------------------------------------------- -- Write operand (by RM) function ZVM:Dyn_WriteOperand(OP,RM) - if RM == 9 then -- Special case: attempting to write to CS - self:Dyn_EmitInterrupt("13","1") - return - end + if RM == 9 then -- Special case: attempting to write to CS + self:Dyn_EmitInterrupt("13","1") + return + end - if self.OperandWriteFunctions[RM] then - if self.EmitExpression[OP] then -- check if we need writeback - local preEmit - if self.WriteInvolvedRegisterLookup[RM] then - preEmit = self.OperandFastWriteFunctions[RM] - self.EmitRegisterChanged[self.WriteInvolvedRegisterLookup[RM]] - = self.InternalRegister[self.WriteInvolvedRegisterLookup[RM]] - else - if self.WriteRequiredRegisterLookup[RM] and - self.EmitRegisterChanged[self.WriteRequiredRegisterLookup[RM]] then - preEmit = self.OperandFastWriteFunctions[RM] - else - preEmit = self.OperandWriteFunctions[RM] - end - end + if self.OperandWriteFunctions[RM] then + if self.EmitExpression[OP] then -- check if we need writeback + local preEmit + if self.WriteInvolvedRegisterLookup[RM] then + preEmit = self.OperandFastWriteFunctions[RM] + self.EmitRegisterChanged[self.WriteInvolvedRegisterLookup[RM]] + = self.InternalRegister[self.WriteInvolvedRegisterLookup[RM]] + else + if self.WriteRequiredRegisterLookup[RM] and + self.EmitRegisterChanged[self.WriteRequiredRegisterLookup[RM]] then + preEmit = self.OperandFastWriteFunctions[RM] + else + preEmit = self.OperandWriteFunctions[RM] + end + end - preEmit = string.gsub(preEmit,"$EXPR",self.EmitExpression[OP]) - preEmit = string.gsub(preEmit,"$BYTE",self.EmitOperandByte[OP] or "0") - preEmit = string.gsub(preEmit,"$SEG","VM."..(self.EmitOperandSegment[OP] or "0")) + preEmit = string.gsub(preEmit,"$EXPR",self.EmitExpression[OP]) + preEmit = string.gsub(preEmit,"$BYTE",self.EmitOperandByte[OP] or "0") + preEmit = string.gsub(preEmit,"$SEG","VM."..(self.EmitOperandSegment[OP] or "0")) - self:Emit(preEmit) - end - end + self:Emit(preEmit) + end + end end @@ -147,9 +147,9 @@ end -------------------------------------------------------------------------------- -- Preprocess microcode text (for microcode syntax to work) function ZVM:Dyn_PreprocessEmit(text) - local preEmit = string.gsub( text,"$1",self.EmitOperand[1]) - preEmit = string.gsub(preEmit,"$2",self.EmitOperand[2]) - return string.gsub(preEmit,"$L","local") + local preEmit = string.gsub( text,"$1",self.EmitOperand[1]) + preEmit = string.gsub(preEmit,"$2",self.EmitOperand[2]) + return string.gsub(preEmit,"$L","local") end @@ -158,7 +158,7 @@ end -------------------------------------------------------------------------------- -- Emit preprocessed text function ZVM:Dyn_Emit(...) - self:Emit(self:Dyn_PreprocessEmit(string.format(...))) + self:Emit(self:Dyn_PreprocessEmit(string.format(...))) end @@ -167,15 +167,15 @@ end -------------------------------------------------------------------------------- -- Emit operand being set to specific expression function ZVM:Dyn_EmitOperand(OP,text,emitNow) - if not text then - self.EmitExpression[1] = self:Dyn_PreprocessEmit(OP) - else - self.EmitExpression[OP] = self:Dyn_PreprocessEmit(text) - if emitNow then - self:Emit("OP"..OP.." = "..self.EmitExpression[OP]) - self.EmitExpression[OP] = "OP"..OP - end - end + if not text then + self.EmitExpression[1] = self:Dyn_PreprocessEmit(OP) + else + self.EmitExpression[OP] = self:Dyn_PreprocessEmit(text) + if emitNow then + self:Emit("OP"..OP.." = "..self.EmitExpression[OP]) + self.EmitExpression[OP] = "OP"..OP + end + end end @@ -184,12 +184,12 @@ end -------------------------------------------------------------------------------- -- Force current state to be updated function ZVM:Dyn_EmitState(errorState) - -- Do we need to emit registers - for v,v in pairs(self.EmitRegisterChanged) do - --if (not errorState) or (not self.EmitRegisterChangedByOperand[k]) then - self:Emit("VM."..v.." = "..v) - --end - end + -- Do we need to emit registers + for v,v in pairs(self.EmitRegisterChanged) do + --if (not errorState) or (not self.EmitRegisterChangedByOperand[k]) then + self:Emit("VM."..v.." = "..v) + --end + end end @@ -198,14 +198,14 @@ end -------------------------------------------------------------------------------- -- Emit forced block return function ZVM:Dyn_EmitBreak(emitIP) - self:Emit("VM.TMR = VM.TMR + %d",self.PrecompileInstruction) - self:Emit("VM.CODEBYTES = VM.CODEBYTES + %d",self.PrecompileBytes) - if emitIP then - self:Emit("VM.IP = %d",(self.PrecompileIP or 0)) - self:Emit("VM.XEIP = %d",(self.PrecompileTrueXEIP or 0)) - end - if self.ExtraEmitFunction then self.ExtraEmitFunction(self) end - self:Emit("if true then return end") + self:Emit("VM.TMR = VM.TMR + %d",self.PrecompileInstruction) + self:Emit("VM.CODEBYTES = VM.CODEBYTES + %d",self.PrecompileBytes) + if emitIP then + self:Emit("VM.IP = %d",(self.PrecompileIP or 0)) + self:Emit("VM.XEIP = %d",(self.PrecompileTrueXEIP or 0)) + end + if self.ExtraEmitFunction then self.ExtraEmitFunction(self) end + self:Emit("if true then return end") end @@ -214,13 +214,13 @@ end -------------------------------------------------------------------------------- -- Make sure specific register value is really globally set function ZVM:Dyn_EmitForceRegisterGlobal(register) - for k,v in pairs(self.EmitRegisterChanged) do - if v == register then - self:Emit("VM."..v.." = "..v) - self.EmitRegisterChanged[k] = nil - return - end - end + for k,v in pairs(self.EmitRegisterChanged) do + if v == register then + self:Emit("VM."..v.." = "..v) + self.EmitRegisterChanged[k] = nil + return + end + end end @@ -229,10 +229,10 @@ end -------------------------------------------------------------------------------- -- Make sure specific register value is really locally set function ZVM:Dyn_EmitForceRegisterLocal(register) - if not self.EmitRegisterChanged[self.NeedRegisterLookup[register]] then - self:Emit(register.." = ".."VM."..register) - self.EmitRegisterChanged[self.NeedRegisterLookup[register]] = register - end + if not self.EmitRegisterChanged[self.NeedRegisterLookup[register]] then + self:Emit(register.." = ".."VM."..register) + self.EmitRegisterChanged[self.NeedRegisterLookup[register]] = register + end end @@ -241,11 +241,11 @@ end -------------------------------------------------------------------------------- -- Flag register as changed/altered function ZVM:Dyn_EmitRegisterValueChanged(register) - for k,v in pairs(self.InternalRegister) do - if string.upper(v) == register then - self.EmitRegisterChanged[k] = register - end - end + for k,v in pairs(self.InternalRegister) do + if string.upper(v) == register then + self.EmitRegisterChanged[k] = register + end + end end @@ -254,10 +254,10 @@ end -------------------------------------------------------------------------------- -- Emit specific opcode function ZVM:Dyn_EmitOpcode(opcode) - self.EmitExpression = {} - if self.OpcodeTable[opcode] then - self.OpcodeTable[opcode](self) - end + self.EmitExpression = {} + if self.OpcodeTable[opcode] then + self.OpcodeTable[opcode](self) + end end @@ -268,11 +268,11 @@ end -- intNo can only be numeric or "$1" -- intParam can only be numeric or "IDX" function ZVM:Dyn_EmitInterrupt(intNo,intParam) - 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:Interrupt(%s,%s)",intNo,intParam) - self:Dyn_EmitBreak() + 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:Interrupt(%s,%s)",intNo,intParam) + self:Dyn_EmitBreak() end @@ -281,34 +281,34 @@ end -------------------------------------------------------------------------------- -- Emit interrupt check function ZVM:Dyn_EmitInterruptCheck() - if self.RQCAP == 1 then - self:Emit("if VM.MEMRQ > 0 then") -- Extended memory request - self:Emit("if VM.MEMRQ == 1 then") -- Delayed request - self:Emit("VM.IP = %d",self.PrecompileStartIP) - self:Emit("VM.XEIP = %d",(self.PrecompileTrueXEIP or 0)) - self:Emit("VM.IDLE = 1") - self:Dyn_EmitState(true) - self:Dyn_EmitBreak() - self:Emit("elseif VM.MEMRQ == 2 then") -- Reading - self:Dyn_EmitState(true) - self:Emit("VM.MEMRQ = 4") - self:Emit("VM.IP = %d",self.PrecompileStartIP) - self:Emit("VM.XEIP = %d",(self.PrecompileTrueXEIP or 0)) - self:Emit("VM:Interrupt(28,VM.LADD)") - self:Dyn_EmitBreak() - self:Emit("elseif VM.MEMRQ == 3 then") -- Writing - self:Dyn_EmitState(true) - self:Emit("VM.MEMRQ = 5") - self:Emit("VM.IP = %d",self.PrecompileStartIP) - self:Emit("VM.XEIP = %d",(self.PrecompileTrueXEIP or 0)) - self:Emit("VM:Interrupt(29,VM.LADD)") - self:Dyn_EmitBreak() - self:Emit("end") - self:Emit("end") - end - self:Emit("if VM.INTR == 1 then") - self:Dyn_EmitBreak(false) - self:Emit("end") + if self.RQCAP == 1 then + self:Emit("if VM.MEMRQ > 0 then") -- Extended memory request + self:Emit("if VM.MEMRQ == 1 then") -- Delayed request + self:Emit("VM.IP = %d",self.PrecompileStartIP) + self:Emit("VM.XEIP = %d",(self.PrecompileTrueXEIP or 0)) + self:Emit("VM.IDLE = 1") + self:Dyn_EmitState(true) + self:Dyn_EmitBreak() + self:Emit("elseif VM.MEMRQ == 2 then") -- Reading + self:Dyn_EmitState(true) + self:Emit("VM.MEMRQ = 4") + self:Emit("VM.IP = %d",self.PrecompileStartIP) + self:Emit("VM.XEIP = %d",(self.PrecompileTrueXEIP or 0)) + self:Emit("VM:Interrupt(28,VM.LADD)") + self:Dyn_EmitBreak() + self:Emit("elseif VM.MEMRQ == 3 then") -- Writing + self:Dyn_EmitState(true) + self:Emit("VM.MEMRQ = 5") + self:Emit("VM.IP = %d",self.PrecompileStartIP) + self:Emit("VM.XEIP = %d",(self.PrecompileTrueXEIP or 0)) + self:Emit("VM:Interrupt(29,VM.LADD)") + self:Dyn_EmitBreak() + self:Emit("end") + self:Emit("end") + end + self:Emit("if VM.INTR == 1 then") + self:Dyn_EmitBreak(false) + self:Emit("end") end @@ -317,40 +317,40 @@ end -------------------------------------------------------------------------------- -- End precompile block function ZVM:Dyn_EndBlock() - if not self.PrecompileBreak then - self:Dyn_EmitState() - self:Dyn_EmitBreak(true) - end - -- TODO: Remove microcode debugging, it's not possible to access normally. - if self.MicrocodeDebug then - if Msg then - local str = self.EmitBlock - Msg("BLOCK: \n") - while str ~= "" do - Msg(string.sub(str,1,100)) - str = string.sub(str,101) - end - Msg("\n") - else - print(self.EmitBlock) - end - end - return self.EmitBlock + if not self.PrecompileBreak then + self:Dyn_EmitState() + self:Dyn_EmitBreak(true) + end + -- TODO: Remove microcode debugging, it's not possible to access normally. + if self.MicrocodeDebug then + if Msg then + local str = self.EmitBlock + Msg("BLOCK: \n") + while str ~= "" do + Msg(string.sub(str,1,100)) + str = string.sub(str,101) + end + Msg("\n") + else + print(self.EmitBlock) + end + end + return self.EmitBlock end -------------------------------------------------------------------------------- -- Begins a block of code that will only run if we're in a quota supporting environment -- and about to exit due to quota. function ZVM:Dyn_BeginQuotaOnlyCode() - self:Dyn_Emit("if VM.QuotaSupported then") - self:Dyn_Emit("if VM.TMR > VM.Quota then") + self:Dyn_Emit("if VM.QuotaSupported then") + self:Dyn_Emit("if VM.TMR > VM.Quota then") end -------------------------------------------------------------------------------- -- Ends a block of "Quota only" code. function ZVM:Dyn_EndQuotaOnlyCode() - self:Dyn_Emit("end") - self:Dyn_Emit("end") + self:Dyn_Emit("end") + self:Dyn_Emit("end") end -------------------------------------------------------------------------------- @@ -360,468 +360,468 @@ end -- Note that to take arguments, you will have to store your values somewhere in VM and -- clear them at the end of your function function ZVM:Dyn_StartQuotaInterrupt() - self:Dyn_Emit("if VM.QuotaSupported then") - self:Dyn_Emit("if VM.TMR > VM.Quota then") - self:Dyn_EmitState() - self:Dyn_Emit("VM.LASTQUO = VM.TIMER+%d*VM.TimerDT",(self.PrecompileInstruction or 0)) - self:Dyn_Emit("VM.QUOFLAG = 1") - self:Dyn_Emit("VM.IP = %d",self.PrecompileIP) - self:Dyn_Emit("VM.XEIP = %d",self.PrecompileXEIP) - self:Dyn_Emit("$L function quotafunc(VM)") + self:Dyn_Emit("if VM.QuotaSupported then") + self:Dyn_Emit("if VM.TMR > VM.Quota then") + self:Dyn_EmitState() + self:Dyn_Emit("VM.LASTQUO = VM.TIMER+%d*VM.TimerDT",(self.PrecompileInstruction or 0)) + self:Dyn_Emit("VM.QUOFLAG = 1") + self:Dyn_Emit("VM.IP = %d",self.PrecompileIP) + self:Dyn_Emit("VM.XEIP = %d",self.PrecompileXEIP) + self:Dyn_Emit("$L function quotafunc(VM)") end function ZVM:Dyn_EndQuotaInterrupt() - self:Dyn_EmitState() - self:Dyn_Emit("end") - self:Dyn_Emit("VM.QuotaOverrunFunc = quotafunc") - self:Dyn_Emit("VM.EndedOnQuota = true") - self:Dyn_EmitBreak() - self:Dyn_Emit("end") - self:Dyn_Emit("end") + self:Dyn_EmitState() + self:Dyn_Emit("end") + self:Dyn_Emit("VM.QuotaOverrunFunc = quotafunc") + self:Dyn_Emit("VM.EndedOnQuota = true") + self:Dyn_EmitBreak() + self:Dyn_Emit("end") + 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) + 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") + 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() + 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 - self.PrecompileIP = self.IP - self.PrecompileStartXEIP = self.XEIP - self.PrecompileBreak = false - self.PrecompileInstruction = 0 - self.PrecompileBytes = 0 - self.PrecompileCurInstructionSize = 0 + self.PrecompileXEIP = self.XEIP + self.PrecompileIP = self.IP + self.PrecompileStartXEIP = self.XEIP + self.PrecompileBreak = false + self.PrecompileInstruction = 0 + self.PrecompileBytes = 0 + self.PrecompileCurInstructionSize = 0 - self.PrecompilePreviousPage = math.floor(self.XEIP / 128) - self:Dyn_StartBlock() + self.PrecompilePreviousPage = math.floor(self.XEIP / 128) + self:Dyn_StartBlock() end function ZVM:Precompile_Finalize() - -- Emit finalizer - self:Dyn_EndBlock() - - local result,message = CompileString(self.EmitBlock,"ZVM:[".. tonumber(self.PrecompileStartXEIP) or 0 .."]") - if not result then - print("[ZVM ERROR]: "..(message or "unknown error")) - else - -- This should guarantee the physical addresses for things. - local page = self:GetPageByIndex(math.floor(self.PrecompileStartXEIP/128)) - local precompileStartPEIP = self.PrecompileStartXEIP - local precompilePEIP = self.PrecompileXEIP - if page.Remapped == 1 then - precompileStartPEIP = page.MappedIndex*128+self.PrecompileStartXEIP%128 - precompilePEIP = precompileStartPEIP+(self.PrecompileXEIP-self.PrecompileStartXEIP) - end - for address = precompileStartPEIP, precompilePEIP-1 do - if not self.IsAddressPrecompiled[address] then - self.IsAddressPrecompiled[address] = { } - end - table.insert(self.IsAddressPrecompiled[address],precompileStartPEIP) - end - setfenv(result,self.Env) - self.PrecompiledData[precompileStartPEIP] = result - end - - return result + -- Emit finalizer + self:Dyn_EndBlock() + + local result,message = CompileString(self.EmitBlock,"ZVM:[".. tonumber(self.PrecompileStartXEIP) or 0 .."]") + if not result then + print("[ZVM ERROR]: "..(message or "unknown error")) + else + -- This should guarantee the physical addresses for things. + local page = self:GetPageByIndex(math.floor(self.PrecompileStartXEIP/128)) + local precompileStartPEIP = self.PrecompileStartXEIP + local precompilePEIP = self.PrecompileXEIP + if page.Remapped == 1 then + precompileStartPEIP = page.MappedIndex*128+self.PrecompileStartXEIP%128 + precompilePEIP = precompileStartPEIP+(self.PrecompileXEIP-self.PrecompileStartXEIP) + end + for address = precompileStartPEIP, precompilePEIP-1 do + if not self.IsAddressPrecompiled[address] then + self.IsAddressPrecompiled[address] = { } + end + table.insert(self.IsAddressPrecompiled[address],precompileStartPEIP) + end + setfenv(result,self.Env) + self.PrecompiledData[precompileStartPEIP] = result + end + + return result end function ZVM:Precompile_Fetch() - local prevIF = self.IF - self.IF = 0 - local value = self:ReadCell(self.PrecompileXEIP) or 0 - self.IF = prevIF - self.PrecompileCurInstructionSize = self.PrecompileCurInstructionSize + 1 - self.PrecompileXEIP = self.PrecompileXEIP + 1 - self.PrecompileIP = self.PrecompileIP + 1 - self.PrecompileBytes = self.PrecompileBytes + 1 - return value or 0 + local prevIF = self.IF + self.IF = 0 + local value = self:ReadCell(self.PrecompileXEIP) or 0 + self.IF = prevIF + self.PrecompileCurInstructionSize = self.PrecompileCurInstructionSize + 1 + self.PrecompileXEIP = self.PrecompileXEIP + 1 + self.PrecompileIP = self.PrecompileIP + 1 + self.PrecompileBytes = self.PrecompileBytes + 1 + return value or 0 end function ZVM:Precompile_Peek() - local prevIF = self.IF - self.IF = 0 - self:ReadCell(self.PrecompileXEIP) - self.IF = prevIF + local prevIF = self.IF + self.IF = 0 + self:ReadCell(self.PrecompileXEIP) + self.IF = prevIF end function ZVM:Precompile_Step() - -- Set true XEIP register value for this step (this value will be used if XEIP is accessed) - self.PrecompileTrueXEIP = self.PrecompileXEIP - self.PrecompileStartIP = self.PrecompileIP - - -- Move on to the next instruction - self.PrecompileInstruction = self.PrecompileInstruction + 1 - - -- Reset requirement for an interrupt check, reset registers - self.EmitNeedInterruptCheck = false - --self.EmitRegisterChangedByOperand = {} - - -- Reset interrupts trigger if precompiling - self.INTR = 0 - - -- Allows an opcode to know how big the instruction is. - self.PrecompileCurInstructionSize = 0 - - -- Check if we crossed the page boundary, if so - repeat the check - if math.floor(self.PrecompileXEIP / 128) ~= self.PrecompilePreviousPage then - self:Emit("VM:SetCurrentPage(%d)",math.floor(self.PrecompileXEIP/128)) - self:Emit("if (VM.PCAP == 1) then") - self:Emit("if (VM.CurrentPage.Execute == 0) and (VM.PreviousPage.RunLevel ~= 0) then") - self:Dyn_EmitInterrupt("14",self.PrecompileIP) - self:Emit("end") - local expectedPage = self:GetPageByIndex(math.floor(self.PrecompileXEIP/128)) - if expectedPage.MappedIndex ~= math.floor(expectedPage.MappedIndex) then - -- Bad address because MappedIndex is a float, send out an invalid address interrupt if this gets executed(it probably will in a sec) - self:Dyn_EmitInterrupt(15,expectedPage.MappedIndex*128) - end - self:Emit("end") - self:Emit("if (VM.CurrentPage.MappedIndex ~= %d) then",expectedPage.MappedIndex) - -- This page wasn't the expected continuation, we should invalidate this address. (force recompile at this point) - self:Emit("VM:InvalidateVirtualPrecompileAddress(VM.XEIP)") - self:Dyn_EmitBreak(true) - self:Emit("end") - self:Emit("VM:SetPreviousPage(%d)",math.floor(self.PrecompileXEIP/128)) - - self.PrecompilePreviousPage = math.floor(self.PrecompileXEIP / 128) - end - - -- Fetch instruction and RM byte - local Opcode,RM = self:Precompile_Fetch(),0 - local isFixedSize = false - local OpCount,OpRunLevel = self.OperandCount,self.OpcodeRunLevel - local negativeOp = Opcode and Opcode < 0 - if negativeOp then - OpCount,OpRunLevel = self.ExtOperandCount,self.ExtOpcodeRunLevel - Opcode = Opcode * - 1 - end - -- Check if it is a fixed-size instruction - if ((Opcode >= 2000) and (Opcode < 4000)) or - ((Opcode >= 12000) and (Opcode < 14000)) then - Opcode = Opcode - 2000 - isFixedSize = true - end - - -- Fetch RM if required - - if (OpCount[Opcode % 1000] and (OpCount[Opcode % 1000] > 0)) - or (self:Precompile_Peek() == 0) or isFixedSize then - RM = self:Precompile_Fetch() - end - - -- If failed to fetch opcode/RM then report an error - if (not Opcode) or (not RM) then--if self.INTR == 1 then - self.IF = 1 - self:Interrupt(5,12) - return - end - - -- Check opcode runlevel - if self.OpcodeRunLevel[Opcode] then - 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 - local dRM2 = math.floor(RM / 10000) - local dRM1 = RM - dRM2*10000 - - -- Default segment offsets - local Segment1 = -4 - local Segment2 = -4 - - -- Decode segment prefixes - if Opcode > 1000 then - if Opcode > 10000 then - Segment2 = self:Precompile_Fetch() or 0 - - Opcode = Opcode-10000 - if Opcode > 1000 then - Segment1 = self:Precompile_Fetch() or 0 - - Opcode = Opcode-1000 - - local temp = Segment2 - Segment2 = Segment1 - Segment1 = temp - else - if isFixedSize then - self:Precompile_Fetch() - end - end - else - Segment1 = self:Precompile_Fetch() or 0 - Opcode = Opcode-1000 - if isFixedSize then - self:Precompile_Fetch() - end - end - elseif isFixedSize then - self:Precompile_Fetch() - self:Precompile_Fetch() - end - - -- If failed to fetch segment prefix then report an error - if (not Segment1) or (not Segment2) then--if self.INTR == 1 then - self:Interrupt(5,12) - return - end - - -- Check if opcode is invalid - if not OpCount[Opcode] then - self:Dyn_EmitInterrupt("4",Opcode) - self.PrecompileBreak = true - else - -- Emit segment prefix if required - self.EmitOperandSegment[1] = self.SegmentLookup[Segment1] - self.EmitOperandSegment[2] = self.SegmentLookup[Segment2] - - -- Fetch immediate values if required - if isFixedSize then - self.EmitOperandByte[1] = self:Precompile_Fetch() or 0 - if not self.EmitOperandByte[1] then self:Interrupt(5,22) return end - self.EmitOperandByte[2] = self:Precompile_Fetch() or 0 - if not self.EmitOperandByte[2] then self:Interrupt(5,32) return end - - if OpCount[Opcode] > 0 then - self:Dyn_LoadOperand(1,dRM1) - if OpCount[Opcode] > 1 then - self:Dyn_LoadOperand(2,dRM2) - end - end - else - if OpCount[Opcode] > 0 then - if self.NeedFetchByteLookup[dRM1] then - self.EmitOperandByte[1] = self:Precompile_Fetch() or 0 - -- If failed to read the byte, report an error - if not self.EmitOperandByte[1] then self:Interrupt(5,22) return end - end - self:Dyn_LoadOperand(1,dRM1) - - if OpCount[Opcode] > 1 then - if self.NeedFetchByteLookup[dRM2] then - self.EmitOperandByte[2] = self:Precompile_Fetch() or 0 - -- If failed to read the byte, report an error - if not self.EmitOperandByte[2] then self:Interrupt(5,32) return end - end - self:Dyn_LoadOperand(2,dRM2) - end - end - end - - -- Emit interrupt check prefix - if self.EmitNeedInterruptCheck then - self:Emit("VM.IP = %d",(self.PrecompileIP or 0)) - self:Emit("VM.XEIP = %d",(self.PrecompileTrueXEIP or 0)) - end - - -- Emit opcode - if negativeOp then - self:Dyn_EmitOpcode(Opcode*-1) - else - self:Dyn_EmitOpcode(Opcode) - end - - -- Write back the values - if OpCount[Opcode] and (OpCount[Opcode] > 0) then - self:Dyn_WriteOperand(1,dRM1) - if OpCount[Opcode] > 1 then - self:Dyn_WriteOperand(2,dRM2) - end - end - - -- Emit interrupt check - 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 - return not self.PrecompileBreak + -- Set true XEIP register value for this step (this value will be used if XEIP is accessed) + self.PrecompileTrueXEIP = self.PrecompileXEIP + self.PrecompileStartIP = self.PrecompileIP + + -- Move on to the next instruction + self.PrecompileInstruction = self.PrecompileInstruction + 1 + + -- Reset requirement for an interrupt check, reset registers + self.EmitNeedInterruptCheck = false + --self.EmitRegisterChangedByOperand = {} + + -- Reset interrupts trigger if precompiling + self.INTR = 0 + + -- Allows an opcode to know how big the instruction is. + self.PrecompileCurInstructionSize = 0 + + -- Check if we crossed the page boundary, if so - repeat the check + if math.floor(self.PrecompileXEIP / 128) ~= self.PrecompilePreviousPage then + self:Emit("VM:SetCurrentPage(%d)",math.floor(self.PrecompileXEIP/128)) + self:Emit("if (VM.PCAP == 1) then") + self:Emit("if (VM.CurrentPage.Execute == 0) and (VM.PreviousPage.RunLevel ~= 0) then") + self:Dyn_EmitInterrupt("14",self.PrecompileIP) + self:Emit("end") + local expectedPage = self:GetPageByIndex(math.floor(self.PrecompileXEIP/128)) + if expectedPage.MappedIndex ~= math.floor(expectedPage.MappedIndex) then + -- Bad address because MappedIndex is a float, send out an invalid address interrupt if this gets executed(it probably will in a sec) + self:Dyn_EmitInterrupt(15,expectedPage.MappedIndex*128) + end + self:Emit("end") + self:Emit("if (VM.CurrentPage.MappedIndex ~= %d) then",expectedPage.MappedIndex) + -- This page wasn't the expected continuation, we should invalidate this address. (force recompile at this point) + self:Emit("VM:InvalidateVirtualPrecompileAddress(VM.XEIP)") + self:Dyn_EmitBreak(true) + self:Emit("end") + self:Emit("VM:SetPreviousPage(%d)",math.floor(self.PrecompileXEIP/128)) + + self.PrecompilePreviousPage = math.floor(self.PrecompileXEIP / 128) + end + + -- Fetch instruction and RM byte + local Opcode,RM = self:Precompile_Fetch(),0 + local isFixedSize = false + local OpCount,OpRunLevel = self.OperandCount,self.OpcodeRunLevel + local negativeOp = Opcode and Opcode < 0 + if negativeOp then + OpCount,OpRunLevel = self.ExtOperandCount,self.ExtOpcodeRunLevel + Opcode = Opcode * - 1 + end + -- Check if it is a fixed-size instruction + if ((Opcode >= 2000) and (Opcode < 4000)) or + ((Opcode >= 12000) and (Opcode < 14000)) then + Opcode = Opcode - 2000 + isFixedSize = true + end + + -- Fetch RM if required + + if (OpCount[Opcode % 1000] and (OpCount[Opcode % 1000] > 0)) + or (self:Precompile_Peek() == 0) or isFixedSize then + RM = self:Precompile_Fetch() + end + + -- If failed to fetch opcode/RM then report an error + if (not Opcode) or (not RM) then--if self.INTR == 1 then + self.IF = 1 + self:Interrupt(5,12) + return + end + + -- Check opcode runlevel + if self.OpcodeRunLevel[Opcode] then + 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 + local dRM2 = math.floor(RM / 10000) + local dRM1 = RM - dRM2*10000 + + -- Default segment offsets + local Segment1 = -4 + local Segment2 = -4 + + -- Decode segment prefixes + if Opcode > 1000 then + if Opcode > 10000 then + Segment2 = self:Precompile_Fetch() or 0 + + Opcode = Opcode-10000 + if Opcode > 1000 then + Segment1 = self:Precompile_Fetch() or 0 + + Opcode = Opcode-1000 + + local temp = Segment2 + Segment2 = Segment1 + Segment1 = temp + else + if isFixedSize then + self:Precompile_Fetch() + end + end + else + Segment1 = self:Precompile_Fetch() or 0 + Opcode = Opcode-1000 + if isFixedSize then + self:Precompile_Fetch() + end + end + elseif isFixedSize then + self:Precompile_Fetch() + self:Precompile_Fetch() + end + + -- If failed to fetch segment prefix then report an error + if (not Segment1) or (not Segment2) then--if self.INTR == 1 then + self:Interrupt(5,12) + return + end + + -- Check if opcode is invalid + if not OpCount[Opcode] then + self:Dyn_EmitInterrupt("4",Opcode) + self.PrecompileBreak = true + else + -- Emit segment prefix if required + self.EmitOperandSegment[1] = self.SegmentLookup[Segment1] + self.EmitOperandSegment[2] = self.SegmentLookup[Segment2] + + -- Fetch immediate values if required + if isFixedSize then + self.EmitOperandByte[1] = self:Precompile_Fetch() or 0 + if not self.EmitOperandByte[1] then self:Interrupt(5,22) return end + self.EmitOperandByte[2] = self:Precompile_Fetch() or 0 + if not self.EmitOperandByte[2] then self:Interrupt(5,32) return end + + if OpCount[Opcode] > 0 then + self:Dyn_LoadOperand(1,dRM1) + if OpCount[Opcode] > 1 then + self:Dyn_LoadOperand(2,dRM2) + end + end + else + if OpCount[Opcode] > 0 then + if self.NeedFetchByteLookup[dRM1] then + self.EmitOperandByte[1] = self:Precompile_Fetch() or 0 + -- If failed to read the byte, report an error + if not self.EmitOperandByte[1] then self:Interrupt(5,22) return end + end + self:Dyn_LoadOperand(1,dRM1) + + if OpCount[Opcode] > 1 then + if self.NeedFetchByteLookup[dRM2] then + self.EmitOperandByte[2] = self:Precompile_Fetch() or 0 + -- If failed to read the byte, report an error + if not self.EmitOperandByte[2] then self:Interrupt(5,32) return end + end + self:Dyn_LoadOperand(2,dRM2) + end + end + end + + -- Emit interrupt check prefix + if self.EmitNeedInterruptCheck then + self:Emit("VM.IP = %d",(self.PrecompileIP or 0)) + self:Emit("VM.XEIP = %d",(self.PrecompileTrueXEIP or 0)) + end + + -- Emit opcode + if negativeOp then + self:Dyn_EmitOpcode(Opcode*-1) + else + self:Dyn_EmitOpcode(Opcode) + end + + -- Write back the values + if OpCount[Opcode] and (OpCount[Opcode] > 0) then + self:Dyn_WriteOperand(1,dRM1) + if OpCount[Opcode] > 1 then + self:Dyn_WriteOperand(2,dRM2) + end + end + + -- Emit interrupt check + 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 + return not self.PrecompileBreak end -- Helper that will convert virtual => physical for you if necessary and then invalidate them. function ZVM:InvalidateVirtualPrecompileAddress(Address) - local Page = self:GetPageByIndex(math.floor(Address/128)) - local newAddress = Address - if Page.Remapped == 1 then - Address = (Page.MappedIndex*128)+(Address%128) - end - self:InvalidatePrecompileAddress(Address) + local Page = self:GetPageByIndex(math.floor(Address/128)) + local newAddress = Address + if Page.Remapped == 1 then + Address = (Page.MappedIndex*128)+(Address%128) + end + self:InvalidatePrecompileAddress(Address) end -- These should be physical addresses. function ZVM:InvalidatePrecompileAddress(Address) - -- Invalidate precompiled data (Moved so the address will be changed to physical if necessary) - if self.IsAddressPrecompiled[Address] then - for k,v in ipairs(self.IsAddressPrecompiled[Address]) do - self.PrecompiledData[v] = nil - self.IsAddressPrecompiled[Address][k] = nil - end - end + -- Invalidate precompiled data (Moved so the address will be changed to physical if necessary) + if self.IsAddressPrecompiled[Address] then + for k,v in ipairs(self.IsAddressPrecompiled[Address]) do + self.PrecompiledData[v] = nil + self.IsAddressPrecompiled[Address][k] = nil + end + end end -------------------------------------------------------------------------------- -- VM step forward function ZVM:Step(overrideSteps,extraEmitFunction) - if self.BusLock == 1 then return end - - -- Trigger timers - self:TimerLogic() - - -- Calculate absolute execution address and set current page - self.XEIP = self.IP + self.CS - self:SetCurrentPage(math.floor(self.XEIP/128)) - - -- Do not allow execution if we are not on kernel page, or not calling from kernel page - if (self.PCAP == 1) and (self.CurrentPage.Execute == 0) and - (self.PreviousPage.RunLevel ~= 0) then - self:Interrupt(14,self.IP) - return -- Step failed - end - - -- Reset interrupts flags - self.INTR = 0 - if self.NIF then - self.IF = self.NIF - self.NIF = nil - end - - local address = self.XEIP - if self.CurrentPage.Remapped == 1 then - address = (self.CurrentPage.MappedIndex*128)+(self.XEIP%128) - end - - -- Check if current instruction is precompiled - local instructionPEIP = address - if self.PrecompiledData[instructionPEIP] or overrideSteps then - -- Precompile next instruction - if overrideSteps then - self:Precompile_Initialize() - self.ExtraEmitFunction = extraEmitFunction - local instruction = 1 - while (instruction <= overrideSteps) and self:Precompile_Step() do - if self.ExtraEmitFunction then - self:Emit("VM.IP = %d",(self.PrecompileIP or 0)) - self:Emit("VM.XEIP = %d",(self.PrecompileTrueXEIP or 0)) - self.ExtraEmitFunction(self) - end - instruction = instruction + 1 - end - self.ExtraEmitFunction = nil - self:Precompile_Finalize() - - -- Step clock forward (account for precompiling) - self.TMR = self.TMR + 24*8000 -- + overrideSteps*9000 - end - -- Execute precompiled instruction - if CLIENT then -- FIXME: hack around crash on PCALL - self.PrecompiledData[address]() - else - local status,message = pcall(self.PrecompiledData[address]) - if not status then - print("[ZVM ERROR]: "..message) - self:Interrupt(5,1) - end - end - else - -- Precompile several next instructions - self:Precompile_Initialize() - - local instruction = 1 - while (instruction <= 24) and self:Precompile_Step() do - instruction = instruction + 1 - end - - self:Precompile_Finalize() - - -- Step clock forward (account for precompiling) - self.TMR = self.TMR + 24*8000--instruction*9000 - end - - -- Set this page as previous (if it is executable) - self.XEIP = self.IP + self.CS - self:SetPreviousPage(math.floor(self.XEIP/128)) - return + if self.BusLock == 1 then return end + + -- Trigger timers + self:TimerLogic() + + -- Calculate absolute execution address and set current page + self.XEIP = self.IP + self.CS + self:SetCurrentPage(math.floor(self.XEIP/128)) + + -- Do not allow execution if we are not on kernel page, or not calling from kernel page + if (self.PCAP == 1) and (self.CurrentPage.Execute == 0) and + (self.PreviousPage.RunLevel ~= 0) then + self:Interrupt(14,self.IP) + return -- Step failed + end + + -- Reset interrupts flags + self.INTR = 0 + if self.NIF then + self.IF = self.NIF + self.NIF = nil + end + + local address = self.XEIP + if self.CurrentPage.Remapped == 1 then + address = (self.CurrentPage.MappedIndex*128)+(self.XEIP%128) + end + + -- Check if current instruction is precompiled + local instructionPEIP = address + if self.PrecompiledData[instructionPEIP] or overrideSteps then + -- Precompile next instruction + if overrideSteps then + self:Precompile_Initialize() + self.ExtraEmitFunction = extraEmitFunction + local instruction = 1 + while (instruction <= overrideSteps) and self:Precompile_Step() do + if self.ExtraEmitFunction then + self:Emit("VM.IP = %d",(self.PrecompileIP or 0)) + self:Emit("VM.XEIP = %d",(self.PrecompileTrueXEIP or 0)) + self.ExtraEmitFunction(self) + end + instruction = instruction + 1 + end + self.ExtraEmitFunction = nil + self:Precompile_Finalize() + + -- Step clock forward (account for precompiling) + self.TMR = self.TMR + 24*8000 -- + overrideSteps*9000 + end + -- Execute precompiled instruction + if CLIENT then -- FIXME: hack around crash on PCALL + self.PrecompiledData[address]() + else + local status,message = pcall(self.PrecompiledData[address]) + if not status then + print("[ZVM ERROR]: "..message) + self:Interrupt(5,1) + end + end + else + -- Precompile several next instructions + self:Precompile_Initialize() + + local instruction = 1 + while (instruction <= 24) and self:Precompile_Step() do + instruction = instruction + 1 + end + + self:Precompile_Finalize() + + -- Step clock forward (account for precompiling) + self.TMR = self.TMR + 24*8000--instruction*9000 + end + + -- Set this page as previous (if it is executable) + self.XEIP = self.IP + self.CS + self:SetPreviousPage(math.floor(self.XEIP/128)) + return end -- Any library that's needed by the entirety of the ZVM instruction set should go here -- Platform dependant libraries(GPU or SPU for example) should be added to the env by the platform ZVM.Env = { - math={ - Clamp = math.Clamp, - Round = math.Round, - floor = math.floor, - ceil = math.ceil, - fmod = math.fmod, - min = math.min, - max = math.max, - mod = math.mod, - sqrt = math.sqrt, - sin = math.sin, - cos = math.cos, - tan = math.tan, - asin = math.asin, - acos = math.acos, - atan = math.atan, - log = math.log, - log10 = math.log10, - exp = math.exp, - pi = math.pi, - abs = math.abs, - random = math.random - }, - bit={ - bnot = bit.bnot, - band = bit.band, - bor = bit.bor, - bxor = bit.bxor, - lshift = bit.lshift, - rshift = bit.rshift - }, - VM = ZVM + math={ + Clamp = math.Clamp, + Round = math.Round, + floor = math.floor, + ceil = math.ceil, + fmod = math.fmod, + min = math.min, + max = math.max, + mod = math.mod, + sqrt = math.sqrt, + sin = math.sin, + cos = math.cos, + tan = math.tan, + asin = math.asin, + acos = math.acos, + atan = math.atan, + log = math.log, + log10 = math.log10, + exp = math.exp, + pi = math.pi, + abs = math.abs, + random = math.random + }, + bit={ + bnot = bit.bnot, + band = bit.band, + bor = bit.bor, + bxor = bit.bxor, + lshift = bit.lshift, + rshift = bit.rshift + }, + VM = ZVM } -------------------------------------------------------------------------------- function ZVM:PrintState() - print("===========================") - print("TMR="..self.TMR.." TIMER="..self.TIMER.." IP="..self.IP.." CMPR="..self.CMPR) - print("EAX="..self.EAX.." EBX="..self.EBX.." ECX="..self.ECX.." EDX="..self.EDX) - print("ESI="..self.ESI.." EDI="..self.EDI.." ESP="..self.ESP.." EBP="..self.EBP.." ESZ="..self.ESZ) - print("CS="..self.CS.." SS="..self.SS.." DS="..self.DS.." FS="..self.FS.. - " GS="..self.GS.." ES="..self.ES.." KS="..self.KS.." LS="..self.LS) - print("MEMRQ="..self.MEMRQ.." MEMADDR="..self.MEMADDR.." LADD="..self.LADD) + print("===========================") + print("TMR="..self.TMR.." TIMER="..self.TIMER.." IP="..self.IP.." CMPR="..self.CMPR) + print("EAX="..self.EAX.." EBX="..self.EBX.." ECX="..self.ECX.." EDX="..self.EDX) + print("ESI="..self.ESI.." EDI="..self.EDI.." ESP="..self.ESP.." EBP="..self.EBP.." ESZ="..self.ESZ) + print("CS="..self.CS.." SS="..self.SS.." DS="..self.DS.." FS="..self.FS.. + " GS="..self.GS.." ES="..self.ES.." KS="..self.KS.." LS="..self.LS) + print("MEMRQ="..self.MEMRQ.." MEMADDR="..self.MEMADDR.." LADD="..self.LADD) end diff --git a/lua/wire/zvm/zvm_features.lua b/lua/wire/zvm/zvm_features.lua index 9aafecfa7d..fcbd309c6d 100644 --- a/lua/wire/zvm/zvm_features.lua +++ b/lua/wire/zvm/zvm_features.lua @@ -19,15 +19,15 @@ 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 + 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 @@ -36,110 +36,110 @@ 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 + 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 @@ -148,7 +148,7 @@ end -------------------------------------------------------------------------------- -- Checks if address is valid local function IsValidAddress(n) - return n and (math.floor(n) == n) and (n >= -140737488355327) and (n <= 140737488355328) + return n and (math.floor(n) == n) and (n >= -140737488355327) and (n <= 140737488355328) end @@ -164,17 +164,17 @@ 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 + 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 + if Address >= 0 + then self:Interrupt(7,Address) return -- MemBus + else return 0 -- IOBus + end end @@ -183,7 +183,7 @@ end -------------------------------------------------------------------------------- -- Default WritePort handler function ZVM:WritePort(Port,Value) - self:WriteCell(-Port-1,Value) + self:WriteCell(-Port-1,Value) end @@ -192,7 +192,7 @@ end -------------------------------------------------------------------------------- -- Default ReadPort handler function ZVM:ReadPort(Port) - return self:ReadCell(-Port-1) + return self:ReadCell(-Port-1) end @@ -201,81 +201,81 @@ 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 + -- 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 @@ -284,86 +284,86 @@ 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 + 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 @@ -371,20 +371,20 @@ 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 + -- 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 @@ -392,20 +392,20 @@ 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 + -- 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 @@ -414,12 +414,12 @@ end -------------------------------------------------------------------------------- -- Write value to stack (SSTACK implementation) function ZVM:WriteToStack(Index,Value) - -- Check bus lock flag - if self.BusLock == 1 then return false end + -- 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) + -- 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 @@ -428,13 +428,13 @@ end -------------------------------------------------------------------------------- -- Read a value from stack (RSTACK implementation) function ZVM:ReadFromStack(Index) - -- Check bus lock flag - if self.BusLock == 1 then return 0 end + -- 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 + -- 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 @@ -443,76 +443,76 @@ 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, + 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, + 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 + 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 + 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 + 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 @@ -520,35 +520,35 @@ 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 + 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 @@ -556,23 +556,23 @@ 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 + 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 @@ -580,11 +580,11 @@ end -------------------------------------------------------------------------------- function ZVM:SetCurrentPage(index) - if self.PCAP == 1 then - self.CurrentPage = self:GetPageByIndex(index) - else - self.CurrentPage = defaultPage - end + if self.PCAP == 1 then + self.CurrentPage = self:GetPageByIndex(index) + else + self.CurrentPage = defaultPage + end end @@ -592,11 +592,11 @@ end -------------------------------------------------------------------------------- function ZVM:SetPreviousPage(index) - if self.PCAP == 1 then - self.PreviousPage = self:GetPageByIndex(index) - else - self.PreviousPage = defaultPage - end + if self.PCAP == 1 then + self.PreviousPage = self:GetPageByIndex(index) + else + self.PreviousPage = defaultPage + end end @@ -604,19 +604,19 @@ 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 + 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 @@ -624,244 +624,244 @@ 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 + 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))) +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 + -- 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 @@ -870,28 +870,28 @@ 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 + 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 @@ -900,64 +900,64 @@ 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 + 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 + 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 + 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 + 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) + 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) + 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) + 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 + for i=0,15 do self:WriteCell(address+i,matrix[i]) end end @@ -966,32 +966,32 @@ 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 + -- 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 @@ -1000,19 +1000,19 @@ 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 + 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 @@ -1021,15 +1021,15 @@ end -------------------------------------------------------------------------------- -- Binary OR function ZVM:BinaryOr(m,n) - local bits_m = self:IntegerToBinary(m) - local bits_n = self:IntegerToBinary(n) - local bits = {} + 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 + for i = 0, self.IPREC-1 do + bits[i] = math.min(1,bits_m[i]+bits_n[i]) + end - return self:BinaryToInteger(bits) + return self:BinaryToInteger(bits) end @@ -1038,15 +1038,15 @@ end -------------------------------------------------------------------------------- -- Binary AND function ZVM:BinaryAnd(m,n) - local bits_m = self:IntegerToBinary(m) - local bits_n = self:IntegerToBinary(n) - local bits = {} + 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 + for i = 0, self.IPREC-1 do + bits[i] = bits_m[i]*bits_n[i] + end - return self:BinaryToInteger(bits) + return self:BinaryToInteger(bits) end @@ -1055,13 +1055,13 @@ end -------------------------------------------------------------------------------- -- Binary NOT function ZVM:BinaryNot(n) - local bits_n = self:IntegerToBinary(n) - local bits = {} + 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) + for i = 0, self.IPREC-1 do + bits[i] = 1-bits_n[i] + end + return self:BinaryToInteger(bits) end @@ -1070,15 +1070,15 @@ end -------------------------------------------------------------------------------- -- Binary XOR function ZVM:BinaryXor(m,n) - local bits_m = self:IntegerToBinary(m) - local bits_n = self:IntegerToBinary(n) - local bits = {} + 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 + for i = 0, self.IPREC-1 do + bits[i] = (bits_m[i]+bits_n[i]) % 2 + end - return self:BinaryToInteger(bits) + return self:BinaryToInteger(bits) end @@ -1087,18 +1087,18 @@ 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) + 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 @@ -1107,17 +1107,17 @@ end -------------------------------------------------------------------------------- -- Binary shift left function ZVM:BinarySHL(n,cnt) - local bits_n = self:IntegerToBinary(n) - local bits = {} + 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 + 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) + return self:BinaryToInteger(bits) end -------------------------------------------------------------------------------- @@ -1125,14 +1125,14 @@ end -- 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 + 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 -------------------------------------------------------------------------------- @@ -1140,7 +1140,7 @@ end -- 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)) + return math.ldexp(1,math.floor(num)) end -------------------------------------------------------------------------------- diff --git a/lua/wire/zvm/zvm_opcodes.lua b/lua/wire/zvm/zvm_opcodes.lua index 939c03ca65..712af0e875 100644 --- a/lua/wire/zvm/zvm_opcodes.lua +++ b/lua/wire/zvm/zvm_opcodes.lua @@ -10,7 +10,7 @@ -- Initialize opcode count lookup table ZVM.OperandCount = {} for _,instruction in pairs(CPULib.InstructionTable) do - ZVM.OperandCount[instruction.Opcode] = instruction.OperandCount + ZVM.OperandCount[instruction.Opcode] = instruction.OperandCount end @@ -18,9 +18,9 @@ end -- Initialize runlevel lookup table ZVM.OpcodeRunLevel = {} for _,instruction in pairs(CPULib.InstructionTable) do - if instruction.Privileged then - ZVM.OpcodeRunLevel[instruction.Opcode] = 0 - end + if instruction.Privileged then + ZVM.OpcodeRunLevel[instruction.Opcode] = 0 + end end -- If an opcode is negative(like for extensions), the opcode will be absoluted @@ -69,1781 +69,1781 @@ ZVM.ExtOpcodeRunLevel = {} ZVM.OpcodeTable = {} ZVM.OpcodeTable[0] = function(self) --END (STOP) - self:Dyn_EmitInterrupt("2","0") - self.PrecompileBreak = true -- Stop precompiler from following further + 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") + 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 + 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") + 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") + 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: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") + 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") + 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") + 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() + 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") + self:Dyn_EmitOperand("$1 + $2") end ZVM.OpcodeTable[11] = function(self) --SUB - self:Dyn_EmitOperand("$1 - $2") + self:Dyn_EmitOperand("$1 - $2") end ZVM.OpcodeTable[12] = function(self) --MUL - self:Dyn_EmitOperand("$1 * $2") + 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") + 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") + self:Dyn_EmitOperand("$2") end ZVM.OpcodeTable[15] = function(self) --CMP - self:Dyn_Emit("VM.CMPR = $1 - $2") + 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() + 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") + 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)") + self:Dyn_EmitOperand("math.min($1,$2)") end ZVM.OpcodeTable[19] = function(self) --MAX - self:Dyn_EmitOperand("math.max($1,$2)") + self:Dyn_EmitOperand("math.max($1,$2)") end -------------------------------------------------------------------------------- ZVM.OpcodeTable[20] = function(self) --INC - self:Dyn_EmitOperand("$1 + 1") + self:Dyn_EmitOperand("$1 + 1") end ZVM.OpcodeTable[21] = function(self) --DEC - self:Dyn_EmitOperand("$1 - 1") + self:Dyn_EmitOperand("$1 - 1") end ZVM.OpcodeTable[22] = function(self) --NEG - self:Dyn_EmitOperand("-$1") + self:Dyn_EmitOperand("-$1") end ZVM.OpcodeTable[23] = function(self) --RAND - self:Dyn_EmitOperand("math.random()") + 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") + 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") + 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") + 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") + 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") + 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") + 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() + 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: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 + 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") + 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)") + self:Dyn_EmitOperand("math.floor($1)") end ZVM.OpcodeTable[34] = function(self) --RND - self:Dyn_EmitOperand("math.Round($1)") + 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)") + 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") + 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)") + self:Dyn_Emit("VM.HaltPort = math.floor($1)") end ZVM.OpcodeTable[38] = function(self) --FSHL - self:Dyn_EmitOperand("$1 * 2") + self:Dyn_EmitOperand("$1 * 2") end ZVM.OpcodeTable[39] = function(self) --FSHR - self:Dyn_EmitOperand("$1 / 2") + 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: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 + 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 + 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") + self:Dyn_Emit("VM.NIF = 1") end ZVM.OpcodeTable[43] = function(self) --CLI - self:Dyn_Emit("VM.IF = 0") + self:Dyn_Emit("VM.IF = 0") end ZVM.OpcodeTable[44] = function(self) --STP - self:Dyn_Emit("VM.PF = 1") + self:Dyn_Emit("VM.PF = 1") end ZVM.OpcodeTable[45] = function(self) --CLP - self:Dyn_Emit("VM.PF = 0") + 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 + -- 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: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 + self.PrecompileBreak = true end ZVM.OpcodeTable[48] = function(self) --STEF - self:Dyn_Emit("VM.EF = 1") + self:Dyn_Emit("VM.EF = 1") end ZVM.OpcodeTable[49] = function(self) --CLEF - self:Dyn_Emit("VM.EF = 0") + 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") + 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") + 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") + 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)") + self:Dyn_EmitOperand("math.sin($2)") end ZVM.OpcodeTable[54] = function(self) --FCOS - self:Dyn_EmitOperand("math.cos($2)") + self:Dyn_EmitOperand("math.cos($2)") end ZVM.OpcodeTable[55] = function(self) --FTAN - self:Dyn_EmitOperand("math.tan($2)") + self:Dyn_EmitOperand("math.tan($2)") end ZVM.OpcodeTable[56] = function(self) --FASIN - self:Dyn_EmitOperand("math.asin($2)") + self:Dyn_EmitOperand("math.asin($2)") end ZVM.OpcodeTable[57] = function(self) --FACOS - self:Dyn_EmitOperand("math.acos($2)") + self:Dyn_EmitOperand("math.acos($2)") end ZVM.OpcodeTable[58] = function(self) --FATAN - self:Dyn_EmitOperand("math.atan($2)") + 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") + 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") + 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") + 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") + 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") + 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") + 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") + 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") + 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") + 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") + 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 + 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 + 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") + 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() + 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") + 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") + 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") + 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") + 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") + 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") + 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") + 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") + 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") + 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)") + self:Dyn_EmitOperand("math.log($2)") end ZVM.OpcodeTable[83] = function(self) --FLOG10 - self:Dyn_EmitOperand("math.log10($2)") + self:Dyn_EmitOperand("math.log10($2)") end ZVM.OpcodeTable[84] = function(self) --IN - self:Dyn_EmitOperand("VM:ReadPort($2)") - self:Dyn_EmitInterruptCheck() + 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() + self:Dyn_Emit("VM:WritePort($1,$2)") + self:Dyn_EmitInterruptCheck() end ZVM.OpcodeTable[86] = function(self) --FABS - self:Dyn_EmitOperand("math.abs($2)") + 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") + 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)") + 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: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 + self.PrecompileBreak = true end -------------------------------------------------------------------------------- ZVM.OpcodeTable[90] = function(self) --FPI - self:Dyn_EmitOperand("3.141592653589793") + self:Dyn_EmitOperand("3.141592653589793") end ZVM.OpcodeTable[91] = function(self) --FE - self:Dyn_EmitOperand("2.718281828459045") + self:Dyn_EmitOperand("2.718281828459045") end ZVM.OpcodeTable[92] = function(self) --INT - self:Dyn_EmitInterrupt("$1","0") + 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") + 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)") + 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") + 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") + 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") + 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))) + 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") + 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() + 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") + 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:Dyn_Emit("VM:Jump(%d + $1)",self.PrecompileIP) + self:Dyn_EmitState() + self:Dyn_EmitBreak() - self.PrecompileBreak = true + 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") + 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") + 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") + 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") + 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") + 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)") + 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") + 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 + 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") + 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") + 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() + 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") + 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") + 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_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") + 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") + self:Dyn_Emit("VM.MF = 1") end ZVM.OpcodeTable[119] = function(self) --CLM - self:Dyn_Emit("VM.MF = 0") + 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() + 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 + 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") + 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") + 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") + 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") + 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) + 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") + 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") + 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") + 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") + 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") + 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() + 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() + 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() + 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_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") + 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 + 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 + 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 + -- 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 + 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 + 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))") + self:Dyn_Emit("VM:SignalError(VM:MakeErrorCode($1,$2))") end ZVM.OpcodeTable[151] = function(self) -- CLERR - self:Dyn_Emit("VM:SignalError(0)") + 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") + 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") + 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") + 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") + 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") + 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") + 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") + 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") + 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") + 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() + 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") + 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() + 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("$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() + 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() + 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("$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() + 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() + 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() + 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() + 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() + 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() + 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() + 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() + 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") + 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() + 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") + 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") + 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() + 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 diff --git a/lua/wire/zvm/zvm_tests.lua b/lua/wire/zvm/zvm_tests.lua index aeb5fc3dbc..91ac10c8bb 100644 --- a/lua/wire/zvm/zvm_tests.lua +++ b/lua/wire/zvm/zvm_tests.lua @@ -207,7 +207,7 @@ function ZVMTestSuite.InternalSuccessCallback() end function ZVMTestSuite.InternalErrorCallback(msg) - HCOMP.LoadFile = ZVMTestSuite.HCOMPLoadFile + HCOMP.LoadFile = ZVMTestSuite.HCOMPLoadFile HCOMP.Warning = ZVMTestSuite.OldHCOMPWarning if ZVMTestSuite.CompileArgs.ErrorCallback then ZVMTestSuite.CompileArgs.ErrorCallback(msg) @@ -464,7 +464,7 @@ function ZVMTestSuite.Initialize(VM,Membus,IOBus) LongestStepExecutionTime = 0, -- Longest execution time during VM:Step ExecutionSteps = 0, -- How many execution steps were performed by this VM } - + table.insert(ZVMTestSuite.Benchmarks,VM.ZVMBenchmark) VM.ZVMBenchmark.ProbableOwner = ZVMTestSuite.TestQueue[#ZVMTestSuite.TestQueue] or "Unknown" if not ZVMTestSuite.BenchmarksByTest[VM.ZVMBenchmark.ProbableOwner] then