diff --git a/.gitignore b/.gitignore index b06e864..f5cb59d 100644 --- a/.gitignore +++ b/.gitignore @@ -210,3 +210,9 @@ FakesAssemblies/ GeneratedArtifacts/ _Pvt_Extensions/ ModelManifest.xml + + +*.exe +*.lib +*.dll +SDL-1.2.15/ \ No newline at end of file diff --git a/libvm/build.bat b/libvm/build.bat new file mode 100644 index 0000000..926c3d3 --- /dev/null +++ b/libvm/build.bat @@ -0,0 +1,21 @@ +@echo off +SET PATHSAVE=%PATH% +call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 + +SET INIT = /nologo +SET OPTIONS = /Zp1 + +REM echo Building explink... +REM cl %INIT% explink.c getopt.c %OPTIONS% + +REM echo Building expdump... +REM cl %INIT% expdump.c getopt.c %OPTIONS% + +echo Building emulator... +SET LIBS= /NXCOMPAT /DYNAMICBASE "SDL.lib" "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /DEBUG /MACHINE:X64 /OPT:REF /INCREMENTAL:NO /SUBSYSTEM:CONSOLE /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /OPT:ICF /ERRORREPORT:PROMPT /NOLOGO /TLBID:1 /NODEFAULTLIB:msvcrt.lib +cl %INIT% /I "SDL-1.2.15\include" emulator.c vm.c getopt.c %OPTIONS% /link %LIBS% + +echo Cleaning up... +del *.obj +SET PATH=%PATHSAVE% +echo Done. \ No newline at end of file diff --git a/libvm/emulator.c b/libvm/emulator.c new file mode 100644 index 0000000..7a240f6 --- /dev/null +++ b/libvm/emulator.c @@ -0,0 +1,238 @@ +#define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include "vm.h" +#include "exp.h" + +#if defined(_MSC_VER) +#undef main +#endif + +bool running = true; + +/** +* An assertion the VM does. +* @param assertion If zero, the assertion failed. +* @param msg The message that should be shown when the assertion fails. +*/ +void vm_assert(int assertion, const char *msg) +{ + if (assertion) + return; + printf("Assertion failed: %s\n", msg); + running = false; +} + +/** +* The hosts syscall implementation. +* @param process The process that calls the syscall. +* @param info Additional information for the syscall. Contains arguments and results. +*/ +uint32_t vm_syscall(spu_t *process, cmdinput_t *info) +{ + fprintf(stdout, "SYSCALL [%d]: (%d, %d)\n", info->info, info->input0, info->input1); + if (info->info == 0) + running = false; + return 0; +} + +/** +* The hosts hardware IO implementation. +* @param process The process that wants to do IO. +* @param info Additional information for the HWIO. Contains arguments and results. +*/ +uint32_t vm_hwio(spu_t *process, cmdinput_t *info) +{ + return 0; +} + +spu_t mainCore; + +void dump_vm() +{ + printf( + "%8X %3d %3d %1X [", + mainCore.codePointer, + mainCore.stackPointer, + mainCore.basePointer, + mainCore.flags + ); + + for (int i = 1; i < mainCore.stackPointer; i++) + { + printf(" %d", mainCore.stack[i]); + } + + printf(" ]\n"); +} + +void update_vm() +{ + vm_step_process(&mainCore); + + // dump_vm(); +} + +void update_input(SDL_Event *ev) +{ + switch (ev->type) + { + case SDL_QUIT: + running = false; + break; + } +} + +void initialize_vm() +{ + // Initialize memory + mainCore.memoryBase = 0x00; // Linear memory, start at 0x00 + mainCore.memorySize = 0x4000000; // 64 MB; + mainCore.memory = malloc(mainCore.memorySize); + + // Initialize code execution + mainCore.code = mainCore.memory; + mainCore.codeLength = mainCore.memorySize / sizeof(instruction_t); + + // Setup registers + mainCore.codePointer = 0; + mainCore.stackPointer = 0; + mainCore.basePointer = 0; + mainCore.flags = 0; + + // Clear stack + memset(mainCore.stack, 0, VM_STACKSIZE * sizeof(uint32_t)); +} + +bool load_exp(const char *fileName) +{ + FILE *f = fopen(fileName, "rb"); + if (f == NULL) + { + fprintf(stderr, "Could not open file %s\n", fileName); + return false; + } + + /////////////////////////////////////////////////////////////////////////////// + + expfile_t fileHeader; + + if (fread(&fileHeader, 1, sizeof(expfile_t), f) != sizeof(expfile_t)) + { + fprintf(stderr, "File %s does not contain a valid header.\n", fileName); + return false; + } + if (fileHeader.magicNumber != EXP_MAGIC) + { + fprintf(stderr, "Invalid magic in %s\n", fileName); + return false; + } + if (fileHeader.majorVersion != 1 && fileHeader.minorVersion == 0) + { + fprintf( + stderr, "Invalid version %s: %d.%d\n", + fileName, + fileHeader.majorVersion, fileHeader.minorVersion); + return false; + } + + for (uint32_t i = 0; i < fileHeader.numSections; i++) + { + expsection_t section; + + fseek(f, fileHeader.posSections + i * sizeof(expsection_t), SEEK_SET); + fread(§ion, 1, sizeof(expsection_t), f); + + fseek(f, section.start, SEEK_SET); + + uint8_t *sectionInRam = (uint8_t*)mainCore.memory + section.base; + int len = fread(sectionInRam, 1, section.length, f); + if (len != section.length) + fprintf(stderr, "Read invalid size.\n"); + } + + return true; +} + +int main(int argc, char **argv) +{ + // Required before ANY virtual machine memory operations... + initialize_vm(); + + // Load the EXP file + if (argc > 1) + { + if (!load_exp(argv[1])) { + return 1; + } + } + + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + return 1; + } + atexit(SDL_Quit); + + SDL_Surface *screen = SDL_SetVideoMode(640, 480, 32, SDL_DOUBLEBUF); + SDL_WM_SetCaption("DasOS Virtual Platform", NULL); + + dump_vm(); + + SDL_Event ev; + while (running) + { + while (SDL_PollEvent(&ev)) + { + update_input(&ev); + } + + uint32_t start = SDL_GetTicks(); + + do { + for (int i = 0; i < 50 && running; i++) + { + update_vm(); + } + } while (running && (SDL_GetTicks() - start) <= 32); + + { // copy screen + SDL_LockSurface(screen); + memcpy( + screen->pixels, + (uint8_t*)mainCore.memory + 4096, + screen->h * screen->pitch); + SDL_UnlockSurface(screen); + } + + SDL_Flip(screen); + } + + SDL_WM_SetCaption("DasOS Virtual Platform - STOPPED", NULL); + + running = true; + while (running) + { + while (SDL_PollEvent(&ev)) + { + if (ev.type == SDL_QUIT) + running = false; + else if (ev.type == SDL_KEYDOWN && ev.key.keysym.sym == SDLK_ESCAPE) + running = false; + } + + SDL_Flip(screen); + + SDL_Delay(32); + } + + return 0; +} + +FILE * __cdecl __iob_func(void) +{ + static FILE iob[3]; + iob[0] = *stdin; + iob[1] = *stdout; + iob[2] = *stderr; + return iob; +} \ No newline at end of file diff --git a/libvm/exp.sln b/libvm/exp.sln deleted file mode 100644 index 037eccb..0000000 --- a/libvm/exp.sln +++ /dev/null @@ -1,38 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.24720.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "explink", "explink.vcxproj", "{2A84DC2E-34B9-48A5-8DE5-170C367B471E}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "expdump", "expdump.vcxproj", "{C685C35A-C310-4A9D-AF6F-98CCBD388683}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {2A84DC2E-34B9-48A5-8DE5-170C367B471E}.Debug|x64.ActiveCfg = Debug|x64 - {2A84DC2E-34B9-48A5-8DE5-170C367B471E}.Debug|x64.Build.0 = Debug|x64 - {2A84DC2E-34B9-48A5-8DE5-170C367B471E}.Debug|x86.ActiveCfg = Debug|Win32 - {2A84DC2E-34B9-48A5-8DE5-170C367B471E}.Debug|x86.Build.0 = Debug|Win32 - {2A84DC2E-34B9-48A5-8DE5-170C367B471E}.Release|x64.ActiveCfg = Release|x64 - {2A84DC2E-34B9-48A5-8DE5-170C367B471E}.Release|x64.Build.0 = Release|x64 - {2A84DC2E-34B9-48A5-8DE5-170C367B471E}.Release|x86.ActiveCfg = Release|Win32 - {2A84DC2E-34B9-48A5-8DE5-170C367B471E}.Release|x86.Build.0 = Release|Win32 - {C685C35A-C310-4A9D-AF6F-98CCBD388683}.Debug|x64.ActiveCfg = Debug|x64 - {C685C35A-C310-4A9D-AF6F-98CCBD388683}.Debug|x64.Build.0 = Debug|x64 - {C685C35A-C310-4A9D-AF6F-98CCBD388683}.Debug|x86.ActiveCfg = Debug|Win32 - {C685C35A-C310-4A9D-AF6F-98CCBD388683}.Debug|x86.Build.0 = Debug|Win32 - {C685C35A-C310-4A9D-AF6F-98CCBD388683}.Release|x64.ActiveCfg = Release|x64 - {C685C35A-C310-4A9D-AF6F-98CCBD388683}.Release|x64.Build.0 = Release|x64 - {C685C35A-C310-4A9D-AF6F-98CCBD388683}.Release|x86.ActiveCfg = Release|Win32 - {C685C35A-C310-4A9D-AF6F-98CCBD388683}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/libvm/expdump.c b/libvm/expdump.c index 004175e..2c07634 100644 --- a/libvm/expdump.c +++ b/libvm/expdump.c @@ -125,7 +125,7 @@ void disassemble(instruction_t *list, uint32_t count, uint32_t base, FILE *f) if (knownInstruction != NULL) { fprintf(f, "%s", knownInstruction->name); - if (instr.input0 == VM_INPUT_ARG) + if (instr.argument != 0 || instr.input0 == VM_INPUT_ARG) { if(instr.output == VM_OUTPUT_JUMP || instr.output == VM_OUTPUT_JUMPR) fprintf(f, " 0x%X", instr.argument); @@ -268,6 +268,7 @@ int main(int argc, char **argv) { fprintf( stderr, "Invalid version %s: %d.%d\n", + fileName, fileHeader.majorVersion, fileHeader.minorVersion); continue; } diff --git a/libvm/expdump.vcxproj b/libvm/expdump.vcxproj deleted file mode 100644 index 1562205..0000000 --- a/libvm/expdump.vcxproj +++ /dev/null @@ -1,156 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - {C685C35A-C310-4A9D-AF6F-98CCBD388683} - Win32Proj - expdump - 8.1 - - - - Application - true - v140 - Unicode - - - Application - false - v140 - true - Unicode - - - Application - true - v140 - Unicode - - - Application - false - v140 - true - Unicode - - - - - - - - - - - - - - - - - - - - - true - - - true - - - false - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - - - Console - true - - - - - - - Level3 - Disabled - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) - - - Console - true - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - - - Console - true - true - true - - - - - Level3 - - - MaxSpeed - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - - - Console - true - true - true - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/libvm/expdump.vcxproj.filters b/libvm/expdump.vcxproj.filters deleted file mode 100644 index ad206e3..0000000 --- a/libvm/expdump.vcxproj.filters +++ /dev/null @@ -1,36 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Quelldateien - - - Quelldateien - - - - - Headerdateien - - - Headerdateien - - - Headerdateien - - - \ No newline at end of file diff --git a/libvm/explink.c b/libvm/explink.c index f666a8e..186c7a9 100644 --- a/libvm/explink.c +++ b/libvm/explink.c @@ -28,12 +28,20 @@ int main(int argc, char **argv) char const *codefileName = NULL; char const *datafileName = NULL; int memsize = 65536; + int codeStart = 0; + int dataStart = 0; opterr = 0; int c; - while ((c = getopt(argc, argv, "c:d:m:o:")) != -1) + while ((c = getopt(argc, argv, "C:D:c:d:m:o:")) != -1) { switch (c) { + case 'C': + codeStart = atoi(optarg); + break; + case 'D': + dataStart = atoi(optarg); + break; case 'm': memsize = atoi(optarg); if (memsize <= 0) { @@ -97,12 +105,15 @@ int main(int argc, char **argv) DEBUG_VAL(fileHeader.posSections); - expsection_t codeSection = { 0, 0 }; + expsection_t codeSection = { 0, codeStart }; strcpy(codeSection.name, ".code"); - expsection_t dataSection = { 1, 0 }; + expsection_t dataSection = { 1, dataStart }; strcpy(dataSection.name, ".data"); + DEBUG_VAL(codeSection.base); + DEBUG_VAL(dataSection.base); + if (codefileName != NULL) fwrite(&codeSection, sizeof(expsection_t), 1, f); if (datafileName != NULL) diff --git a/libvm/explink.vcxproj b/libvm/explink.vcxproj deleted file mode 100644 index 8342daa..0000000 --- a/libvm/explink.vcxproj +++ /dev/null @@ -1,159 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - {2A84DC2E-34B9-48A5-8DE5-170C367B471E} - Win32Proj - libvm - 8.1 - explink - - - - Application - true - v140 - Unicode - - - Application - false - v140 - true - Unicode - - - Application - true - v140 - Unicode - - - Application - false - v140 - true - Unicode - - - - - - - - - - - - - - - - - - - - - true - - - true - - - false - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - 1Byte - - - Console - true - - - - - - - Level3 - Disabled - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) - 1Byte - - - Console - true - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - 1Byte - - - Console - true - true - true - - - - - Level3 - - - MaxSpeed - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - 1Byte - - - Console - true - true - true - - - - - - - - - - - - - \ No newline at end of file diff --git a/libvm/explink.vcxproj.filters b/libvm/explink.vcxproj.filters deleted file mode 100644 index 4bece04..0000000 --- a/libvm/explink.vcxproj.filters +++ /dev/null @@ -1,30 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Quelldateien - - - Quelldateien - - - - - Headerdateien - - - \ No newline at end of file diff --git a/libvm/vm.c b/libvm/vm.c new file mode 100644 index 0000000..c27e84b --- /dev/null +++ b/libvm/vm.c @@ -0,0 +1,259 @@ +#include "vm.h" + +#include + +static uint32_t cmd_copy(cmdinput_t *info) +{ + return info->input0; +} + +static uint32_t cmd_load(spu_t *p, cmdinput_t *info) +{ + uint32_t result = 0; + switch(info->info) { + case 2: + result |=(uint32_t)(vm_read_byte(p, info->input0+3))<<24; + result |=(uint32_t)(vm_read_byte(p, info->input0+2))<<16; + case 1: + result |=(uint32_t)(vm_read_byte(p, info->input0+1))<< 8; + case 0: + result |=(uint32_t)(vm_read_byte(p, info->input0+0))<< 0; + break; + } + return result; +} + +static uint32_t cmd_store(spu_t *p, cmdinput_t *info) +{ + switch(info->info) { + case 2: + vm_write_byte(p, info->input0+3, info->input1>>24); + vm_write_byte(p, info->input0+2, info->input1>>16); + case 1: + vm_write_byte(p, info->input0+1, info->input1>>8); + case 0: + vm_write_byte(p, info->input0, info->input1); + break; + } + return info->input1; +} + +static uint32_t cmd_spget(spu_t *p, cmdinput_t *info) +{ + return p->stackPointer; +} + +static uint32_t cmd_spset(spu_t *p, cmdinput_t *info) +{ + return p->stackPointer = info->input0; +} + +static uint32_t cmd_bpget(spu_t *p, cmdinput_t *info) +{ + return p->basePointer; +} + +static uint32_t cmd_bpset(spu_t *p, cmdinput_t *info) +{ + return p->basePointer = info->input0; +} + +static uint32_t cmd_cpget(spu_t *p, cmdinput_t *info) +{ + return p->codePointer + info->info; +} + +static inline int16_t makeSigned(uint16_t val) +{ + return *((int16_t*)&val); +} + +static uint32_t cmd_get(spu_t *p, cmdinput_t *info) +{ + return p->stack[p->basePointer + makeSigned(info->input0)]; +} + +static uint32_t cmd_set(spu_t *p, cmdinput_t *info) +{ + return p->stack[p->basePointer + makeSigned(info->input0)] = info->input1; +} + +static uint32_t cmd_math(cmdinput_t *info) +{ + switch(info->info) + { + // IMPORTANT: + // input1 - input0 because then input0 can be a fixed value +#define S(name, op) case name: return info->input1 op info->input0; break; + S(VM_MATH_ADD, +) + S(VM_MATH_SUB, -) + S(VM_MATH_MUL, *) + S(VM_MATH_DIV, /) + S(VM_MATH_MOD, %) + S(VM_MATH_AND, &) + S(VM_MATH_OR, |) + S(VM_MATH_XOR, ^) +#undef S + case VM_MATH_NOT: return ~info->input0; break; + default: vm_assert(0, "Invalid instruction: MATH command not defined."); return -1; + } +} + +int vm_step_process(spu_t *process) +{ + vm_assert(process != NULL, "process must not be NULL."); + instruction_t instr = process->code[process->codePointer++]; + + int exec = 1; + switch(instr.execZ) + { + case VM_EXEC_X: + /* Don't modify execution. */ + break; + case VM_EXEC_0: + if((process->flags & VM_FLAG_Z) != 0) + exec = 0; + break; + case VM_EXEC_1: + if((process->flags & VM_FLAG_Z) == 0) + exec = 0; + break; + default: + vm_assert(0, "Invalid instruction: execZ undefined."); + break; + } + + switch(instr.execN) + { + case VM_EXEC_X: + /* Don't modify execution. */ + break; + case VM_EXEC_0: + if((process->flags & VM_FLAG_N) != 0) + exec = 0; + break; + case VM_EXEC_1: + if((process->flags & VM_FLAG_N) == 0) + exec = 0; + break; + default: + vm_assert(0, "Invalid instruction: execN undefined."); + break; + } + + // Only do further instruction execution when + // the execution condition is met. + if(exec) + { + cmdinput_t info = { 0 }; + switch(instr.input0) + { + case VM_INPUT_ZERO: info.input0 = 0; break; + case VM_INPUT_POP: info.input0 = vm_pop(process); break; + case VM_INPUT_PEEK: info.input0 = vm_peek(process); break; + case VM_INPUT_ARG: info.input0 = instr.argument; break; + default: vm_assert(0, "Invalid instruction: input0 undefined."); + } + + switch(instr.input1) + { + case VM_INPUT_ZERO: info.input1 = 0; break; + case VM_INPUT_POP: info.input1 = vm_pop(process); break; + default: vm_assert(0, "Invalid instruction: input1 undefined."); + } + + info.info = instr.cmdinfo; + + uint32_t output = -1; + switch(instr.command) + { + case VM_CMD_COPY: output = cmd_copy(&info); break; + case VM_CMD_STORE: output = cmd_store(process, &info); break; + case VM_CMD_LOAD: output = cmd_load(process, &info); break; + case VM_CMD_MATH: output = cmd_math(&info); break; + case VM_CMD_SYSCALL: output = vm_syscall(process, &info); break; + case VM_CMD_HWIO: output = vm_hwio(process, &info); break; + case VM_CMD_SPGET: output = cmd_spget(process, &info); break; + case VM_CMD_SPSET: output = cmd_spset(process, &info); break; + case VM_CMD_BPGET: output = cmd_bpget(process, &info); break; + case VM_CMD_BPSET: output = cmd_bpset(process, &info); break; + case VM_CMD_CPGET: output = cmd_cpget(process, &info); break; + case VM_CMD_GET: output = cmd_get(process, &info); break; + case VM_CMD_SET: output = cmd_set(process, &info); break; + default: vm_assert(0, "Invalid instruction: command undefined."); + } + + switch(instr.flags) + { + case VM_FLAG_YES: + process->flags = 0; + if(output == 0) + process->flags |= VM_FLAG_Z; + else if((output & (1<<31)) != 0) + process->flags |= VM_FLAG_N; + break; + case VM_FLAG_NO: break; + default: + vm_assert(0, "Invalid instruction: invalid flags."); + } + + switch(instr.output) + { + case VM_OUTPUT_DISCARD: break; + case VM_OUTPUT_PUSH: vm_push(process, output); break; + case VM_OUTPUT_JUMP: process->codePointer = output; break; + case VM_OUTPUT_JUMPR: + process->codePointer += *((int32_t*)&output); + break; + default: + vm_assert(0, "Invalid instruction: invalid output."); + } + } + + return process->codePointer < process->codeLength; +} + +void vm_push(spu_t *process, uint32_t value) +{ + vm_assert(process != NULL, "process must not be NULL."); + vm_assert(process->stackPointer < VM_STACKSIZE, "Stack overflow"); + process->stack[++process->stackPointer] = value; +} + +uint32_t vm_pop(spu_t *process) +{ + vm_assert(process != NULL, "process must not be NULL."); + uint32_t psp = process->stackPointer; + uint32_t val = process->stack[process->stackPointer--]; + + // Underflow check works because unsigned overflow is defined ;) + vm_assert(psp >= process->stackPointer, "Stack underflow"); + + return val; +} + +uint32_t vm_peek(spu_t *process) +{ + vm_assert(process != NULL, "process must not be NULL."); + return process->stack[process->stackPointer]; +} + + + + + +uint8_t vm_read_byte(spu_t *process, uint32_t address) +{ + vm_assert(process != NULL, "process must not be NULL."); + address -= process->memoryBase; + vm_assert(address < process->memorySize, "Out of memory."); + return ((uint8_t*)process->memory)[address]; +} + +void vm_write_byte(spu_t *process, uint32_t address, uint8_t value) +{ + vm_assert(process != NULL, "process must not be NULL."); + address -= process->memoryBase; + vm_assert(address < process->memorySize, "Out of memory."); + ((uint8_t*)process->memory)[address] = value; +}