| The so-called 'disassembler' takes in Cicada bytecode code (NOT real machine code) | and outputs a text version that is somewhat more readable. The two arguments are the compiled code string and | the position of the 'flag' (error flag) within that string. Index 1 = bytes 1-wordSize, index 2 = ws+1 - 2*ws bytes, etc. | An flag position of 0 indicates no flag. The return value is the disassembly string. disassembler :: { compiledNames *:: [] string instructionWords :: { "j", "jt", "jf", "code", "ret", "f", "CCf", "dg", "feq", "sm", "sID", "sti", "stis", "s*", "rsz", "+i", "+is", "rm", "eq", "ne", "gt", "ge", "lt", "le", "eq@", "ne@", "add", "sub", "mul", "div", "pow", "mod", "not", "and", "or", "xor", "c#", "sub", "app", "args", "this", "that", "back", "top", "*", "arr", "bool", "char", "int", "dbl", "str", "cb", "cc", "ci", "cdb", "cst", "" } instructionArguments :: { 0, 1, 1, 0, 1, 2, 2, 2, 2, 0, 1, 2, 3, 1, 2, 2, 3, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } extraWords :: { 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, size(double)/size(int), 1, 0 } dgWords :: [] { ID :: int, abbr :: string } dgWords[*] = { { 1, "equ" }, { 6, "mdf"}, { 16, "eqa"}, { 22, "dqa"}, { 44, "vdf"}, { 46, "def" }, { 47, "deq"}, { 148, "dqa*"}, { 172, "def*"}, { 173, "deq*"}, { 236, "def**"}, { 237, "deq**"}, { 204, "def-c**"} } CicadaFunctionWords :: { "call", "", "compile", "transform", "load", "save", "input", "print", "read_string", "print_string", "trap", "throw", "top", "size", "abs", "floor", "ceil", "log", "cos", "sin", "tan", "acos", "asin", "atan", "random", "find", "type", "member_ID", "bytecode", "springCleaning" } returnDisassembly :: expandFunctions :: bool bytecodeWords :: [] int numCompiledNames :: numWords :: disasmStartPos :: flagPosition :: int namespace :: bytecodeString :: output :: string wordSize := size(int) doubleSizeInInts := size(double)/size(int) runDisAsm :: { varStart :: varEnd :: CN_counter :: wordCounter :: argsError :: int rtrn :: * params :: { expandFunctions := @\.\.expandFunctions, flagPosition := @\.\.flagPosition } code params = { true, 0 } if trap( returnDisassembly = (top(args) <= 2) if returnDisassembly then { bytecodeString } = args else { bytecodeString, disasmStartPos } = { args[1], args[3] } if top(args) >= 2 then ( compiledNames = @args[2] if compiledNames ==@ * then compiledNames = @allNames ) else compiledNames = @allNames (params< 0") compiledNames = @nothing return ) numWords = size(bytecodeString)/wordSize if disasmStartPos > numWords then ( printl("disassemble() error: start_pos > # of words") compiledNames = @nothing return ) numCompiledNames = top(compiledNames) bytecodeWords[^numWords] bytecodeWords =! bytecodeString output = "" if trap( if returnDisassembly then ( wordCounter = 1 output = RecursionPackage.writeSentences(wordCounter) if wordCounter /= numWords+1 then ( cat(output, "[Not the correct end-of-code]\n") ) ) else ( wordCounter = disasmStartPos output = RecursionPackage.writeExpression(wordCounter) args[3] = wordCounter ) ) /= passed then ( printl("disassemble() error: problem with the bytecode") compiledNames = @nothing return ) compiledNames = @nothing if not returnDisassembly then return else return ((rtrn =@ *) = @output) } RecursionPackage :: { doWrite :: bool expressionString := "" writeSentences :: { WSexpressionTop :: * bottomLevel := false sentenceString :: string code WSexpressionTop = @args[1] sentenceString = "" while bytecodeWords[WSexpressionTop] /= 0 do ( sentenceString = cat(sentenceString, writeExpression(WSexpressionTop)) if bytecodeWords[WSexpressionTop] /= 0 and returnDisassembly then ( if bottomLevel then sentenceString = cat(sentenceString, "\n") else if doWrite then sentenceString = cat(sentenceString, ", ") ) ) if WSexpressionTop > flagPosition and flagPosition /= 0 and returnDisassembly then ( cat(output, "<-- ***** ") flagPosition = 0 ) if bottomLevel and returnDisassembly then cat(output, "\n") WSexpressionTop =@ * return sentenceString } writeExpression :: { constBool :: bool constChar :: char constInt :: numChars :: charCounter :: instructionArgs :: argCounter :: int constDouble :: double constString :: theCmd :: string WEcat_fulloutput :: { ; theCmd = cat(theCmd, args, " ") } WEcat_linesonly :: { } WEcat *:: * currentCommand :: extraSkip :: mID :: int expressionTop *:: * code theCmd = "" if returnDisassembly and doWrite then WEcat = @WEcat_fulloutput else WEcat = @WEcat_linesonly expressionTop = @args[1] currentCommand = bytecodeWords[expressionTop] if currentCommand == 0 then return WEcat(instructionWords[currentCommand]) extraSkip = extraWords[currentCommand] instructionArgs = instructionArguments[currentCommand] | some commands get special treatment | jump commands -- we write out the jump offset if currentCommand >= 1 and currentCommand <= 3 then ( constInt =! bytecodeWords[expressionTop+1] WEcat(constInt) extraSkip = 1 ) | Cicada library functions -- substitute the name of the specific library (if it's obvious) else if currentCommand == 7 then ( if bytecodeWords[expressionTop+1] == 54 then ( theCmd = "" WEcat(CicadaFunctionWords[bytecodeWords[expressionTop+2]+1]) extraSkip = 2 instructionArgs = that-1 ) ) | define-equate-etc. operators -- write in the name of the specific operator if it's a common one else if currentCommand == 8 then ( theCmd = "" if trap(WEcat(dgWords[binsearch(dgWords[*].ID, bytecodeWords[expressionTop+1])].abbr)) /= passed then ( WEcat(instructionWords[currentCommand], " ", bytecodeWords[expressionTop+1]) ) ) | search-member or step-to-member -- write in the member name else if currentCommand == 10 or currentCommand == 11 then ( mID = bytecodeWords[expressionTop+1] if compiledNames == @nothing then WEcat(mID) else if mID < 0 then WEcat("$", -mID) else if mID > numCompiledNames or mID == 0 then WEcat("??") else WEcat("$", compiledNames[mID]) extraSkip = 1 ) | constant: bool, char, int, double, string -- just write out the constant in the disassembly else if currentCommand == 52 then ( constBool = (bytecodeWords[expressionTop+1] /= 0) theCmd = "" if constBool then WEcat("true") else WEcat("false") extraSkip = 1 ) else if currentCommand == 53 then ( theCmd = "" constChar = bytecodeWords[expressionTop+1] WEcat("'", constChar, "'") extraSkip = 1 ) else if currentCommand == 54 then ( theCmd = "" constInt = bytecodeWords[expressionTop+1] WEcat(constInt) extraSkip = 1 ) else if currentCommand == 55 then ( theCmd = "" constDouble =! bytecodeWords[] WEcat(constDouble) extraSkip = doubleSizeInInts ) else if currentCommand == 56 then ( theCmd = "" numChars = bytecodeWords[expressionTop+1] constString =! bytecodeWords[] constString = constString[<1, numChars>] for (charCounter::int) in <1, numChars> ( if constString[charCounter] < 32 or constString[charCounter] > 127 then ( constString[charCounter] = 35 )) WEcat("\"", constString, "\"") extraSkip = ceil(numChars/wordSize)+1 ) | code block -- write it in {} else if currentCommand == 57 then ( theCmd = "" expressionTop = that + 1 subExpression :: RecursionPackage subExpression.doWrite = doWrite and expandFunctions if doWrite and not subExpression.doWrite then theCmd = "{ ... " else theCmd = "{ " WEcat(subExpression.writeSentences(expressionTop), "}") ) | if we're trying to mark a spot in the code, write out a marker there expressionTop = that + 1 + extraSkip if expressionTop > flagPosition and flagPosition /= 0 then ( command = cat(command, "<-- ***** ") flagPosition = 0 ) | now write out each of the arguments of the operator if instructionArgs > 0 then ( subExpression::RecursionPackage WEcat("(") for (argCounter::int) in <1, instructionArgs> ( if argCounter /= 1 then WEcat(",") subExpression.doWrite = doWrite WEcat(subExpression.writeExpression(expressionTop)) ) WEcat(")") ) expressionTop =@ * if top(theCmd) > 0 then theCmd[^top-1] return theCmd } } RecursionPackage.writeSentences.bottomLevel = true RecursionPackage.doWrite = true } disassemble := @disassembler.runDisAsm