diff --git a/src/cat/LuaScriptInterface.cpp b/src/cat/LuaScriptInterface.cpp index f5b69ee3f..87496ec6e 100644 --- a/src/cat/LuaScriptInterface.cpp +++ b/src/cat/LuaScriptInterface.cpp @@ -20,6 +20,7 @@ #include "dialogues/TextPrompt.h" #include "dialogues/ConfirmPrompt.h" #include "simulation/Simulation.h" +#include "virtualmachine/VirtualMachine.h" #include "game/GameModel.h" #include "LuaScriptHelper.h" #include "client/HTTP.h" @@ -63,6 +64,7 @@ LuaScriptInterface::LuaScriptInterface(GameController * c, GameModel * m): initInterfaceAPI(); initRendererAPI(); initElementsAPI(); + initVirtualMachineAPI(); //Old TPT API int i = 0, j; @@ -651,6 +653,42 @@ void LuaScriptInterface::initElementsAPI() } } +vm::VirtualMachine * LuaScriptInterface::updateVirtualMachines[PT_NUM]; + +int LuaScriptInterface::updateVM(UPDATE_FUNC_ARGS) +{ + vm::VirtualMachine * vMachine = updateVirtualMachines[parts[i].type]; + + vm::word w; + int argAddr = 0, argCount = 5; + vMachine->sim = sim; + + /* Set up call. */ + vMachine->OpPUSH(w); //Pointless null in stack + w.int4 = (argCount + 2) * sizeof(vm::word); + vMachine->OpENTER(w); + argAddr = 8; + + //Arguments + w.int4 = i; vMachine->Marshal(argAddr, w); argAddr += 4; + w.int4 = x; vMachine->Marshal(argAddr, w); argAddr += 4; + w.int4 = y; vMachine->Marshal(argAddr, w); argAddr += 4; + w.int4 = nt; vMachine->Marshal(argAddr, w); argAddr += 4; + w.int4 = surround_space; vMachine->Marshal(argAddr, w); argAddr += 4; + + w.int4 = 0; + vMachine->Push(w); + + vMachine->OpCALL(w); + vMachine->Run(); + w.int4 = (argCount + 2) * sizeof(vm::word); + vMachine->OpLEAVE(w); + vMachine->OpPOP(w); //Pop pointless null + vMachine->End(); + + return 0; +} + int LuaScriptInterface::elements_loadDefault(lua_State * l) { int args = lua_gettop(l); @@ -957,6 +995,11 @@ int LuaScriptInterface::elements_property(lua_State * l) lua_el_func[id] = luaL_ref(l, LUA_REGISTRYINDEX); luacon_sim->elements[id].Update = &luacon_elementReplacement; } + else if(lua_type(l, 3) == LUA_TLIGHTUSERDATA) + { + updateVirtualMachines[id] = (vm::VirtualMachine*)lua_touserdata(l, 3); + luacon_sim->elements[id].Update = &updateVM; + } else if(lua_type(l, 3) == LUA_TBOOLEAN && !lua_toboolean(l, 3)) { lua_el_func[id] = 0; @@ -1061,6 +1104,41 @@ int LuaScriptInterface::elements_free(lua_State * l) return 0; } +void LuaScriptInterface::initVirtualMachineAPI() +{ + //Methods + struct luaL_reg vmAPIMethods [] = { + {"loadProgram", virtualMachine_loadProgram}, + {NULL, NULL} + }; + luaL_register(l, "virtualMachine", vmAPIMethods); + + //elem shortcut + lua_getglobal(l, "virtualMachine"); + lua_setglobal(l, "vm"); + + int vmAPI = lua_gettop(l); +} + +int LuaScriptInterface::virtualMachine_loadProgram(lua_State * l) +{ + luaL_checktype(l, 1, LUA_TSTRING); + + vm::VirtualMachine * newVM = new vm::VirtualMachine(1); + try + { + const char * tempString = lua_tostring(l, 1); + int tempStringLength = lua_strlen(l, 1); + std::vector programData(tempString, tempString+tempStringLength); + newVM->LoadProgram(programData); + } + catch(std::exception & e) + { + return luaL_error(l, "Unable to load program"); + } + lua_pushlightuserdata(l, newVM); + return 1; +} bool LuaScriptInterface::OnBrushChanged(int brushType, int rx, int ry) { diff --git a/src/cat/LuaScriptInterface.h b/src/cat/LuaScriptInterface.h index 5bee8e119..c40728c8e 100644 --- a/src/cat/LuaScriptInterface.h +++ b/src/cat/LuaScriptInterface.h @@ -23,6 +23,12 @@ namespace ui class Window; } +namespace vm +{ + class VirtualMachine; +} + + //Because lua only has bindings for C, we're going to have to go outside "outside" the LuaScriptInterface, this means we can only have one instance :( #define LOCAL_LUA_DIR "Lua" @@ -59,6 +65,9 @@ class LuaScriptInterface: public CommandInterface { static int renderer_colourMode(lua_State * l); //Elements + static vm::VirtualMachine * updateVirtualMachines[PT_NUM]; + static int updateVM(UPDATE_FUNC_ARGS); + // void initElementsAPI(); static int elements_allocate(lua_State * l); static int elements_element(lua_State * l); @@ -71,6 +80,10 @@ class LuaScriptInterface: public CommandInterface { static int interface_showWindow(lua_State * l); static int interface_closeWindow(lua_State * l); static int interface_addComponent(lua_State * l); + + //VM + void initVirtualMachineAPI(); + static int virtualMachine_loadProgram(lua_State * l); public: ui::Window * Window; lua_State *l; diff --git a/src/interface/Textbox.cpp b/src/interface/Textbox.cpp index 603fa23d8..ab4d907f1 100644 --- a/src/interface/Textbox.cpp +++ b/src/interface/Textbox.cpp @@ -191,6 +191,21 @@ void Textbox::pasteIntoSelection() backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound()); cursor = getLowerSelectionBound(); } + for(std::string::iterator iter = newText.begin(), end = newText.end(); iter != end; ++iter) + { + if(!CharacterValid(*iter)) + { + if(inputType == All) + { + if(*iter == '\n' || *iter == '\r') + *iter = ' '; + else + *iter = '?'; + } + else + *iter = '0'; + } + } backingText.insert(cursor, newText); cursor = cursor+newText.length(); ClearSelection(); diff --git a/src/virtualmachine/Exceptions.h b/src/virtualmachine/Exceptions.h index fbc434176..9628d105f 100644 --- a/src/virtualmachine/Exceptions.h +++ b/src/virtualmachine/Exceptions.h @@ -86,4 +86,15 @@ namespace vm } ~OutOfMemoryException() throw() {}; }; + + class InvalidProgramException: public RuntimeException + { + public: + InvalidProgramException() {} + const char * what() const throw() + { + return "Could not load program"; + } + ~InvalidProgramException() throw() {}; + }; } \ No newline at end of file diff --git a/src/virtualmachine/Syscalls.cpp b/src/virtualmachine/Syscalls.cpp index b9f921155..642e7639f 100644 --- a/src/virtualmachine/Syscalls.cpp +++ b/src/virtualmachine/Syscalls.cpp @@ -61,9 +61,9 @@ namespace vm TRAPDEF(partCreate) { - //Push(sim->create_part(ARG(0).int4, ARG(1).int4, ARG(2).int4, ARG(3).int4)); - printf("create_part(%d, %d, %d, %d)\n", ARG(0).int4, ARG(1).int4, ARG(2).int4, ARG(3).int4); + printf("%d, %d, %d, %d\n", ARG(0).int4, ARG(1).int4, ARG(2).int4, ARG(3).int4); Push(0); + //Push(sim->create_part(ARG(0).int4, ARG(1).int4, ARG(2).int4, ARG(3).int4)); } TRAPDEF(partChangeType) diff --git a/src/virtualmachine/VirtualMachine.cpp b/src/virtualmachine/VirtualMachine.cpp index 9de822dbf..7d1af8e9c 100644 --- a/src/virtualmachine/VirtualMachine.cpp +++ b/src/virtualmachine/VirtualMachine.cpp @@ -74,86 +74,87 @@ namespace vm } /* Read one octet from file. */ - int VirtualMachine::readByte(FILE *qvmfile) + int VirtualMachine::readByte(std::istream & input) { int o; - o = fgetc(qvmfile); + o = input.get(); if (o < 0) o = 0; /* EOF (hack) */ return o; } /* Read little-endian 32-bit integer from file. */ - int VirtualMachine::readInt(FILE *qvmfile) + int VirtualMachine::readInt(std::istream & input) { int a, b, c, d, n; - a = readByte(qvmfile); - b = readByte(qvmfile); - c = readByte(qvmfile); - d = readByte(qvmfile); + a = readByte(input); + b = readByte(input); + c = readByte(input); + d = readByte(input); n = (a) | (b << 8) | (c << 16) | (d << 24); return n; } - int VirtualMachine::LoadProgram(char * filename) + int VirtualMachine::readProgram(std::istream & input) { - FILE * qvmfile = fopen(filename, "rb"); qvm_header_t qvminfo; int i, n; uint1_t x[4]; word w; DEBUGTRACE("Loading file...\n"); - qvminfo.magic = readInt(qvmfile); /* magic. */ + qvminfo.magic = readInt(input); /* magic. */ if (qvminfo.magic != QVM_MAGIC) - { - DEBUGTRACE("Invalid magic"); - //q3vm_error("Does not appear to be a QVM file."); - /* XXX: option to force continue. */ - return 0; - } + { + DEBUGTRACE("Invalid magic"); + throw InvalidProgramException(); + //q3vm_error("Does not appear to be a QVM file."); + /* XXX: option to force continue. */ + return 0; + } DEBUGTRACE("Magic OK\n"); /* variable-length instructions mean instruction count != code length */ - qvminfo.inscount = readInt(qvmfile); - qvminfo.codeoff = readInt(qvmfile); - qvminfo.codelen = readInt(qvmfile); - qvminfo.dataoff = readInt(qvmfile); - qvminfo.datalen = readInt(qvmfile); - qvminfo.litlen = readInt(qvmfile); - qvminfo.bsslen = readInt(qvmfile); + qvminfo.inscount = readInt(input); + qvminfo.codeoff = readInt(input); + qvminfo.codelen = readInt(input); + qvminfo.dataoff = readInt(input); + qvminfo.datalen = readInt(input); + qvminfo.litlen = readInt(input); + qvminfo.bsslen = readInt(input); /* Code segment should follow... */ /* XXX: use fseek with SEEK_CUR? */ - DEBUGTRACE("Searching for .code @ %d from %d\n", qvminfo.codeoff, ftell(qvmfile)); + DEBUGTRACE("Searching for .code @ %d from %d\n", qvminfo.codeoff, input.tellg()); + // rom = (q3vm_rom_t*)(hunk); /* ROM-in-hunk */ rom = (Instruction*)calloc(qvminfo.inscount, sizeof(rom[0])); - while (ftell(qvmfile) < qvminfo.codeoff) - readByte(qvmfile); + while (input.tellg() < qvminfo.codeoff) + readByte(input); while (romSize < qvminfo.inscount) { - n = readByte(qvmfile); + n = readByte(input); w.int4 = 0; if ((i = opcodeParameterSize(n))) { x[0] = x[1] = x[2] = x[3] = 0; - fread(&x, 1, i, qvmfile); + input.readsome((char*)x, 4); w.uint4 = (x[0]) | (x[1] << 8) | (x[2] << 16) | (x[3] << 24); } rom[romSize].Operation = n; rom[romSize].Parameter = w; romSize++; } - DEBUGTRACE("After loading code: at %d, should be %d\n", ftell(qvmfile), qvminfo.codeoff + qvminfo.codelen); + DEBUGTRACE("After loading code: at %d, should be %d\n", input.tellg(), qvminfo.codeoff + qvminfo.codelen); /* Then data segment. */ // ram = hunk + ((romlen + 3) & ~3); /* RAM-in-hunk */ ram = hunk; - DEBUGTRACE("Searching for .data @ %d from %d\n", qvminfo.dataoff, ftell(qvmfile)); - while (ftell(qvmfile) < qvminfo.dataoff) - readByte(qvmfile); + DEBUGTRACE("Searching for .data @ %d from %d\n", qvminfo.dataoff, input.tellg()); + while (input.tellg() < qvminfo.dataoff) + readByte(input); for (n = 0; n < (qvminfo.datalen / sizeof(uint1_t)); n++) { - i = fread(&x, 1, sizeof(x), qvmfile); + i = input.readsome((char*)x, sizeof(x)); w.uint4 = (x[0]) | (x[1] << 8) | (x[2] << 16) | (x[3] << 24); *((word*)(ram + ramSize)) = w; ramSize += sizeof(word); @@ -164,7 +165,7 @@ namespace vm DEBUGTRACE("Loading .lit\n"); for (n = 0; n < (qvminfo.litlen / sizeof(uint1_t)); n++) { - i = fread(&x, 1, sizeof(x), qvmfile); + i = input.readsome((char*)x, sizeof(x)); memcpy(&(w.uint1), &x, sizeof(x)); /* no byte-swapping. */ *((word*)(ram + ramSize)) = w; ramSize += sizeof(word); @@ -187,8 +188,8 @@ namespace vm { int stacksize = 0x10000; dataStack = ramSize - (stacksize / 2); - //returnStack = ramSize; - returnStack = dataStack+4; + returnStack = ramSize; + //returnStack = dataStack+4; RP = returnStack; DP = dataStack; } @@ -201,6 +202,122 @@ namespace vm return 1; } + int VirtualMachine::LoadProgram(std::vector data) + { + /*class vectorwrapbuf : public std::basic_streambuf > + { + public: + vectorwrapbuf(std::vector &vec) { + setg(vec.data(), vec.data(), vec.data() + vec.size()); + } + }; + vectorwrapbuf databuf(data); + std::istream is(&databuf); + return readProgram(is);*/ + std::stringstream ss(std::string(data.begin(), data.end())); + return readProgram((std::istream &)ss); + } + + int VirtualMachine::LoadProgram(char * filename) + { + /*FILE * qvmfile = fopen(filename, "rb"); + qvm_header_t qvminfo; + int i, n; + uint1_t x[4]; + word w; + + DEBUGTRACE("Loading file...\n"); + qvminfo.magic = readInt(qvmfile); + if (qvminfo.magic != QVM_MAGIC) + { + DEBUGTRACE("Invalid magic"); + return 0; + } + DEBUGTRACE("Magic OK\n"); + + qvminfo.inscount = readInt(qvmfile); + qvminfo.codeoff = readInt(qvmfile); + qvminfo.codelen = readInt(qvmfile); + qvminfo.dataoff = readInt(qvmfile); + qvminfo.datalen = readInt(qvmfile); + qvminfo.litlen = readInt(qvmfile); + qvminfo.bsslen = readInt(qvmfile); + + + DEBUGTRACE("Searching for .code @ %d from %d\n", qvminfo.codeoff, ftell(qvmfile)); + + rom = (Instruction*)calloc(qvminfo.inscount, sizeof(rom[0])); + while (ftell(qvmfile) < qvminfo.codeoff) + readByte(qvmfile); + while (romSize < qvminfo.inscount) + { + n = readByte(qvmfile); + w.int4 = 0; + if ((i = opcodeParameterSize(n))) + { + x[0] = x[1] = x[2] = x[3] = 0; + fread(&x, 1, i, qvmfile); + w.uint4 = (x[0]) | (x[1] << 8) | (x[2] << 16) | (x[3] << 24); + } + rom[romSize].Operation = n; + rom[romSize].Parameter = w; + romSize++; + } + DEBUGTRACE("After loading code: at %d, should be %d\n", ftell(qvmfile), qvminfo.codeoff + qvminfo.codelen); + + + ram = hunk; + DEBUGTRACE("Searching for .data @ %d from %d\n", qvminfo.dataoff, ftell(qvmfile)); + while (ftell(qvmfile) < qvminfo.dataoff) + readByte(qvmfile); + for (n = 0; n < (qvminfo.datalen / sizeof(uint1_t)); n++) + { + i = fread(&x, 1, sizeof(x), qvmfile); + w.uint4 = (x[0]) | (x[1] << 8) | (x[2] << 16) | (x[3] << 24); + *((word*)(ram + ramSize)) = w; + ramSize += sizeof(word); + } + + + DEBUGTRACE("Loading .lit\n"); + for (n = 0; n < (qvminfo.litlen / sizeof(uint1_t)); n++) + { + i = fread(&x, 1, sizeof(x), qvmfile); + memcpy(&(w.uint1), &x, sizeof(x)); + *((word*)(ram + ramSize)) = w; + ramSize += sizeof(word); + } + + DEBUGTRACE("Allocating .bss %d (%X) bytes\n", qvminfo.bsslen, qvminfo.bsslen); + ramSize += qvminfo.bsslen; + + hunkFree = hunkSize - ((ramSize * sizeof(uint1_t)) + 4); + + DEBUGTRACE("VM hunk has %d of %d bytes free (RAM = %d B).\n", hunkFree, hunkSize, ramSize); + if (ramSize > hunkSize) + { + throw OutOfMemoryException(); + return 0; + } + + + { + int stacksize = 0x10000; + dataStack = ramSize - (stacksize / 2); + //returnStack = ramSize; + returnStack = dataStack+4; + RP = returnStack; + DP = dataStack; + } + + + PC = romSize + 1; + + ramMask = ramSize; + + return 1;*/ + } + void VirtualMachine::End() { PC = romSize+1; diff --git a/src/virtualmachine/VirtualMachine.h b/src/virtualmachine/VirtualMachine.h index 1f5b088a5..04dd4000a 100644 --- a/src/virtualmachine/VirtualMachine.h +++ b/src/virtualmachine/VirtualMachine.h @@ -117,8 +117,9 @@ namespace vm }; #undef OPDEF - int readByte(FILE *qvmfile); - int readInt(FILE *qvmfile); + int readProgram(std::istream & input); + int readByte(std::istream & input); + int readInt(std::istream & input); int opcodeParameterSize(int opcode); int syscall(int programCounter); @@ -153,6 +154,7 @@ public: virtual ~VirtualMachine(); int LoadProgram(char * filename); + int LoadProgram(std::vector fileData); int Run(); int CallInterpreted(int address); void End();