/* SOC++: A turing-completeness extension to SOC through Lua-defined Actions also known as Golden's latest shenanigan -- Integer Types -- The SOC++ Actions expect that you send them numbers with specific bounds. Specifically, A_SOC++_*Byte functions expect 8-bit integers, A_SOC++_*Word functions expect 16-bit numbers, and A_SOC++_*LongWord functions expect 32-bit numbers. There are also other special integers that expect special constants (eg. states), which have their own types. These integer types are defined here simply to illustrate these requirements and conventions, and help you understand the Action definitions below. int8_t: signed 8-bit integer (byte) int16_t: signed 16-bit integer (word) int32_t: signed 32-bit integer (long word) state_t: SRB2 state constant (S_*) memcell_t (int32_t *): pointer to a memcell (covered next) -- Pointers -- Pointers are just memory addresses stored as values. Dereferencing refers to reading the memory stored at the index determined by another value in memory - For example, if the number 42 is being dereferenced, you're reading the value at index 42 in memory. You can have pointers *to* pointers, in which case they're called "double pointers". This pattern continues on for more iterations of pointers. In this implementation, pointers to memory are stored as int32_t numbers (or just regular SRB2 Lua integers). If you're ever called, say, a 4-star programmer, they're telling you that you're utilising level-4 pointers. Though it's impressive that you've managed to implement something like that in SOC++, it's not a compliment, typically. Feel free to make use of them if they end up being useful, but try not to go overboard. Terminology used in below Action definitions: any_t ptr: any_t value, named "ptr" any_t *ptr: pointer to any_t value, named "ptr" any_t **ptr: pointer to pointer to any_t value (double pointer to any_t value), named "ptr" -- Action documentation: -- // Memory manipulation - Note, SOC++ implements one-byte addressing. That means that each memory address holds one byte of data. - This becomes important when dealing with words or long words. Words are 2 bytes long, long words are 4 bytes long. - Simply adding 1 to a pointer will overwrite the lesser significant bytes of words of long words if you're not careful! // For any functions, that are named "A_SOC++_Action*" that use integer types "int*_t", do the following for your desired bit width. // For byte (8-bit) operations, replace "*" in action names with "Byte", and replace "int*_t" with "int8_t". // For word (16-bit) operations, replace "*" in action names with "Word", and replace "int*_t" with "int16_t". // For long word (32-bit) operations, replace "*" in action names with "LongWord", and replace "int*_t" with "int32_t". // For example, A_SOC++_Set*(memcell_t memcell, int*_t integer) would translate into: // A_SOC++_SetByte(memcell_t memcell, int8_t integer) for 8-bit operations, // A_SOC++_SetWord(memcell_t memcell, int16_t integer) for 16-bit operations, // A_SOC++_SetLongWord(memcell_t memcell, int32_t integer) for 32-bit operations, A_SOC++_Set*(memcell_t memcell, int*_t integer): set integer at memcell to integer constant A_SOC++_Clear*(memcell_t memcell): shorthand for A_SOC++_Set*(memcell, 0) A_SOC++_Copy*(memcell_t memcell, memcell_t memcell): copy integer at memcell1 to integer at memcell2 A_SOC++_Print*(memcell_t memcell): print integer at memcell to console // Value stack manipulation // A classic FIFO stack A_SOC++_Push*(memcell_t memcell): push integer at memcell1 to the stack A_SOC++_Push*Const(int*_t integer): push integer constant to the stack A_SOC++_Pop*(memcell_t memcell): pop integer from the stack to memory cell // Math functions A_SOC++_Add*(memcell_t memcell1, memcell_t memcell2): add integer at memcell1 to integer at memcell2, result stored in memcell1 A_SOC++_Add*Const(memcell_t memcell, int*_t integer): add integer at memcell to integer constant, result stored in memcell A_SOC++_Sub*(memcell_t memcell1, memcell_t memcell2): subtract integer at memcell1 with integer at memcell2, result stored in memcell1 A_SOC++_Sub*Const(memcell_t memcell, int*_t integer): subtract integer at memcell with integer constant, result stored in memcell A_SOC++_Mul*(memcell_t memcell1, memcell_t memcell2): multiply integer at memcell1 with integer at memcell2, result stored in memcell1 A_SOC++_Mul*Const(memcell_t memcell, int*_t integer): multiply integer at memcell with integer constant, result stored in memcell A_SOC++_Div*(memcell_t memcell1, memcell_t memcell2): divide integer at memcell1 with integer at memcell2, result stored in memcell1 A_SOC++_Div*Const(memcell_t memcell, int*_t integer): divide integer at memcell with integer constant, result stored in memcell A_SOC++_Mod*(memcell_t memcell1, memcell_t memcell2): modulo integer at memcell1 with integer at memcell2, result stored in memcell1 A_SOC++_Mod*Const(memcell_t memcell, int*_t integer): modulo integer at memcell with integer constant, result stored in memcell // String printing A_SOC++_PrintString(memcell_t memcell, int32_t length): print bytes as ASCII characters into the console, up to a specific length. A_SOC++_PrintCString(memcell_t memcell): print bytes as ASCII characters into the console, up to the next 0 byte. // Program flow // Implements conditional branching // Also implements subroutines A_SOC++_Compare*(memcell_t memcell1, memcell_t memcell2): compare integer at memcell1 to integer at memcell2 A_SOC++_Compare*Const(memcell_t memcell, int*_t integer): compare integer at memcell to integer constant A_SOC++_JumpIfEqual(state_t newstate): set actor's state to new state if and only if a previous comparison was equal A_SOC++_JumpIfNotEqual(state_t newstate): set actor's state to new state if and only if a previous comparison was not equal A_SOC++_JumpIfGreaterThan(state_t newstate): set actor's state to new state if and only if a previous comparison was greater than A_SOC++_JumpIfLessThan(state_t newstate): set actor's state to new state if and only if a previous comparison was less than A_SOC++_JumpIfGreaterEqual(state_t newstate): set actor's state to new state if and only if a previous comparison was greater than or equal A_SOC++_JumpIfLessEqual(state_t newstate): set actor's state to new state if and only if a previous comparison was less than or equal A_SOC++_BranchIfEqual(state_t newstate): A_SOC++_BranchIfNotEqual(state_t newstate): A_SOC++_BranchIfGreaterThan(state_t newstate): A_SOC++_BranchIfLessThan(state_t newstate): A_SOC++_BranchIfGreaterEqual(state_t newstate): A_SOC++_BranchIfLessEqual(state_t newstate): same as above but pushes next state to call stack to return to upon A_SOC++_Return A_SOC++_Branch(state_t newstate): pushes next state to call stack to return to upon A_SOC++_Return, unconditionally jump to new state A_SOC++_Return(): pop state from call stack to set current state. */ // // info and debug // local debug = false local function spp_printf(format, ...) local print = hudprint or print print(string.format("\x89SOC++: \x80" .. format, ...)) end // // some constants // local action_prefix = "A_SOC++_" local tmpstate_name = "S_SOC++_TMPSTATE" local socpp = {version = {major = 1, minor = 0, patch = 0}, actions = {}} local socpp_key = "soc++" // // basic version control // local socpp_g = _G[socpp_key] if socpp_g and socpp_g.actions then spp_printf("SOC++ was previously added, clearing its global Action references for replacement.") spp_printf("This will not replace references within states, so your old SOC++ code will be uneffected.") for k, v in pairs(socpp_g.actions) do _G[action_prefix .. k] = nil end end rawset(_G, socpp_key, socpp) // // Lua-side memory cell manipulation // -- top scope locals, because all the 0-duration states of a object are updated iteratively, not sporadically local recursion = 0 -- recursion level. local queue = {} -- format {{actor = actor, state = state}, ...} local tmpstate = freeslot(tmpstate_name) states[tmpstate] = {SPR_NULL, A, -1, nil, 0, 0, tmpstate} local SOCVars = {} local actions = {} local CFF_EQUAL = 1<<0 local CFF_GREATER = 1<<1 -- strips all the data outside of the byte range, keeping sign data local function toByte(integer) local sign = (integer & 0x80000000) >> 31 local byte = (integer & 0xFF) | (sign << 7) return byte end -- strips all the data outside of the word range, keeping sign data local function toWord(integer) local sign = (integer & 0x80000000) >> 31 local word = (integer & 0xFFFF) | (sign << 15) return word end -- assert but acts like error("message", 2) local function assertArg(condition, msg) if not condition then error(msg or "assertion failed!", 3) end return condition end // Gets a byte from memory. Doesn't include input sanitation checks. local function mem_getByte(memory, index) if index < 0 then return 0 end local realindex = index / 4 local realshift = 32 - (((index % 4) + 1) * 8) local realmask = 0xFF << realshift memory[realindex] = $ or 0 local realvalue = (memory[realindex] & realmask) >> realshift local realsign = realvalue >> 7 if realsign then realvalue = $ - 256 end return realvalue & 0xFF end // Sets a byte in memory to a new value. Doesn't include input sanitation checks. local function mem_setByte(memory, index, value, maxmemaddress) if index < 0 then return maxmemaddress end local realindex = index / 4 local realshift = 32 - (((index % 4) + 1) * 8) local realmask = 0xFF << realshift local sign = (value & 0x80000000) >> 31 local byte = (value & 0xFF) | (sign << 7) memory[realindex] = $ or 0 memory[realindex] = $ & ~realmask memory[realindex] = $ | (byte << realshift) if maxmemaddress ~= nil and realindex > maxmemaddress then return realindex end return maxmemaddress end local function socv_generallySetComparisonVars(socvars, num1, num2) socvars.cff = $ & ~CFF_EQUAL socvars.cff = $ & ~CFF_GREATER socvars.cff = $ | ((num1 == num2) and CFF_EQUAL or 0) socvars.cff = $ | ((num1 > num2) and CFF_GREATER or 0) end // Nice helper function that tries to convert a key into an index. If it fails, it prints a customisable reason. local function tryGetNumericKey(key, reason) local index = tonumber(key) if index == nil then error(string.format("bruh moment trying to %s at memory key '%s'", reason or "access value", key or "nil"), 2) end return index end function SOCVars:__construct(...) local socvars = { memory = {}, -- memory data, for all memory Actions maxmemaddress = 0, -- highest address written to through memory Actions callstack = {}, -- call stack, for A_Branch* and A_Return valuestack = {}, -- value stack, for SOC++ scripter convenience. valuestackpointer = 0, -- the first byte in the valuestack that isnt written to. cff = 0, -- control flow flags maxcallstack = 256 -- maximum length of call stack before it gets suspicious } setmetatable(socvars, {__index = SOCVars}) return socvars end // Gets a byte from a memory cell. function SOCVars:getByte(key) local index = tryGetNumericKey(key, "get byte") return mem_getByte(self.memory, index) end // Gets a word from a memory cell. function SOCVars:getWord(key) local index = tryGetNumericKey(key, "get word") local num = 0 num = $ | (mem_getByte(self.memory, index + 0) << 8) num = $ | (mem_getByte(self.memory, index + 1) << 0) return num end // Gets a long word from a memory cell. function SOCVars:getLongWord(key) local index = tryGetNumericKey(key, "get long word") local num = 0 num = $ | (mem_getByte(self.memory, index + 0) << 24) num = $ | (mem_getByte(self.memory, index + 1) << 16) num = $ | (mem_getByte(self.memory, index + 2) << 8) num = $ | (mem_getByte(self.memory, index + 3) << 0) return num end // Pops a byte from the stack. function SOCVars:popByte() local num = mem_getByte(self.valuestack, self.valuestackpointer - 1) mem_setByte(self.valuestack, self.valuestackpointer - 1, 0) self.valuestackpointer = $ - 1 self.valuestackpointer = $ >= 0 and $ or 0 return num end // Pops a word from the stack. function SOCVars:popWord() local num = 0 num = $ | (mem_getByte(self.valuestack, self.valuestackpointer - 2) << 8) num = $ | (mem_getByte(self.valuestack, self.valuestackpointer - 1) ) mem_setByte(self.valuestack, self.valuestackpointer - 2, 0) mem_setByte(self.valuestack, self.valuestackpointer - 1, 0) self.valuestackpointer = $ - 2 self.valuestackpointer = $ >= 0 and $ or 0 return num end // Pops a long word from the stack. function SOCVars:popLongWord() local num = 0 num = $ | (mem_getByte(self.valuestack, self.valuestackpointer - 4) << 24) num = $ | (mem_getByte(self.valuestack, self.valuestackpointer - 3) << 16) num = $ | (mem_getByte(self.valuestack, self.valuestackpointer - 2) << 8) num = $ | (mem_getByte(self.valuestack, self.valuestackpointer - 1) << 0) mem_setByte(self.valuestack, self.valuestackpointer - 4, 0) mem_setByte(self.valuestack, self.valuestackpointer - 3, 0) mem_setByte(self.valuestack, self.valuestackpointer - 2, 0) mem_setByte(self.valuestack, self.valuestackpointer - 1, 0) self.valuestackpointer = $ - 4 self.valuestackpointer = $ >= 0 and $ or 0 return num end // Sets a memory cell's value to a byte. function SOCVars:setByte(key, byte) local index = tryGetNumericKey(key, "set byte") self.maxmemaddress = mem_setByte(self.memory, index, value, $) end // Sets a memory cell's value to a word. function SOCVars:setWord(key, word) local index = tryGetNumericKey(key, "set word") self.maxmemaddress = mem_setByte(self.memory, index + 0, (word >> 8) & 0xFF, $) self.maxmemaddress = mem_setByte(self.memory, index + 1, (word ) & 0xFF, $) end // Sets a memory cell's value to a long word. function SOCVars:setLongWord(key, longword) local index = tryGetNumericKey(key, "set long word") self.maxmemaddress = mem_setByte(self.memory, index + 0, (longword >> 24) & 0xFF, $) self.maxmemaddress = mem_setByte(self.memory, index + 1, (longword >> 16) & 0xFF, $) self.maxmemaddress = mem_setByte(self.memory, index + 2, (longword >> 8) & 0xFF, $) self.maxmemaddress = mem_setByte(self.memory, index + 3, (longword ) & 0xFF, $) end // Pushes a byte to the stack. function SOCVars:pushByte(byte) self.maxmemaddress = mem_setByte(self.valuestack, self.valuestackpointer, value, $) self.valuestackpointer = $ + 1 end // Pushes a word to the stack. function SOCVars:pushWord(word) self.maxmemaddress = mem_setByte(self.valuestack, self.valuestackpointer + 0, (word >> 8) & 0xFF, $) self.maxmemaddress = mem_setByte(self.valuestack, self.valuestackpointer + 1, (word ) & 0xFF, $) self.valuestackpointer = $ + 2 end // Pushes a long word to the stack. function SOCVars:pushLongWord(longword) self.maxmemaddress = mem_setByte(self.valuestack, self.valuestackpointer + 0, (longword >> 24) & 0xFF, $) self.maxmemaddress = mem_setByte(self.valuestack, self.valuestackpointer + 1, (longword >> 16) & 0xFF, $) self.maxmemaddress = mem_setByte(self.valuestack, self.valuestackpointer + 2, (longword >> 8) & 0xFF, $) self.maxmemaddress = mem_setByte(self.valuestack, self.valuestackpointer + 3, (longword ) & 0xFF, $) self.valuestackpointer = $ + 4 end -- i probably didn't need 3 functions that do the exact same thing, but consistency is helpful??? function SOCVars:compareBytes(byte1, byte2) socv_generallySetComparisonVars(self, byte1, byte2) end function SOCVars:compareWords(word1, word2) socv_generallySetComparisonVars(self, word1, word2) end function SOCVars:compareLongWords(longword1, longword2) socv_generallySetComparisonVars(self, longword1, longword2) end -- NOTE: call only via tail call function SOCVars:jumpOnCondition(actor, state, condition) assertArg(states[state], "State not valid!") if condition then recursion = $ + 1 if recursion >= 98 then table.insert(queue, 1, {actor = actor, state = state}) actor.state = tmpstate return end actor.state = state end end -- NOTE: call only via tail call function SOCVars:branchOnCondition(actor, state, condition) assertArg(states[state], "State not valid!") if condition then recursion = $ + 1 if #self.callstack > self.maxcallstack then error("Too much recursion, fix your SOC++ code!", 2) else table.insert(self.callstack, states[actor.state].nextstate) end if recursion >= 98 then table.insert(queue, 1, {actor = actor, state = state}) actor.state = tmpstate return end actor.state = state end end -- NOTE: call only via tail call function SOCVars:popCall(actor) recursion = $ + 1 local state = table.remove(self.callstack) if state == nil then return end if recursion >= 98 then table.insert(queue, 1, {actor = actor, state = state}) actor.state = tmpstate return end actor.state = state end -- Helpful function that helped me debug issues with the memory setters and getters function SOCVars:dumpMemory() local str = "[" local chars = "0123456789ABCDEF" for i = 0, self.maxmemaddress do local v = self.memory[i] or 0 str = $ .. "0x" for i = 1, 8 do local nibble = v >> (32 - (i * 4)) local index = (nibble & 0xF) + 1 str = $ .. chars:sub(index, index) end if i < self.maxmemaddress then str = $ .. ", " end end str = $ .. "]" print(str) end -- SOCVars lua class setmetatable(SOCVars, { -- instantiate SOCVars __call = SOCVars.__construct }) // Internal helper function that gets the SOC++ vars local function getActorSOCVars(actor) actor["soc++"] = $ or SOCVars() return actor["soc++"] end // // Writing to memory // function actions.SetByte(actor, memcell, integer) // A_SOC++_SetByte getActorSOCVars(actor):setByte(memcell, integer) end function actions.SetWord(actor, memcell, integer) // A_SOC++_SetWord getActorSOCVars(actor):setWord(memcell, integer) end function actions.SetLongWord(actor, memcell, integer) // A_SOC++_SetLongWord getActorSOCVars(actor):setLongWord(memcell, integer) end // // Clearing from memory // function actions.ClearByte(actor, memcell) // A_SOC++_ClearByte actions.SetByte(actor, memcell, 0) end function actions.ClearWord(actor, memcell) // A_SOC++_ClearWord actions.SetWord(actor, memcell, 0) end function actions.ClearLongWord(actor, memcell) // A_SOC++_ClearLongWord actions.SetLongWord(actor, memcell, 0) end // // Copying memory // function actions.CopyByte(actor, memcell1, memcell2) // A_SOC++_CopyByte local socvars = getActorSOCVars(actor) socvars:setByte(memcell2, socvars:getByte(memcell1)) end function actions.CopyWord(actor, memcell1, memcell2) // A_SOC++_CopyWord local socvars = getActorSOCVars(actor) socvars:setWord(memcell2, socvars:getWord(memcell1)) end function actions.CopyLongWord(actor, memcell1, memcell2) // A_SOC++_CopyLongWord local socvars = getActorSOCVars(actor) socvars:setLongWord(memcell2, socvars:getLongWord(memcell1)) end // // Printing (reading) memory // function actions.PrintByte(actor, memcell) // A_SOC++_PrintByte print(getActorSOCVars(actor):getByte(memcell)) end function actions.PrintWord(actor, memcell) // A_SOC++_PrintWord print(getActorSOCVars(actor):getWord(memcell)) end function actions.PrintLongWord(actor, memcell) // A_SOC++_PrintLongWord print(getActorSOCVars(actor):getLongWord(memcell)) end // // Printing numerical constants (for debugging your SOC++ script) // function actions.PrintByteConst(actor, integer) // A_SOC++_PrintByteConst print(toByte(integer)) end function actions.PrintWordConst(actor, integer) // A_SOC++_PrintWordConst print(toWord(integer)) end function actions.PrintLongWordConst(actor, integer) // A_SOC++_PrintLongWordConst print(integer) end // // Pushing numbers to the stack from memory // function actions.PushByte(actor, memcell) // A_SOC++_PushByte local socvars = getActorSOCVars(actor) socvars:pushByte(socvars:getByte(memcell)) end function actions.PushWord(actor, memcell) // A_SOC++_PushWord local socvars = getActorSOCVars(actor) socvars:pushWord(socvars:getWord(memcell)) end function actions.PushLongWord(actor, memcell) // A_SOC++_PushLongWord local socvars = getActorSOCVars(actor) socvars:pushLongWord(socvars:getLongWord(memcell)) end // // Pushing numbers to the stack from numerical constants // function actions.PushByteConst(actor, integer) // A_SOC++_PushByteConst local socvars = getActorSOCVars(actor) socvars:pushByte(toByte(integer)) end function actions.PushWordConst(actor, integer) // A_SOC++_PushWordConst local socvars = getActorSOCVars(actor) socvars:pushWord(toWord(integer)) end function actions.PushLongWordConst(actor, integer) // A_SOC++_PushLongWordConst local socvars = getActorSOCVars(actor) socvars:pushLongWord(integer) end // // Popping numbers from the stack to memory // function actions.PopByte(actor, memcell) // A_SOC++_PopByte local socvars = getActorSOCVars(actor) socvars:setByte(memcell, socvars:popByte()) end function actions.PopWord(actor, memcell) // A_SOC++_PopWord local socvars = getActorSOCVars(actor) socvars:setWord(memcell, socvars:popWord()) end function actions.PopLongWord(actor, memcell) // A_SOC++_PopLongWord local socvars = getActorSOCVars(actor) socvars:setLongWord(memcell, socvars:popLongWord()) end // // Addition between two memory cells, writes to first cell. // function actions.AddByte(actor, memcell1, memcell2) // A_SOC++_AddByte local socvars = getActorSOCVars(actor) socvars:setByte(memcell1, socvars:getByte(memcell1) + socvars:getByte(memcell2)) end function actions.AddWord(actor, memcell1, memcell2) // A_SOC++_AddWord local socvars = getActorSOCVars(actor) socvars:setWord(memcell1, socvars:getWord(memcell1) + socvars:getWord(memcell2)) end function actions.AddLongWord(actor, memcell1, memcell2) // A_SOC++_AddLongWord local socvars = getActorSOCVars(actor) socvars:setLongWord(memcell1, socvars:getLongWord(memcell1) + socvars:getLongWord(memcell2)) end // // Addition with memory and constant // function actions.AddByteConst(actor, memcell, integer) // A_SOC++_AddByteConst local socvars = getActorSOCVars(actor) socvars:setByte(memcell, socvars:getByte(memcell) + integer) end function actions.AddWordConst(actor, memcell, integer) // A_SOC++_AddWordConst local socvars = getActorSOCVars(actor) socvars:setWord(memcell, socvars:getWord(memcell) + integer) end function actions.AddLongWordConst(actor, memcell, integer) // A_SOC++_AddLongWordConst local socvars = getActorSOCVars(actor) socvars:setLongWord(memcell, socvars:getLongWord(memcell) + integer) end // // Subtraction between two memory cells, writes to first cell. // function actions.SubByte(actor, memcell1, memcell2) // A_SOC++_SubByte local socvars = getActorSOCVars(actor) socvars:setByte(memcell1, socvars:getByte(memcell1) - socvars:getByte(memcell2)) end function actions.SubWord(actor, memcell1, memcell2) // A_SOC++_SubWord local socvars = getActorSOCVars(actor) socvars:setWord(memcell1, socvars:getWord(memcell1) - socvars:getWord(memcell2)) end function actions.SubLongWord(actor, memcell1, memcell2) // A_SOC++_SubLongWord local socvars = getActorSOCVars(actor) socvars:setLongWord(memcell1, socvars:getLongWord(memcell1) - socvars:getLongWord(memcell2)) end // // Subtraction with memory and constant // function actions.SubByteConst(actor, memcell, integer) // A_SOC++_SubByteConst local socvars = getActorSOCVars(actor) socvars:setByte(memcell, socvars:getByte(memcell) - integer) end function actions.SubWordConst(actor, memcell, integer) // A_SOC++_SubWordConst local socvars = getActorSOCVars(actor) socvars:setWord(memcell, socvars:getWord(memcell) - integer) end function actions.SubLongWordConst(actor, memcell, integer) // A_SOC++_SubLongWordConst local socvars = getActorSOCVars(actor) socvars:setLongWord(memcell, socvars:getLongWord(memcell) - integer) end // // Multiplication between two memory cells, writes to first cell. // function actions.MulByte(actor, memcell1, memcell2) // A_SOC++_MulByte local socvars = getActorSOCVars(actor) socvars:setByte(memcell1, socvars:getByte(memcell1) * socvars:getByte(memcell2)) end function actions.MulWord(actor, memcell1, memcell2) // A_SOC++_MulWord local socvars = getActorSOCVars(actor) socvars:setWord(memcell1, socvars:getWord(memcell1) * socvars:getWord(memcell2)) end function actions.MulLongWord(actor, memcell1, memcell2) // A_SOC++_MulLongWord local socvars = getActorSOCVars(actor) socvars:setLongWord(memcell1, socvars:getLongWord(memcell1) * socvars:getLongWord(memcell2)) end // // Multiplication with memory and constant // function actions.MulByteConst(actor, memcell, integer) // A_SOC++_MulByteConst local socvars = getActorSOCVars(actor) socvars:setByte(memcell, socvars:getByte(memcell) * integer) end function actions.MulWordConst(actor, memcell, integer) // A_SOC++_MulWordConst local socvars = getActorSOCVars(actor) socvars:setWord(memcell, socvars:getWord(memcell) * integer) end function actions.MulLongWordConst(actor, memcell, integer) // A_SOC++_MulLongWordConst local socvars = getActorSOCVars(actor) socvars:setLongWord(memcell, socvars:getLongWord(memcell) * integer) end // // Division between two memory cells, writes to first cell. // function actions.DivByte(actor, memcell1, memcell2) // A_SOC++_DivByte local socvars = getActorSOCVars(actor) socvars:setByte(memcell1, socvars:getByte(memcell1) / assertArg(socvars:getByte(memcell2), "division by zero")) end function actions.DivWord(actor, memcell1, memcell2) // A_SOC++_DivWord local socvars = getActorSOCVars(actor) socvars:setWord(memcell1, socvars:getWord(memcell1) / assertArg(socvars:getWord(memcell2), "division by zero")) end function actions.DivLongWord(actor, memcell1, memcell2) // A_SOC++_DivLongWord local socvars = getActorSOCVars(actor) socvars:setLongWord(memcell1, socvars:getLongWord(memcell1) / assertArg(socvars:getLongWord(memcell2), "division by zero")) end // // Division with memory and constant // function actions.DivByteConst(actor, memcell, integer) // A_SOC++_DivByteConst local socvars = getActorSOCVars(actor) socvars:setByte(memcell, socvars:getByte(memcell) / assertArg(integer, "division by zero")) end function actions.DivWordConst(actor, memcell, integer) // A_SOC++_DivWordConst local socvars = getActorSOCVars(actor) socvars:setWord(memcell, socvars:getWord(memcell) / assertArg(integer, "division by zero")) end function actions.DivLongWordConst(actor, memcell, integer) // A_SOC++_DivLongWordConst local socvars = getActorSOCVars(actor) socvars:setLongWord(memcell, socvars:getLongWord(memcell) / assertArg(integer, "division by zero")) end // // Modulous operation between two memory cells, writes to first cell. // function actions.ModByte(actor, memcell1, memcell2) // A_SOC++_ModByte local socvars = getActorSOCVars(actor) socvars:setByte(memcell1, socvars:getByte(memcell1) % assertArg(socvars:getByte(memcell2), "modulo by zero")) end function actions.ModWord(actor, memcell1, memcell2) // A_SOC++_ModWord local socvars = getActorSOCVars(actor) socvars:setWord(memcell1, socvars:getWord(memcell1) % assertArg(socvars:getWord(memcell2), "modulo by zero")) end function actions.ModLongWord(actor, memcell1, memcell2) // A_SOC++_ModLongWord local socvars = getActorSOCVars(actor) socvars:setLongWord(memcell1, socvars:getLongWord(memcell1) % assertArg(socvars:getLongWord(memcell2), "modulo by zero")) end // // Modulous operation with memory and constant // function actions.ModByteConst(actor, memcell, integer) // A_SOC++_ModByteConst local socvars = getActorSOCVars(actor) socvars:setByte(memcell, socvars:getByte(memcell) % assertArg(integer, "modulo by zero")) end function actions.ModWordConst(actor, memcell, integer) // A_SOC++_ModWordConst local socvars = getActorSOCVars(actor) socvars:setWord(memcell, socvars:getWord(memcell) % assertArg(integer, "modulo by zero")) end function actions.ModLongWordConst(actor, memcell, integer) // A_SOC++_ModLongWordConst local socvars = getActorSOCVars(actor) socvars:setLongWord(memcell, socvars:getLongWord(memcell) % assertArg(integer, "modulo by zero")) end // // Print memory cells as ASCII characters // // Prints a string with a specific length. function actions.PrintString(actor, memcell, length) // A_SOC++_PrintString local socvars = getActorSOCVars(actor) local str = "" for i = memcell, memcell + length - 1 do str = $ .. string.char(socvars:getByte(i)) end print(str) end // Prints a C string (an list of characters terminated by a 0 byte). function actions.PrintCString(actor, memcell) // A_SOC++_PrintCString local socvars = getActorSOCVars(actor) local str = "" local byte = socvars:getByte(memcell) while byte do str = $ .. string.char(byte) memcell = $ + 1 byte = socvars:getByte(memcell) end print(str) end // // Memory comparisons, for conditional jumping and branching. // function actions.CompareByte(actor, memcell1, memcell2) // A_SOC++_CompareByte local socvars = getActorSOCVars(actor) socvars:compareBytes(socvars:getByte(memcell1), socvars:getByte(memcell2)) end function actions.CompareWord(actor, memcell1, memcell2) // A_SOC++_CompareWord local socvars = getActorSOCVars(actor) socvars:compareWords(socvars:getWord(memcell1), socvars:getWord(memcell2)) end function actions.CompareLongWord(actor, memcell1, memcell2) // A_SOC++_CompareLongWord local socvars = getActorSOCVars(actor) socvars:compareLongWords(socvars:getLongWord(memcell1), socvars:getLongWord(memcell2)) end // // Memory comparisons with constants, for conditional jumping and branching. // function actions.CompareByteConst(actor, memcell, integer) // A_SOC++_CompareByteConst local socvars = getActorSOCVars(actor) socvars:compareBytes(socvars:getByte(memcell), integer) end function actions.CompareWordConst(actor, memcell, integer) // A_SOC++_CompareWordConst local socvars = getActorSOCVars(actor) socvars:compareWords(socvars:getWord(memcell), integer) end function actions.CompareLongWordConst(actor, memcell, integer) // A_SOC++_CompareLongWordConst local socvars = getActorSOCVars(actor) socvars:compareLongWords(socvars:getLongWord(memcell), integer) end // // Conditional jumping // function actions.JumpIfEqual(actor, state) // A_SOC++_JumpIfEqual local socvars = getActorSOCVars(actor) return socvars:jumpOnCondition(actor, state, ((socvars.cff & CFF_EQUAL))) end function actions.JumpIfNotEqual(actor, state) // A_SOC++_JumpIfNotEqual local socvars = getActorSOCVars(actor) return socvars:jumpOnCondition(actor, state, (not (socvars.cff & CFF_EQUAL))) end function actions.JumpIfLessThan(actor, state) // A_SOC++_JumpIfLessThan local socvars = getActorSOCVars(actor) return socvars:jumpOnCondition(actor, state, (not (socvars.cff & CFF_GREATER) and not (socvars.cff & CFF_EQUAL))) end function actions.JumpIfGreaterThan(actor, state) // A_SOC++_JumpIfGreaterThan local socvars = getActorSOCVars(actor) return socvars:jumpOnCondition(actor, state, ((socvars.cff & CFF_GREATER) and not (socvars.cff & CFF_EQUAL))) end function actions.JumpIfLessEqual(actor, state) // A_SOC++_JumpIfLessEqual local socvars = getActorSOCVars(actor) return socvars:jumpOnCondition(actor, state, (not (socvars.cff & CFF_GREATER) and (socvars.cff & CFF_EQUAL))) end function actions.JumpIfGreaterEqual(actor, state) // A_SOC++_JumpIfGreaterEqual local socvars = getActorSOCVars(actor) return socvars:jumpOnCondition(actor, state, ((socvars.cff & CFF_GREATER) and (socvars.cff & CFF_EQUAL))) end // // Conditional branching // function actions.BranchIfEqual(actor, state) // A_SOC++_BranchIfEqual local socvars = getActorSOCVars(actor) return socvars:branchOnCondition(actor, state, ((socvars.cff & CFF_EQUAL))) end function actions.BranchIfNotEqual(actor, state) // A_SOC++_BranchIfNotEqual local socvars = getActorSOCVars(actor) return socvars:branchOnCondition(actor, state, (not (socvars.cff & CFF_EQUAL))) end function actions.BranchIfLessThan(actor, state) // A_SOC++_BranchIfLessThan local socvars = getActorSOCVars(actor) return socvars:branchOnCondition(actor, state, (not (socvars.cff & CFF_GREATER) and not (socvars.cff & CFF_EQUAL))) end function actions.BranchIfGreaterThan(actor, state) // A_SOC++_BranchIfGreaterThan local socvars = getActorSOCVars(actor) return socvars:branchOnCondition(actor, state, ((socvars.cff & CFF_GREATER) and not (socvars.cff & CFF_EQUAL))) end function actions.BranchIfLessEqual(actor, state) // A_SOC++_BranchIfLessEqual local socvars = getActorSOCVars(actor) return socvars:branchOnCondition(actor, state, (not (socvars.cff & CFF_GREATER) and (socvars.cff & CFF_EQUAL))) end function actions.BranchIfGreaterEqual(actor, state) // A_SOC++_BranchIfGreaterEqual local socvars = getActorSOCVars(actor) return socvars:branchOnCondition(actor, state, ((socvars.cff & CFF_GREATER) and (socvars.cff & CFF_EQUAL))) end function actions.Branch(actor, state) // A_SOC++_BranchIfGreaterEqual local socvars = getActorSOCVars(actor) return socvars:branchOnCondition(actor, state, true) end function actions.Return(actor) // A_SOC++_Return return getActorSOCVars(actor):popCall(actor) end spp_printf("Registering SOC actions...") for k, v in pairs(actions) do local func = v if debug then func = function(actor, var1, var2) spp_printf(action_prefix .. k .. "(actor, " .. var1 .. ", " .. var2 .. ")") v(actor, var1, var2) end end _G[action_prefix .. k] = func end // A begrudingly required hook used to resolve recursion issues // If I didn't do anything I'd fill up the lua call stack and have // C stack overflow errors in the console, // and more importantly, broken SOC++ execution addHook("PreThinkFrame", function() recursion = 1 while #queue do local v = table.remove(queue) v.actor.state = v.state end queue = {} end) /* // Initial debugging stuff I did to test out things before I decided to make a test SOC // Keeping here for releases for those interested in the pre-SOC debugging process. local actor = setmetatable({}, {__index = actions}) local socvars = getActorSOCVars(actor) actor:SetLongWord(0, UINT32_MAX) actor:PrintLongWord(0) socvars:dumpMemory() actor:AddLongWordConst(0, 1) actor:PrintLongWord(0) actor:SetLongWord(4, 0x48656C6C) // "Hell" actor:SetLongWord(8, 0x6F20776F) // "o wo" actor:SetLongWord(12, 0x726C6421) // "rld!" actor:PrintString(4, 12) socvars:dumpMemory() actor:CompareLongWord(1, 2) */