From f5d0f0aca17c133825bc33891bdae7c396eabe07 Mon Sep 17 00:00:00 2001 From: bjcscat Date: Tue, 31 Oct 2023 11:46:53 -0500 Subject: [PATCH 1/3] Bring ZCPU back onto mainline --- data/cpuchip/examples/helloworld.txt | 42 + data/cpuchip/examples/udh_test.txt | 38 + data/cpuchip/lib/drivers/drv_cscr.txt | 195 ++ data/cpuchip/lib/drivers/drv_udh.txt | 126 + data/cpuchip/lib/zcrt/ctype.txt | 272 +++ data/cpuchip/lib/zcrt/init.txt | 49 + data/cpuchip/lib/zcrt/string.txt | 259 ++ data/gpuchip/examples/3d_cube.txt | 66 + data/gpuchip/examples/3d_icosahedron.txt | 79 + data/gpuchip/examples/3d_letter_a.txt | 80 + data/gpuchip/examples/3d_tunnel.txt | 73 + data/gpuchip/examples/bounce.txt | 65 + data/gpuchip/examples/cube.txt | 138 ++ data/gpuchip/examples/foxlogo.txt | 84 + data/gpuchip/examples/hud_engine.txt | 101 + data/gpuchip/examples/hud_fighter.txt | 134 ++ data/gpuchip/examples/line_graph.txt | 62 + data/gpuchip/examples/mt2.txt | 46 + data/gpuchip/examples/mt3.txt | 106 + data/gpuchip/examples/plasma.txt | 116 + data/gpuchip/examples/sprite.txt | 30 + data/gpuchip/examples/stargate.txt | 211 ++ data/gpuchip/examples/table.txt | 75 + data/gpuchip/examples/terrain.txt | 77 + data/gpuchip/examples/texture.txt | 30 + data/gpuchip/examples/trig.txt | 64 + data/gpuchip/examples/verynice1.txt | 54 + data/gpuchip/examples/verynice2.txt | 122 + data/gpuchip/lib/drivers/drv_gl.txt | 502 ++++ data/gpuchip/lib/drivers/drv_gl_toolkit.txt | 122 + data/spuchip/examples/beatbox.txt | 56 + data/spuchip/examples/mario_theme.txt | 156 ++ lua/autorun/cpu_load.lua | 33 + lua/entities/gmod_wire_cpu.lua | 288 +++ lua/entities/gmod_wire_gpu/cl_gpuvm.lua | 2360 +++++++++++++++++++ lua/entities/gmod_wire_gpu/cl_init.lua | 447 ++++ lua/entities/gmod_wire_gpu/init.lua | 394 ++++ lua/entities/gmod_wire_gpu/shared.lua | 12 + lua/entities/gmod_wire_spu/cl_init.lua | 179 ++ lua/entities/gmod_wire_spu/cl_spuvm.lua | 455 ++++ lua/entities/gmod_wire_spu/init.lua | 338 +++ lua/entities/gmod_wire_spu/shared.lua | 10 + lua/wire/client/hlzasm/hc_codetree.lua | 690 ++++++ lua/wire/client/hlzasm/hc_compiler.lua | 872 +++++++ lua/wire/client/hlzasm/hc_expression.lua | 955 ++++++++ lua/wire/client/hlzasm/hc_opcodes.lua | 43 + lua/wire/client/hlzasm/hc_optimize.lua | 136 ++ lua/wire/client/hlzasm/hc_output.lua | 647 +++++ lua/wire/client/hlzasm/hc_preprocess.lua | 164 ++ lua/wire/client/hlzasm/hc_syntax.lua | 1513 ++++++++++++ lua/wire/client/hlzasm/hc_tokenizer.lua | 585 +++++ lua/wire/client/text_editor/modes/zcpu.lua | 325 +++ lua/wire/cpu_default_data_decompressor.lua | 49 + lua/wire/cpu_default_data_files.lua | 1 + lua/wire/cpulib.lua | 1159 +++++++++ lua/wire/stools/cpu.lua | 400 ++++ lua/wire/stools/gpu.lua | 195 ++ lua/wire/stools/spu.lua | 168 ++ lua/wire/zvm/zvm_core.lua | 662 ++++++ lua/wire/zvm/zvm_data.lua | 489 ++++ lua/wire/zvm/zvm_features.lua | 1122 +++++++++ lua/wire/zvm/zvm_opcodes.lua | 1657 +++++++++++++ 62 files changed, 19978 insertions(+) create mode 100644 data/cpuchip/examples/helloworld.txt create mode 100644 data/cpuchip/examples/udh_test.txt create mode 100644 data/cpuchip/lib/drivers/drv_cscr.txt create mode 100644 data/cpuchip/lib/drivers/drv_udh.txt create mode 100644 data/cpuchip/lib/zcrt/ctype.txt create mode 100644 data/cpuchip/lib/zcrt/init.txt create mode 100644 data/cpuchip/lib/zcrt/string.txt create mode 100644 data/gpuchip/examples/3d_cube.txt create mode 100644 data/gpuchip/examples/3d_icosahedron.txt create mode 100644 data/gpuchip/examples/3d_letter_a.txt create mode 100644 data/gpuchip/examples/3d_tunnel.txt create mode 100644 data/gpuchip/examples/bounce.txt create mode 100644 data/gpuchip/examples/cube.txt create mode 100644 data/gpuchip/examples/foxlogo.txt create mode 100644 data/gpuchip/examples/hud_engine.txt create mode 100644 data/gpuchip/examples/hud_fighter.txt create mode 100644 data/gpuchip/examples/line_graph.txt create mode 100644 data/gpuchip/examples/mt2.txt create mode 100644 data/gpuchip/examples/mt3.txt create mode 100644 data/gpuchip/examples/plasma.txt create mode 100644 data/gpuchip/examples/sprite.txt create mode 100644 data/gpuchip/examples/stargate.txt create mode 100644 data/gpuchip/examples/table.txt create mode 100644 data/gpuchip/examples/terrain.txt create mode 100644 data/gpuchip/examples/texture.txt create mode 100644 data/gpuchip/examples/trig.txt create mode 100644 data/gpuchip/examples/verynice1.txt create mode 100644 data/gpuchip/examples/verynice2.txt create mode 100644 data/gpuchip/lib/drivers/drv_gl.txt create mode 100644 data/gpuchip/lib/drivers/drv_gl_toolkit.txt create mode 100644 data/spuchip/examples/beatbox.txt create mode 100644 data/spuchip/examples/mario_theme.txt create mode 100644 lua/autorun/cpu_load.lua create mode 100644 lua/entities/gmod_wire_cpu.lua create mode 100644 lua/entities/gmod_wire_gpu/cl_gpuvm.lua create mode 100644 lua/entities/gmod_wire_gpu/cl_init.lua create mode 100644 lua/entities/gmod_wire_gpu/init.lua create mode 100644 lua/entities/gmod_wire_gpu/shared.lua create mode 100644 lua/entities/gmod_wire_spu/cl_init.lua create mode 100644 lua/entities/gmod_wire_spu/cl_spuvm.lua create mode 100644 lua/entities/gmod_wire_spu/init.lua create mode 100644 lua/entities/gmod_wire_spu/shared.lua create mode 100644 lua/wire/client/hlzasm/hc_codetree.lua create mode 100644 lua/wire/client/hlzasm/hc_compiler.lua create mode 100644 lua/wire/client/hlzasm/hc_expression.lua create mode 100644 lua/wire/client/hlzasm/hc_opcodes.lua create mode 100644 lua/wire/client/hlzasm/hc_optimize.lua create mode 100644 lua/wire/client/hlzasm/hc_output.lua create mode 100644 lua/wire/client/hlzasm/hc_preprocess.lua create mode 100644 lua/wire/client/hlzasm/hc_syntax.lua create mode 100644 lua/wire/client/hlzasm/hc_tokenizer.lua create mode 100644 lua/wire/client/text_editor/modes/zcpu.lua create mode 100644 lua/wire/cpu_default_data_decompressor.lua create mode 100644 lua/wire/cpu_default_data_files.lua create mode 100644 lua/wire/cpulib.lua create mode 100644 lua/wire/stools/cpu.lua create mode 100644 lua/wire/stools/gpu.lua create mode 100644 lua/wire/stools/spu.lua create mode 100644 lua/wire/zvm/zvm_core.lua create mode 100644 lua/wire/zvm/zvm_data.lua create mode 100644 lua/wire/zvm/zvm_features.lua create mode 100644 lua/wire/zvm/zvm_opcodes.lua diff --git a/data/cpuchip/examples/helloworld.txt b/data/cpuchip/examples/helloworld.txt new file mode 100644 index 0000000000..64b994a430 --- /dev/null +++ b/data/cpuchip/examples/helloworld.txt @@ -0,0 +1,42 @@ +//Wired Hello World! +//Connect CPU membus input to console screen +//Connect CPUs CLK input to button (toggle) +//Notice how you can store your +//subroutines/calls in DATA area +jmp _code; +message: + db 'Hello World!',0; +WriteString: //ESI - String pointer, EDX - Param + mov eax,65536; + AWriteLoop: + cmp #esi,0; //Terminate on char 0 + je AEnd; + mov #eax,#esi; //Output char + inc eax; + mov #eax,edx; //Output char param + inc eax; + inc esi; + jmp AWriteLoop; + AEnd: +ret //Return from call + +_code: + mov esi,message; + mov edx,000999; //White foreground on black background + call WriteString; + +//More about colors: +//Lower 3 digits are foreground, +//and higher 3 digits are background +//Each of 3 digits shows amount of +//RED, GREEN, and BLUE (in order) +//Each color has 10 shades - from 0 to 9 +// +//For example, 999044 will be dark yellow (044) on +//a white background (999) +// +//Experiment with colors! +// +//Also, the 7th digit (if its not equal to 0) will +//cause the character to blink by changing foreground and +//background places (actual data in memory wont change) diff --git a/data/cpuchip/examples/udh_test.txt b/data/cpuchip/examples/udh_test.txt new file mode 100644 index 0000000000..085a116be8 --- /dev/null +++ b/data/cpuchip/examples/udh_test.txt @@ -0,0 +1,38 @@ +//------------------------------------------------------------------------------ +// Universal Device Host driver test application +//------------------------------------------------------------------------------ +#pragma CRT ZCRT + +//Include drivers for console screen and device host +#include +#include + +void main() { + float i; + udhSetBusAddress(65536); + + cscrInitialize(0); + + udhQueryDevices(); + + for (i = 0; i < MAX_CONSOLE_SCREENS; i++) { + cscrSelect(i); + cscrSetActive(1); + cscrClear(); + + cscrSetCursor(0,0); + cscrPrintLine("Screen ",930); + cscrPrintNumber(i,930); + } + + cscrSelect(0); + cscrSetCursor(0,2); + cscrPrintLine("UDH driver test\n",039); + for (i = 0; i < 8; i++) { + cscrPrintLine("DEVICE ",999); + cscrPrintNumber(i,999); + cscrPrintLine(": ",999); + cscrPrintLine(udhGetDeviceName(i),666); + cscrPrintLine("\n",999); + } +} diff --git a/data/cpuchip/lib/drivers/drv_cscr.txt b/data/cpuchip/lib/drivers/drv_cscr.txt new file mode 100644 index 0000000000..799d9a54cb --- /dev/null +++ b/data/cpuchip/lib/drivers/drv_cscr.txt @@ -0,0 +1,195 @@ +//------------------------------------------------------------------------------ +// ZCPU standard library and drivers set (C) 2011 by Black Phoenix +// +// UDH-enabled console screen highspeed driver +//------------------------------------------------------------------------------ + +//Define to check if console screen driver is available +#define CSCR_DRIVER + +//Maximum number of console screens supported +#define MAX_CONSOLE_SCREENS 8 + +//Console screen registers +#define CURSOR_RATE 2043 +#define CURSOR_SIZE 2044 +#define CURSOR_POSITION 2045 +#define CURSOR_VISIBLE 2046 +#define LOW_SHIFT_COL 2031 +#define HIGH_SHIFT_COL 2032 +#define LOW_SHIFT_ROW 2033 +#define HIGH_SHIFT_ROW 2034 +#define SHIFT_ROWS 2038 +#define SHIFT_CELLS 2037 +#define CLEAR_SCREEN 2041 +#define BACKGROUND_COLOR 2042 +#define SCREEN_ACTIVE 2047 +#define SCREEN_ROTATION 2024 +#define SCREEN_BRIGHTNESS 2036 + +//Driver data +char* cscrOffsets[MAX_CONSOLE_SCREENS]; +float cscrDevices[MAX_CONSOLE_SCREENS]; +char* cscrCharacterPointer[MAX_CONSOLE_SCREENS]; +float cscrSelectedScreen; + +#ifdef UDH_DRIVER +//Update console screen offsets +void cscrUDHQueryFunction() { + float i,n; + n = udhGetDevices(11,MAX_CONSOLE_SCREENS,cscrDevices); + for (i = 0; i < n; i++) { + cscrOffsets[i] = udhGetDeviceOffset(cscrDevices[i]); + } +} +#endif + +//Initialize console screen driver. screenOffset may be 0 if using UDH +void cscrInitialize(char* screenOffset) { + float i; + + for (i = 0; i < MAX_CONSOLE_SCREENS; i++) { + cscrOffsets[i] = screenOffset; + } + +#ifdef UDH_DRIVER + if (!screenOffset) { + udhRegisterDriver(cscrUDHQueryFunction); + cscrUDHQueryFunction(); + } +#endif + cscrSelectedScreen = 0; +} + +float cscrPresent(float screen) { + return cscrOffsets[cscrSelectedScreen] != 0; +} + +void cscrSelect(float screen) { + cscrSelectedScreen = screen; + max cscrSelectedScreen,0; + min cscrSelectedScreen,MAX_CONSOLE_SCREENS; +} + +void cscrSetActive(float clk) { + if (!cscrOffsets[cscrSelectedScreen]) return; + *(cscrOffsets[cscrSelectedScreen]+SCREEN_ACTIVE) = clk; +} + +void cscrClear() { + if (!cscrOffsets[cscrSelectedScreen]) return; + *(cscrOffsets[cscrSelectedScreen]+CLEAR_SCREEN) = 1; + cscrCharacterPointer[cscrSelectedScreen] = 0; +} + +void cscrSetBackground(float col) { + if (!cscrOffsets[cscrSelectedScreen]) return; + *(cscrOffsets[cscrSelectedScreen]+BACKGROUND_COLOR) = col; +} + +void cscrSetRotation(float rot) { + *(cscrOffsets[cscrSelectedScreen]+SCREEN_ROTATION) = rot; +} + +void cscrSetBrightness(float bright) { + *(cscrOffsets[cscrSelectedScreen]+SCREEN_BRIGHTNESS) = bright; +} + +void cscrLoadImage(char* imgdata) { + if (!cscrOffsets[cscrSelectedScreen]) return; + + preserve ESI,EDI; + ESI = imgdata; + EDI = cscrOffsets[cscrSelectedScreen]; + mcopy 30*18*2; +} + +void cscrPutLine(char* scrptr, float col, char* str) { + if (!cscrOffsets[cscrSelectedScreen]) return; + char* curptr = scrptr; + + while (*str) { + *(cscrOffsets[cscrSelectedScreen]+curptr*2+0) = *str; + *(cscrOffsets[cscrSelectedScreen]+curptr*2+1) = col; + + str++; + curptr++; + } +} + +void cscrPutChar(char* scrptr, float col, char ch) { + if (!cscrOffsets[cscrSelectedScreen]) return; + + *(cscrOffsets[cscrSelectedScreen]+scrptr*2+0) = ch; + *(cscrOffsets[cscrSelectedScreen]+scrptr*2+1) = col; +} + +void cscrNewLine() { + if (!cscrOffsets[cscrSelectedScreen]) return; + + cscrCharacterPointer[cscrSelectedScreen] /= 30; + fint cscrCharacterPointer[cscrSelectedScreen]; + cscrCharacterPointer[cscrSelectedScreen] = (cscrCharacterPointer[cscrSelectedScreen]+1)*30; + + if (cscrCharacterPointer[cscrSelectedScreen] >= 30*18) { + cscrCharacterPointer[cscrSelectedScreen] = cscrCharacterPointer[cscrSelectedScreen] - 30; + *(cscrOffsets[cscrSelectedScreen]+SHIFT_ROWS) = 1; + } +} + +void cscrPrintLine(char* str, float col) { + if (!cscrOffsets[cscrSelectedScreen]) return; + + while (*str) { + if (*str == '\n') { + cscrNewLine(); + str++; + if (*str == 0) return; + } + + *(cscrOffsets[cscrSelectedScreen]+cscrCharacterPointer[cscrSelectedScreen]*2+0) = *str; + *(cscrOffsets[cscrSelectedScreen]+cscrCharacterPointer[cscrSelectedScreen]*2+1) = col; + + cscrCharacterPointer[cscrSelectedScreen]++; + if (cscrCharacterPointer[cscrSelectedScreen] >= 30*18) cscrNewLine(); + str++; + } +} + +void cscrPrintNumber(float num, float col) { + if (!cscrOffsets[cscrSelectedScreen]) return; + + float ndig,a; + a = num; + ndig = 0; + while (a > 0) { + ndig++; + a /= 10; + fint a; + } + max ndig,1; + a = num; + + cscrCharacterPointer[cscrSelectedScreen] = cscrCharacterPointer[cscrSelectedScreen] + ndig; + char* charPtr = cscrCharacterPointer[cscrSelectedScreen] - 1; + while (ndig > 0) { + preserve EDX; + mov EDX,a; + mod EDX,10; + add EDX,48; + + *(cscrOffsets[cscrSelectedScreen]+charPtr*2+0) = EDX; + *(cscrOffsets[cscrSelectedScreen]+charPtr*2+1) = col; + charPtr--; + + a /= 10; + fint a; + + ndig--; + } +} + +void cscrSetCursor(float x, y) { + if (!cscrOffsets[cscrSelectedScreen]) return; + cscrCharacterPointer[cscrSelectedScreen] = x+y*30; +} diff --git a/data/cpuchip/lib/drivers/drv_udh.txt b/data/cpuchip/lib/drivers/drv_udh.txt new file mode 100644 index 0000000000..fd618523c7 --- /dev/null +++ b/data/cpuchip/lib/drivers/drv_udh.txt @@ -0,0 +1,126 @@ +//------------------------------------------------------------------------------ +// ZCPU standard library and drivers set (C) 2011 by Black Phoenix +// +// Universal device host driver. Only supports 8 devices right now +//------------------------------------------------------------------------------ + +#define UDH_DRIVER + +//Maximum number of devices supported +#define MAX_UDH_DEVICES 8 + +//Address range of a single device +#define MAX_UDH_ADDRESS_RANGE 4*1024 + +//Maximum number of drivers that may register with UDH +#define MAX_UDH_DRIVERS 8 + +//Device name/string data +string udhDeviceString0,"None"; +string udhDeviceString1,"Unknown"; +string udhDeviceString2,"Extended bus"; +string udhDeviceString3,"Address bus"; +string udhDeviceString4,"Zyelios CPU"; +string udhDeviceString5,"Zyelios GPU"; +string udhDeviceString6,"Zyelios SPU"; +string udhDeviceString7,"Flash EEPROM"; +string udhDeviceString8,"ROM"; +string udhDeviceString9,"Data bus"; +string udhDeviceString10,"CD Ray"; +string udhDeviceString11,"Console screen"; +string udhDeviceString12,"Digital screen"; +string udhDeviceString13,"Data plug"; +string udhDeviceString14,"Data socket"; +string udhDeviceString15,"Keyboard"; +string udhDeviceString16,"Oscilloscope"; +string udhDeviceString17,"Sound emitter"; +string udhDeviceString18,"Constant value"; +string udhDeviceString19,"Data port"; +string udhDeviceString20,"RAM"; +udhDeviceName: + db udhDeviceString0, udhDeviceString1, udhDeviceString2; + db udhDeviceString3, udhDeviceString4, udhDeviceString5; + db udhDeviceString6, udhDeviceString7, udhDeviceString8; + db udhDeviceString9, udhDeviceString10,udhDeviceString11; + db udhDeviceString12,udhDeviceString13,udhDeviceString14; + db udhDeviceString15,udhDeviceString16,udhDeviceString17; + db udhDeviceString18,udhDeviceString19,udhDeviceString20; + +//Extended bus offset +char* udhBusOffset; + +//List of callbacks to call when querying devices +void* udhQueryCallback[MAX_UDH_DRIVERS]; +float udhQueryCallbackCount = 0; + +float udhSetBusAddress(char* extOffset) { + udhBusOffset = extOffset; + udhQueryDevices(); +} + +void udhQueryDevices() { + float i; + + //Run the query + udhBusOffset[16] = 32+MAX_UDH_DEVICES; + udhBusOffset[17] = 1; + + //Reconfigure all devices + //FIXME: only supports single extended bus right now + for (i = 0; i < 8; i++) { + udhBusOffset[i*2+0] = (4*1024)*i; + udhBusOffset[i*2+1] = (4*1024)*i+((4*1024)-1); + } + + //Update all drivers + for (i = 0; i < udhQueryCallbackCount; i++) { + void* functionPtr = udhQueryCallback[i]; + functionPtr(); + } +} + +void udhRegisterDriver(void* queryDeviceFunction) { + udhQueryCallback[udhQueryCallbackCount] = queryDeviceFunction; + if (udhQueryCallbackCount < MAX_UDH_DRIVERS) udhQueryCallbackCount++; +} + +float udhGetDeviceType(float busIndex) { + return udhBusOffset[32+busIndex]; +} + +float udhGetDeviceOffset(float busIndex) { + return 65536+32+MAX_UDH_DEVICES+udhBusOffset[busIndex*2]; +} + +char* udhGetDeviceName(float busIndex) { + float deviceType = udhGetDeviceType(busIndex); + if ((deviceType >= 0) && (deviceType <= 20)) { + return udhDeviceName[deviceType]; + } else { + return udhDeviceName[1]; + } +} + +void udhSetDeviceOffsetSize(float busIndex, char* offst, char* size) { + udhBusOffset[busIndex*2+0] = offst; + udhBusOffset[busIndex*2+1] = offst+size-1; +} + +float udhGetNumDevices() { + return MAX_UDH_DEVICES; +} + +float udhGetDevices(float type, float maxCount, char* deviceList) { + float i,devPtr,n; + + devPtr = deviceList; + n = 0; + for (i = 0; i < MAX_UDH_DEVICES; i++) { + if ((udhGetDeviceType(i) == type) && (n < maxCount)) { + n++; + *devPtr++ = i; + } + } + + return n; +} diff --git a/data/cpuchip/lib/zcrt/ctype.txt b/data/cpuchip/lib/zcrt/ctype.txt new file mode 100644 index 0000000000..4d3a21e380 --- /dev/null +++ b/data/cpuchip/lib/zcrt/ctype.txt @@ -0,0 +1,272 @@ +//------------------------------------------------------------------------------ +// ZCPU CRT sourcecode (for HL-ZASM compiler) (C) 2011 by Black Phoenix +// +// Character classification functions. +//------------------------------------------------------------------------------ + +#define _CONTROL 1 +#define _SPACE 2 +#define _BLANK 4 +#define _DIGIT 8 +#define _HEX 16 +#define _PUNCT 32 +#define _UPPER 64 +#define _LOWER 128 +#define _GRAPH 256 + +#define _MAXCHARS 0x83 + +//test for alphanumeric character +float isalnum(char c) { + preserve eax; + eax = c; max eax,0; min eax,_MAXCHARS; + eax = __ctype_characters[eax]; + + band eax,_LOWER+_UPPER+_DIGIT; +} + +//test for alphabetic character +float isalpha(char c) { + preserve eax; + eax = c; max eax,0; min eax,_MAXCHARS; + eax = __ctype_characters[eax]; + + band eax,_LOWER+_UPPER; +} + +//test for blank character +float isblank(char c) { + preserve eax; + eax = c; max eax,0; min eax,_MAXCHARS; + eax = __ctype_characters[eax]; + + band eax,_BLANK; +} + +//test for control character +float iscontrol(char c) { + preserve eax; + eax = c; max eax,0; min eax,_MAXCHARS; + eax = __ctype_characters[eax]; + + band eax,_CONTROL; +} + +//test for digit +float isdigit(char c) { + preserve eax; + eax = c; max eax,0; min eax,_MAXCHARS; + eax = __ctype_characters[eax]; + + band eax,_DIGIT; +} + +//test for graphic character, excluding the space character +float isgraph(char c) { + preserve eax; + eax = c; max eax,0; min eax,_MAXCHARS; + eax = __ctype_characters[eax]; + + band eax,_GRAPH; +} + +//test for lowercase character +float islower(char c) { + preserve eax; + eax = c; max eax,0; min eax,_MAXCHARS; + eax = __ctype_characters[eax]; + + band eax,_LOWER; +} + +//test for printable character, including the space character. +float isprint(char c) { + preserve eax; + eax = c; max eax,0; min eax,_MAXCHARS; + eax = __ctype_characters[eax]; + + band eax,_LOWER+_UPPER+_DIGIT+_PUNCT+_BLANK+_GRAPH; +} + +//test for punctuation character +float ispunct(char c) { + preserve eax; + eax = c; max eax,0; min eax,_MAXCHARS; + eax = __ctype_characters[eax]; + + band eax,_PUNCT; +} + +//test for any whitespace character +float isspace(char c) { + preserve eax; + eax = c; max eax,0; min eax,_MAXCHARS; + eax = __ctype_characters[eax]; + + band eax,_SPACE; +} + +//test for uppercase character +float isupper(char c) { + preserve eax; + eax = c; max eax,0; min eax,_MAXCHARS; + eax = __ctype_characters[eax]; + + band eax,_UPPER; +} + +//test for hexadecimal digit. Not locale-specific. +float isxdigit(char c) { + preserve eax; + eax = c; max eax,0; min eax,_MAXCHARS; + eax = __ctype_characters[eax]; + + band eax,_HEX; +} + +//convert character to lowercase +char tolower(char c) { + if (islower(c)) return c - 0x20; + return c; +} + +//convert character to uppercase +char toupper(char c) { + if (isupper(c)) return c + 0x20; + return c; +} + +__ctype_characters: + db _CONTROL, //00 (NUL) + db _CONTROL; //01 (SOH) + db _CONTROL; //02 (STX) + db _CONTROL; //03 (ETX) + db _CONTROL; //04 (EOT) + db _CONTROL; //05 (ENQ) + db _CONTROL; //06 (ACK) + db _CONTROL; //07 (BEL) + db _CONTROL; //08 (BS) + db _SPACE+_CONTROL; //09 (HT) + db _SPACE+_CONTROL; //0A (LF) + db _SPACE+_CONTROL; //0B (VT) + db _SPACE+_CONTROL; //0C (FF) + db _SPACE+_CONTROL; //0D (CR) + db _CONTROL; //0E (SI) + db _CONTROL; //0F (SO) + db _CONTROL; //10 (DLE) + db _CONTROL; //11 (DC1) + db _CONTROL; //12 (DC2) + db _CONTROL; //13 (DC3) + db _CONTROL; //14 (DC4) + db _CONTROL; //15 (NAK) + db _CONTROL; //16 (SYN) + db _CONTROL; //17 (ETB) + db _CONTROL; //18 (CAN) + db _CONTROL; //19 (EM) + db _CONTROL; //1A (SUB) + db _CONTROL; //1B (ESC) + db _CONTROL; //1C (FS) + db _CONTROL; //1D (GS) + db _CONTROL; //1E (RS) + db _CONTROL; //1F (US) + db _SPACE+_BLANK; //20 SPACE + db _PUNCT; //21 ! + db _PUNCT; //22 " + db _PUNCT; //23 # + db _PUNCT; //24 $ + db _PUNCT; //25 % + db _PUNCT; //26 & + db _PUNCT; //27 ' + db _PUNCT; //28 ( + db _PUNCT; //29 ) + db _PUNCT; //2A * + db _PUNCT; //2B + + db _PUNCT; //2C ; + db _PUNCT; //2D - + db _PUNCT; //2E . + db _PUNCT; //2F / + db _DIGIT+_HEX; //30 0 + db _DIGIT+_HEX; //31 1 + db _DIGIT+_HEX; //32 2 + db _DIGIT+_HEX; //33 3 + db _DIGIT+_HEX; //34 4 + db _DIGIT+_HEX; //35 5 + db _DIGIT+_HEX; //36 6 + db _DIGIT+_HEX; //37 7 + db _DIGIT+_HEX; //38 8 + db _DIGIT+_HEX; //39 9 + db _PUNCT; //3A : + db _PUNCT; //3B ; + db _PUNCT; //3C < + db _PUNCT; //3D = + db _PUNCT; //3E > + db _PUNCT; //3F ? + db _PUNCT; //40 @ + db _UPPER+_HEX; //41 A + db _UPPER+_HEX; //42 B + db _UPPER+_HEX; //43 C + db _UPPER+_HEX; //44 D + db _UPPER+_HEX; //45 E + db _UPPER+_HEX; //46 F + db _UPPER; //47 G + db _UPPER; //48 H + db _UPPER; //49 I + db _UPPER; //4A J + db _UPPER; //4B K + db _UPPER; //4C L + db _UPPER; //4D M + db _UPPER; //4E N + db _UPPER; //4F O + db _UPPER; //50 P + db _UPPER; //51 Q + db _UPPER; //52 R + db _UPPER; //53 S + db _UPPER; //54 T + db _UPPER; //55 U + db _UPPER; //56 V + db _UPPER; //57 W + db _UPPER; //58 X + db _UPPER; //59 Y + db _UPPER; //5A Z + db _PUNCT; //5B [ + db _PUNCT; //5C \ + db _PUNCT; //5D ] + db _PUNCT; //5E ^ + db _PUNCT; //5F _ + db _PUNCT; //60 ` + db _LOWER+_HEX; //61 a + db _LOWER+_HEX; //62 b + db _LOWER+_HEX; //63 c + db _LOWER+_HEX; //64 d + db _LOWER+_HEX; //65 e + db _LOWER+_HEX; //66 f + db _LOWER; //67 g + db _LOWER; //68 h + db _LOWER; //69 i + db _LOWER; //6A j + db _LOWER; //6B k + db _LOWER; //6C l + db _LOWER; //6D m + db _LOWER; //6E n + db _LOWER; //6F o + db _LOWER; //70 p + db _LOWER; //71 q + db _LOWER; //72 r + db _LOWER; //73 s + db _LOWER; //74 t + db _LOWER; //75 u + db _LOWER; //76 v + db _LOWER; //77 w + db _LOWER; //78 x + db _LOWER; //79 y + db _LOWER; //7A z + db _PUNCT; //7B { + db _PUNCT; //7C | + db _PUNCT; //7D } + db _PUNCT; //7E ~ + db _CONTROL; //7F (DEL) + + db _GRAPH; //80 + db _GRAPH; //81 + db _GRAPH; //82 + db _GRAPH; //83 diff --git a/data/cpuchip/lib/zcrt/init.txt b/data/cpuchip/lib/zcrt/init.txt new file mode 100644 index 0000000000..c00ade7436 --- /dev/null +++ b/data/cpuchip/lib/zcrt/init.txt @@ -0,0 +1,49 @@ +//------------------------------------------------------------------------------ +// ZCPU CRT sourcecode (for HL-ZASM compiler) (C) 2011 by Black Phoenix +// +// C runtime library initialization +//------------------------------------------------------------------------------ + +#ifdef ZCRT_EXTENDED_MODE + // Initialize extended mode + mov edi,&zcrtInterruptTable; //Need "&" because array is defined below + mov esi,&zcrtInterruptTable; add esi,1024; + @InitTable: + mov #edi,zcrtErrorHandler; inc edi; + mov #edi,0; inc edi; + mov #edi,0; inc edi; + mov #edi,32; inc edi; + cmp edi,esi; + jl @InitTable; + + lidtr zcrtInterruptTable; + stef; +#endif + +// Call main function +main(); + +// Stop the processor execution +#ifdef ZCRT_EXTENDED_MODE + clef; +#endif +int 1; + +//------------------------------------------------------------------------------ +// Allocate the interrupt table +#ifdef ZCRT_EXTENDED_MODE + float zcrtInterruptTable[1024]; + char* zcrtInterruptEntrypoint; + + // Default interrupt handlers + zcrtErrorHandler: + //Execute handler if required + if (zcrtInterruptEntrypoint) { + float errorNo,errorCode; + cpuget errorNo,28; + cpuget errorCode,27; + + zcrtInterruptEntrypoint(errorNo,errorCode); + } + iret +#endif diff --git a/data/cpuchip/lib/zcrt/string.txt b/data/cpuchip/lib/zcrt/string.txt new file mode 100644 index 0000000000..da6b31e4b4 --- /dev/null +++ b/data/cpuchip/lib/zcrt/string.txt @@ -0,0 +1,259 @@ +//------------------------------------------------------------------------------ +// ZCPU CRT sourcecode (for HL-ZASM compiler) (C) 2011 by Black Phoenix +// +// String library. Contains functions to work with C strings (C89-compatible) +//------------------------------------------------------------------------------ + +#define NULL 0 + +//copies n bytes between two memory areas; if there is overlap, the behavior is undefined +void *memcpy(void *dest, void *src, float n) { + preserve esi,edi; + register float rem; + + esi = src; + edi = dest; + rem = n; + while (rem) { + register float count = rem; + min count,8192; + mcopy count; + rem = rem - count; + } + return dest; +} + +//copies n bytes between two memory areas; unlike with memcpy the areas may overlap +//void *memmove(void *dest, void *src, float n); +#define memmove memcpy + +//returns a pointer to the first occurrence of c in the first n bytes of s, or NULL if not found +void* memchr(void *s, float c, float n) { + register void *r = s; + register float rem = n; + + while (rem) { + if (*r == c) { + return r; + } + ++r; + --rem; + } + + return NULL; +} + +//compares the first n bytes of two memory areas +//int memcmp(const void *s1, const void *s2, float n); +#define memcmp strcmp + +//overwrites a memory area with n copies of c +void* memset(void *ptr, float c, float n) { + register void *p = ptr; + register float rem = n; + register float ch = c; + + while (rem) { + *p++ = ch; + --rem; + } + + return ptr; +} + +//appends the string src to dest +char* strcat(char *src, *dest) { + register char *srcptr, *destptr; + + srcptr = src; + while (*++srcptr) ; + + destptr = dest; + while (*srcptr++ = *destptr++) ; + return src; +} + +//appends at most n bytes of the string src to dest +char* strncat(char *src, *dest, float n) { + register char *srcptr, *destptr; + register float i; + + srcptr = src; + srcptr--; + while (*++srcptr) ; + + destptr = dest; + i = n; + while (i--) { + if (*srcptr++ = *destptr++) continue; + } + *srcptr = 0; + return src; +} + +//locates character c in a string, searching from the beginning +char* strchr(char *str, c) { + register char *strptr, ch; + strptr = str; + ch = c; + while(*strptr) { + if (*strptr == ch) return strptr; + ++strptr; + } + return 0; +} + +//locates character c in a string, searching from the end +char* strrchr(char *str, c) { + register char *strptr, ch; + register char *findptr; + + findptr = 0; + strptr = str; + ch = c; + while (*strptr) { + if (*strptr == ch) findptr = strptr; + ++strptr; + } + return findptr; +} + +//compares two strings lexicographically +float strcmp(char *src, *dest) { + register char *srcptr, *destptr; + + srcptr = src; + destptr = dest; + while (*srcptr == *destptr) { + if (*srcptr == 0) return 0; + ++srcptr; ++destptr; + } + return (*srcptr - *destptr); +} + +//compares up to the first n bytes of two strings lexicographically +float strncmp(char *src, *dest, float n) { + register char *srcptr, *destptr; + register float i; + + srcptr = src; + destptr = dest; + i = n; + + while (i && (*srcptr == *destptr)) { + if (*srcptr == 0) return 0; + ++srcptr; ++destptr; --i; + } + if (i) return (*srcptr - *destptr); + return 0; +} + +//copies a string from one location to another +char* strcpy(char *dest, *src) { + register char *srcptr, *destptr; + + destptr = dest; + srcptr = src; + while (*destptr++ = *srcptr++) ; + return dest; +} + + +//write exactly n bytes to dest, copying from src or add 0's +char* strncpy(char *dest, *src, float n) { + register char *srcptr, *destptr; + register float i; + + destptr = dest; + srcptr = src; + i = n; + + while (i-- > 0) { + if (*destptr++ = *srcptr++) continue; + while (i-- > 0) *destptr++ = 0; + } + *destptr = 0; + return dest; +} + +//returns the string representation of an error number e.g. errno +//char *strerror(int); + +//finds the length of a C string +float strlen(char* str) { + register char* strptr; + register float n; + + strptr = str; + n = 0; + while (*strptr++) n++; + return n; +} + +//determines the length of the maximal initial substring consisting entirely of characters in accept +float strspn(char *str, *accept) { + register char *s = str; + register char *p = accept; + + while (*p) { + if (*p++ == *s) { + ++s; + p = accept; + } + } + return s - str; +} + +//determines the length of the maximal initial substring consisting entirely of characters not in reject +float strcspn(char *str, char *reject) { + register char *s, *p; + + for (s=str; *s; s++) { + for (p=reject; *p; p++) { + if (*p == *s) goto done; + } + } + done: + return s - str; +} + +//finds the first occurrence of any character in accept +char* strpbrk(char *str, char *accept) { + register char *s; + register char *p; + + for (s=str; *s; s++) { + for (p=accept; *p; p++) { + if (*p == *s) return s; + } + } + return NULL; +} + +//finds the first occurrence of the string "needle" in the longer string "haystack" +char *strstr(char *haystack, char *needle) { + register char *s = haystack; + register char *p = needle; + + while (1) { + if (!*p) { + return haystack; + } + if (*p == *s) { + ++p; + ++s; + } else { + p = needle; + if (!*s) { + return NULL; + } + s = ++haystack; + } + } +} + +//parses a string into a sequence of tokens; non-thread safe in the spec, non-reentrant +//char *strtok(char *, const char * delim); + +//transforms src into a collating form, such that the numerical sort order of the transformed string is equivalent to the collating order of src +//float strxfrm(char *dest, const char *src, float n); diff --git a/data/gpuchip/examples/3d_cube.txt b/data/gpuchip/examples/3d_cube.txt new file mode 100644 index 0000000000..bd2324f879 --- /dev/null +++ b/data/gpuchip/examples/3d_cube.txt @@ -0,0 +1,66 @@ +// Author: Drunkie +// Description: 3D Cube + +Main(); + +#include + +void Main() +{ + glClear( 0, 0, 0 ); // Clear screen + + glCoordPipe( GL_CPIPE_N1_1 ); // Set coordinate pipe to [-1 to 1] mode + glVertexPipe( GL_VPIPE_XYZTRANSFORM ); // Set vertex pipe to xyz transformation + + glLightPos( 0, 0, -20 ); // Set the light position + glLightColor( 255, 255, 255, 1 ); // Set the light color + + glLookAt( + 0, 0, -5, // Camera pos + 0, 0, 0, // Camera target + 0, 1, 0 // Camera up + ); + + // Create variable to hold curtime + float time; + timer time; + + // Create perspective and matrix transformations + glPerspective( 30, 1, 1, 20 ); // FOV, ASPECT RATIO, ZNEAR, ZFAR + glRotate( 1, 1, 0, time ); // AXIS X, Y, Z, ANGLE W + glTranslate( 0, 0, 0 ); // TRANSLATION X, Y, Z + glScale( 1, 1, 1 ); // SCALE X, Y, Z + + glEnable( GL_VERTEX_BUFFER ); // Enable vertex buffer + glEnable( GL_VERTEX_ZSORT ); // Enable Z sorting + + // Solid 3D polygon + glFillMode( GL_FILL_SOLID ); // Set fillmode as solid + glColor4( 100, 149, 237, 180 ); // Set draw color with alpha + glPoly3D( vertexBuffer, 12 ); // Draw 3D polygon + glFlush(); // Flush the vertex buffer to the screen + + // Wireframe 3D polygon + glLineWidth( 1 ); // Set line width + glFillMode( GL_FILL_WIREFRAME ); // Set fillmode as solid + glColor4( 255, 255, 255, 255 ); // Set draw color with alpha + glPoly3D( vertexBuffer, 12 ); // Draw 3D polygon + glFlush(); // Flush the vertex buffer to the screen + + glExit(); // Exit +} + +// The vertex data for our model +vertexBuffer: +db -1,-1,-1; db 1,-1,-1; db 1,1,-1; +db -1,-1,-1; db 1,1,-1; db -1,1,-1; +db 1,-1,1; db -1,-1,1; db 1,1,1; +db -1,-1,1; db -1,1,1; db 1,1,1; +db 1,-1,-1; db -1,-1,-1; db 1,-1,1; +db -1,-1,-1; db -1,-1,1; db 1,-1,1; +db -1,1,-1; db 1,1,-1; db 1,1,1; +db -1,1,1; db -1,1,-1; db 1,1,1; +db -1,-1,-1; db -1,1,-1; db -1,1,1; +db -1,-1,1; db -1,-1,-1; db -1,1,1; +db 1,1,-1; db 1,-1,-1; db 1,1,1; +db 1,-1,-1; db 1,-1,1; db 1,1,1; diff --git a/data/gpuchip/examples/3d_icosahedron.txt b/data/gpuchip/examples/3d_icosahedron.txt new file mode 100644 index 0000000000..0c5041d6ef --- /dev/null +++ b/data/gpuchip/examples/3d_icosahedron.txt @@ -0,0 +1,79 @@ +// Author: Drunkie +// Description: Draws a 3D icosahedron model (solid and wireframe) + +Main(); + +#include + +void Main() +{ + glSleep( 40 ); // Sleep for 40 milliseconds (Reduces fps lag) + glClear( 0, 0, 0 ); // Clear screen + + glCoordPipe( GL_CPIPE_N1_1 ); // Set coordinate pipe to [-1 to 1] mode + glVertexPipe( GL_VPIPE_XYZTRANSFORM ); // Set vertex pipe to xyz transformation + + glLightPos( 0, 0, -50 ); // Set the light position + glLightColor( 255, 255, 255, 1 ); // Set the light color + + glLookAt( + 0, 0, -2.25, // Camera pos + 0, 0, 0, // Camera target + 0, 1, 0 // Camera up + ); + + // Create variable to hold curtime + float time; + timer time; + + // Create perspective and matrix transformations + glPerspective( 30, 1, 1, 20 ); // FOV, ASPECT RATIO, ZNEAR, ZFAR + glRotate( 1, 1, 0, time ); // AXIS X, Y, Z, ANGLE W + glTranslate( 0, 0, 0 ); // TRANSLATION X, Y, Z + glScale( 1, 1, 1, 0 ); // SCALE X, Y, Z + + glEnable( GL_VERTEX_BUFFER ); // Enable vertex buffer + glEnable( GL_VERTEX_ZSORT ); // Enable Z sorting + glEnable( GL_VERTEX_LIGHTING ); // Enable vertex lighting + //glEnable( GL_VERTEX_CULLING ); // Enable face culling + + // Solid 3D polygon + glFillMode( GL_FILL_SOLID ); // Set fillmode as solid + glColor4( 100, 149, 237, 180 ); // Set the draw color with alpha + glPoly3D( vertexBuffer, 20 ); // Draw 3D polygon + glFlush(); // Flush the vertex buffer to the screen + + glDisable( GL_VERTEX_LIGHTING ); // Enable vertex lighting + + // Wireframe 3D polygon + glLineWidth( 1 ); // Set line width of wireframe + glFillMode( GL_FILL_WIREFRAME ); // Set fillmode as wireframe + glColor4( 255, 255, 255, 255 ); // Set the draw color with alpha + glPoly3D( vertexBuffer, 20 ); // Draw 3D polygon + glFlush(); // Flush the vertex buffer to the screen + + glExit(); // Exit +} + +// The vertex data for our model +vertexBuffer: +db 0,0,1; db 0,0.9,0.5; db 0.9,0.3,0.4; +db 0,0,1; db -0.9,0.3,0.4; db 0,0.9,0.5; +db 0,0,1; db -0.5,-0.7,0.4; db -0.9,0.3,0.4; +db 0,0,1; db 0.5,-0.7,0.4; db -0.5,-0.7,0.4; +db 0,0,1; db 0.9,0.3,0.4; db 0.5,-0.7,0.4; +db 0.9,-0.3,-0.4; db 0.9,0.3,0.4; db 0.5,0.7,-0.4; +db 0,0.9,0.5; db 0.5,0.7,-0.4; db 0.9,0.3,0.4; +db 0,0.9,0.5; db -0.5,0.7,-0.4; db 0.5,0.7,-0.4; +db 0,0.9,0.5; db -0.9,0.3,0.4; db -0.5,0.7,-0.4; +db -0.9,-0.3,-0.4; db -0.5,0.7,-0.4; db -0.9,0.3,0.4; +db -0.9,-0.3,-0.4; db -0.9,0.3,0.4; db -0.5,-0.7,0.4; +db -0.9,-0.3,-0.4; db -0.5,-0.7,0.4; db 0,-0.9,-0.5; +db 0.5,-0.7,0.4; db 0,-0.9,-0.5; db -0.5,-0.7,0.4; +db 0.5,-0.7,0.4; db 0.9,-0.3,-0.4; db 0,-0.9,-0.5; +db 0.5,-0.7,0.4; db 0.9,0.3,0.4; db 0.9,-0.3,-0.4; +db 0,0,-1; db 0,-0.9,-0.5; db 0.9,-0.3,-0.4; +db 0,0,-1; db 0.9,-0.3,-0.4; db 0.5,0.7,-0.4; +db 0,0,-1; db 0.5,0.7,-0.4 db -0.5,0.7,-0.4; +db 0,0,-1; db -0.5,0.7,-0.4; db -0.9,-0.3,-0.4; +db 0,0,-1; db -0.9,-0.3,-0.4; db 0,-0.9,-0.5; diff --git a/data/gpuchip/examples/3d_letter_a.txt b/data/gpuchip/examples/3d_letter_a.txt new file mode 100644 index 0000000000..01d61060f0 --- /dev/null +++ b/data/gpuchip/examples/3d_letter_a.txt @@ -0,0 +1,80 @@ +// Author: Drunkie +// Description: Draws a 3D model of the letter A + +Main(); + +#include + +void Main() +{ + glSleep( 40 ); // Sleep for 40 milliseconds (reduces fps lag) + glClear( 100, 149, 237 ); // Clear screen + + glCoordPipe( GL_CPIPE_N1_1 ); // Set coordinate pipe to [-1 to 1] mode + glVertexPipe( GL_VPIPE_XYZTRANSFORM ); // Set vertex pipe to xyz transformation + + glLightPos( 0, 0, -50 ); // Set the light position + glLightColor( 255, 255, 255, 1 ); // Set the light color + + glLookAt( + 0, 0, -2.8, // Camera pos + 0, 0, 0, // Camera target + 0, 1, 0 // Camera up + ); + + // Create variable to hold curtime + float time; + timer time; + + // Create perspective and matrix transformations + glPerspective( 30, 1, 1, 20 ); // FOV, ASPECT RATIO, ZNEAR, ZFAR + glRotate( 0, 1, 0, time ); // AXIS X, Y, Z, ANGLE W + glTranslate( 0, -0.1, 0, 0 ); // TRANSLATION X, Y, Z + glScale( 1, 1, 1, 0 ); // SCALE X, Y, Z + + glEnable( GL_VERTEX_BUFFER ); // Enable vertex buffer + glEnable( GL_VERTEX_ZSORT ); // Enable Z sorting + glEnable( GL_VERTEX_LIGHTING ); // Enable vertex lighting + glEnable( GL_VERTEX_CULLING ); // Enable face culling + + // Solid 3D polygon + glFillMode( GL_FILL_SOLID ); // Set fillmode as solid + glColor4( 255, 255, 255, 255 ); // Set draw color with alpha + glPoly3D( vertexBuffer, 30 ); // Draw 3D polygon + glFlush(); // Flush the vertex buffer to the screen + + glExit(); // Exit +} + +// The vertex data for our model +vertexBuffer: +db 1,1,0; db 0.75,1,0; db 0.25,-1,0; +db 0.75,1,0; db 0,-1,0; db 0.25,-1,0; +db -1,1,0; db -0.25,-1,0; db -0.75,1,0; +db -0.75,1,0; db -0.25,-1,0; db 0,-1,0; +db 1,1,0.25; db 0.25,-1,0.25; db 0.75,1,0.25; +db 0.75,1,0.25; db 0.25,-1,0.25; db 0,-1,0.25; +db -1,1,0.25; db -0.75,1,0.25; db -0.25,-1,0.25; +db -0.75,1,0.25; db 0,-1,0.25; db -0.25,-1,0.25; +db 0.25,-1,0; db -0.25,-1,0; db 0.25,-1,0.25; +db -0.25,-1,0; db -0.25,-1,0.25; db 0.25,-1,0.25; +db -1,1,0; db -1,1,0.25; db -0.25,-1,0; +db -1,1,0.25; db -0.25,-1,0.25; db -0.25,-1,0; +db 1,1,0.25; db 1,1,0; db 0.25,-1,0; +db 1,1,0.25; db 0.25,-1,0; db 0.25,-1,0.25; +db -0.75,1,0; db 0,-1,0; db -0.75,1,0.25; +db -0.75,1,0.25; db 0,-1,0; db 0,-1,0.25; +db 0.75,1,0; db 0.75,1,0.25; db 0,-1,0; +db 0.75,1,0.25; db 0,-1,0.25; db 0,-1,0; +db -0.47,0.25,0; db -0.38,0.01,0; db 0.38,0.01,0; +db 0.38,0.01,0; db 0.47,0.25,0; db -0.47,0.25,0; +db -0.47,0.25,0.25; db 0.38,0.01,0.25; db -0.38,0.01,0.25; +db 0.38,0.01,0.25; db -0.47,0.25,0.25; db 0.47,0.25,0.25; +db -0.38,0.01,0; db -0.38,0.01,0.25; db 0.38,0.01,0; +db 0.38,0.01,0; db -0.38,0.01,0.25; db 0.38,0.01,0.25; +db -0.47,0.25,0; db 0.47,0.25,0; db -0.47,0.25,0.25; +db -0.47,0.25,0.25; db 0.47,0.25,0; db 0.47,0.25,0.25; +db -1,1,0; db -0.75,1,0; db -1,1,0.25; +db -0.75,1,0; db -0.75,1,0.25; db -1,1,0.25; +db 1,1,0; db 1,1,0.25; db 0.75,1,0; +db 0.75,1,0; db 1,1,0.25; db 0.75,1,0.25; diff --git a/data/gpuchip/examples/3d_tunnel.txt b/data/gpuchip/examples/3d_tunnel.txt new file mode 100644 index 0000000000..394947937f --- /dev/null +++ b/data/gpuchip/examples/3d_tunnel.txt @@ -0,0 +1,73 @@ +// Author: Drunkie +// Description: Draws a never ending tunnel in 3D! + +Main(); + +#include + +void Main() +{ + glSleep( 60 ); // Sleep for 60 milliseconds (reduces fps lag) + glClear( 0, 0, 0 ); // Clear screen + + glCoordPipe( GL_CPIPE_N1_1 ); // Set coordinate pipe to [-1 to 1] mode + glVertexPipe( GL_VPIPE_XYZTRANSFORM ); // Set vertex pipe to xyz transformation + + glLightPos( -1, -1, -1 ); // Set the light position + glLightColor( 255, 255, 255, 1.25 ); // Set the light color + + glLookAt( + 0, 0, -25, // Camera pos + 0, 0, 0, // Camera target + 0, 1, 0 // Camera up + ); + + // Loop and draw 4 models + for (i = 0; i < 4; i++) + { + // Set translations for each model + timer zTranslate; + zTranslate *= -16; + mod zTranslate,16; + zTranslate += (i * 16); + + // Create perspective and matrix transformations + glPerspective( 8, 1, 0.4, 20 ); // FOV, ASPECT RATIO, ZNEAR, ZFAR + glRotate( 0, 0, 0, 0 ); // AXIS X, Y, Z, ANGLE W + glTranslate( 0, 0, zTranslate ); // TRANSLATION X, Y, Z + glScale( 1.2, 1, 8 ); // SCALE X, Y, Z + + glEnable( GL_VERTEX_BUFFER ); // Enable vertex buffer + glEnable( GL_VERTEX_ZSORT ); // Enable Z sorting + glEnable( GL_VERTEX_LIGHTING ); // Enable vertex lighting + glEnable( GL_VERTEX_CULLING ); // Enable face culling + + // Solid 3D polygon + glFillMode( GL_FILL_SOLID ); // Set fillmode as solid + glColor4( 255, 255, 255, 150 ); // Set the draw color with alpha + glPoly3D( VertexBuffer, 12 ); // Draw 3D polygon + glFlush(); // Send our vertex buffer to screen + + // Wireframe 3D polygon + glLineWidth( 1 ); // Set line width of wireframe + glFillMode( GL_FILL_WIREFRAME ); // Set fillmode to wireframe + glColor4( 255, 255, 255, 255 ); // Set the draw color with alpha + glPoly3D( vertexBuffer, 8 ); // Draw 3D polygon + glFlush(); // Send our vertex buffer to screen + } + + glExit(); // Exit +} + +float i; +float zTranslate; + +vertexBuffer: +db 1,1,-1; db -1,1,-1; db 1,1,1; +db -1,1,-1; db -1,1,1; db 1,1,1; +db 1,1,-1; db 1,1,1; db 1,-1,-1; +db 1,-1,-1; db 1,1,1; db 1,-1,1; +db -1,1,-1; db -1,-1,-1; db -1,1,1; +db -1,-1,-1; db -1,-1,1; db -1,1,1; +db 1,-1,-1; db 1,-1,1; db -1,-1,-1; +db -1,-1,-1; db 1,-1,1; db -1,-1,1; diff --git a/data/gpuchip/examples/bounce.txt b/data/gpuchip/examples/bounce.txt new file mode 100644 index 0000000000..fe04d50aa4 --- /dev/null +++ b/data/gpuchip/examples/bounce.txt @@ -0,0 +1,65 @@ +////////////////////////////////// +// BOUNCING BALL GPU EXAMPLE // +////////////////////////////////// +dentrypoint 0,_draw; // Set draw start entrypoint to "_draw" + // +rand #ball.x; // Set random ball start point +rand #ball.y; // + // +dexit; // Exit the initialization routine... +////////////////////////////////// +_draw: // Entrypoint for the drawing function + // +dcvxpipe 2; // Set coordinate pipe to 2 (to use coordinates 0...1) +dclrscr bg_color; // Clear screen with background color + // +dmuldt eax,#d.x; // EAX = Direction Vector * Delta (change of coords per frame) +add #ball.x,eax; // Move the ball +dmuldt eax,#d.y; // +add #ball.y,eax; // + // +cmp #ball.x,0.9; // Check hits against walls +cge bounce.x; // Call bounce routine... +cmp #ball.x,0.0; // +cle bounce.x; // + // +cmp #ball.y,0.9; // Bounce on other axis +cge bounce.y; // +cmp #ball.y,0.0; // +cle bounce.y; // + // +dcolor ball_color; // Set color to color of ball +drectwh ball,ball_wh; // Draw the ball + // +dsetsize 24; // Set font size +dwrite textpos,text; + // +dexit; // Exit the draw function +////////////////////////////////// +bounce.x: // Bounce function (change X speed) + neg #d.x; // + min #ball.x,0.9; // + max #ball.x,0.0; // +ret // + // +bounce.y: // Bounce function (change Y speed) + neg #d.y; // + min #ball.y,0.9; // + max #ball.y,0.0; // +ret // +////////////////////////////////// +// Data and resources // +////////////////////////////////// + // +color ball_color,255,255,255; // Ball color (white) +color bg_color, 64, 32,128; // Background color (neon violet) + // +vector2f ball; // Ball position +vector2f ball_wh,0.1,0.1; // Ball width/height + // +vector2f textpos,0.1,0.1; // Text position + // +vector2f d,1.0,1.0; // Movement direction & speed + // +string text,'Bouncing ball!'; // "Bouncing ball!" +////////////////////////////////// diff --git a/data/gpuchip/examples/cube.txt b/data/gpuchip/examples/cube.txt new file mode 100644 index 0000000000..959c09c022 --- /dev/null +++ b/data/gpuchip/examples/cube.txt @@ -0,0 +1,138 @@ +//timer EAX;// div EAX,8; +//fsin EAX,EAX; +//mul EAX,512; +//fabs EAX,EAX; +//neg EAX; +//add EAX,512; + +dcvxpipe 3; //-1..1 (opengl screen) +dvxpipe 5; //matrix projection + +//Initialize transform +mperspective mProjectionMatrix,vPerspective; + +//Render starts +dclrscr bg_color; +mlookat mViewMatrix,vLookAt; //View matrix + +timer eax; +mov #vRotate.w,eax; + +//Rotate translate +mrotate mRotateMatrix,vRotate; +mtranslate mTranslateMatrix,vTranslate; + +//Create model matrix +mmov mModelMatrix,mTranslateMatrix; +mmul mModelMatrix,mRotateMatrix; + +//modelViewMatrix = ViewMatrix * modelMatrx +mmov mModelViewMatrix,mViewMatrix; +mmul mModelViewMatrix,mModelMatrix; + +//load matrix +mload mModelViewMatrix; +mloadproj mProjectionMatrix; + +//setup light +dsetlight 0,lightdata; + +//setup buffer +denable 0; //Vertex buffer +denable 1; //ZSorting +denable 2; //Lighting +denable 3; //Face culling + +//render cube +dcolor fg_color; +dvxdata_3f cube2,12; +dvxflush; + +ddisable 0; //Disable everything! +ddisable 1; +ddisable 2; +ddisable 3; + +dcvxpipe 0; +dvxpipe 0; + +//You can write some text here now +// +dexit; + +//======== +cube2: +db -1,-1,-1; +db 1,-1,-1; +db 1,1,-1; +cube3: +db -1,-1,-1; +db 1,1,-1; +db -1,1,-1; +cube4: +db 1,-1,1; +db -1,-1,1; +db 1,1,1; +cube5: +db -1,-1,1; +db -1,1,1; +db 1,1,1; +cube6: +db 1,-1,-1; +db -1,-1,-1; +db 1,-1,1; +cube7: +db -1,-1,-1; +db -1,-1,1; +db 1,-1,1; +cube8: +db -1,1,-1; +db 1,1,-1; +db 1,1,1; +cube9: +db -1,1,1; +db -1,1,-1; +db 1,1,1; +cube10: +db -1,-1,-1; +db -1,1,-1; +db -1,1,1; +cube11: +db -1,-1,1; +db -1,-1,-1; +db -1,1,1; +cube12: +db 1,1,-1; +db 1,-1,-1; +db 1,1,1; +cube13: +db 1,-1,-1; +db 1,-1,1; +db 1,1,1; + +lightdata: +vector4f lightpos, 0,50,-50, 0; //x y z +color lightcol,255,255,255, 1; //R G B Brightness +//======== + +matrix mRotateMatrix; +matrix mTranslateMatrix; + +matrix mProjectionMatrix; //This defines our projection to screen +matrix mViewMatrix; //This defines our camera transformations + +matrix mModelMatrix; //This is our model transformations +matrix mModelViewMatrix; //This is our model relatively to camera transform + + +vector4f vRotate, 1, 1, 1, 0; // +vector4f vTranslate, 0, 0, 0, 0; // <0> +vector4f vPerspective, 30, 1.6, 1, 20; // + +vLookAt: +vector3f vLookAt_Eye, 0, 0, -5; //Where our camera is +vector3f vLookAt_Center, 0, 0, 0; //What we look at +vector3f vLookAt_Up, 0, 1, 0; //Where our matt-hat is + +color fg_color,255,255,25; +color bg_color,64,32,12; diff --git a/data/gpuchip/examples/foxlogo.txt b/data/gpuchip/examples/foxlogo.txt new file mode 100644 index 0000000000..6bc42f3a5e --- /dev/null +++ b/data/gpuchip/examples/foxlogo.txt @@ -0,0 +1,84 @@ +//Fox game console logo (also example on how to work with polygons) + +dclrscr chassis; + +dcolor fox1c; +dvxdata_2f fox1a,16; //16 max!! +dvxdata_2f fox2a,3; +dvxdata_2f fox3a,3; +dvxdata_2f fox4a,3; +dvxdata_2f fox5a,3; +dvxdata_2f fox6a,3; +dvxdata_2f fox7a,6; +dvxdata_2f fox8a,3; + +dcolor fox2c; +dvxdata_2f fox2,4; + +dexit; + +//=========================================== +color chassis,0,0,0; + +color fox1c,60,60,60; +color fox2c,100,100,100; +//=========================================== +fox1a: //N=16 +db 60,218 +db 62,173 +db 32,36 +db 214,119 +db 268,128 +db 318,168 +db 352,233 +db 494,243 +db 499,254 +db 496,266 +db 478,321 +db 335,374 +db 265,408 +db 223,419 +db 95,430 +db 109,408 + +fox2a: //N = 3 +db 109,408 +db 57,432 +db 69,376 +fox3a: +db 69,376 +db 33,394 +db 59,327 +fox4a: +db 59,327 +db 24,348 +db 54,273 +fox5a: +db 54,273 +db 29,286 +db 57,240 +fox6a: +db 57,240 +db 26,245 +db 60,218 + +fox7a: //N=6 +db 109,408 +db 69,376 +db 59,327 +db 54,273 +db 57,240 +db 60,218 + +fox8a: //N=3 +db 177,150; +db 269,150; +db 190,47; + +//=========================================== +fox2: //N=4 +db 340,238 +db 286,257 +db 274,203 +db 311,213 +//=========================================== diff --git a/data/gpuchip/examples/hud_engine.txt b/data/gpuchip/examples/hud_engine.txt new file mode 100644 index 0000000000..71bdb8a29e --- /dev/null +++ b/data/gpuchip/examples/hud_engine.txt @@ -0,0 +1,101 @@ +//mov #65522,1; +//mov #65525,0.66; +//port0 & port1 - engine left/right throttle (0..1) +//port2 & port3 - delta (not used) + +//This displays engine window in PhoenixWings airplane + +mov #65485,16; //set circle quality + +dclrscr hud_border; + +dcolor hud_text; +dcircle hud_engine1gauge,68; +dcircle hud_engine2gauge,68; +dcolor hud_border; +dcircle hud_engine1gauge,64; +dcircle hud_engine2gauge,64; + +dcolor hud_text; +dsetwidth 1; +dline hud_engine1gauge_start,hud_engine1gauge; +dline hud_engine2gauge_start,hud_engine2gauge; + +dsetwidth 2; + +//=== +mov eax,port0; mul eax,100; +mul eax,0.1; +mul #left_power,1.9; +add #left_power,eax; +div #left_power,2; + +mov eax,#left_power; div eax,100; +mul eax,6.00; +add eax,1.57; + +drotatescale eax,1; +dmove hud_engine1gauge; + +dline gauge_base,gauge_needle; +//== +mov #right_power,#left_power; //comment this and.. +//uncomment if your left/right engines are not synchronized +//mov eax,port1; mul eax,100; +//mul eax,0.1; +//mul #right_power,1.9; +//add #right_power,eax; +//div #right_power,2; + +//mov eax,#right_power; div eax,100; +//mul eax,6.00; +//add eax,1.57; + +drotatescale eax,1; +dmove hud_engine2gauge; + +dline gauge_base,gauge_needle; +//== + +//use this for whatever you wanna +//mov #left_delta,port2; sub #left_delta,7.6; mul #left_delta,10; +//mov #right_delta,port3; sub #right_delta,7.6; mul #right_delta,10; + +drotatescale 0,1; //reset! +dmove 0; + +dsetfont 4; +dsetsize 28; +dwritefmt hud_text1pos,hud_text1; +dwritefmt hud_text2pos,hud_text2; + +cmp port4,1; +dcolor hud_yellow; +je _nsh; + dshade 0.25; +_nsh: +dwrite hud_text3pos,hud_text3; +dexit; + +vector2f hud_text1pos,70,212; +vector2f hud_text2pos,310,212; +vector2f hud_text3pos,20,460; + +string hud_text1,'Left Engine',10,10,'N1 = %i%%',10,'Delta = %i%%'; +alloc left_power; alloc left_delta; +string hud_text2,'Right Engine',10,10,'N1 = %i%%',10,'Delta = %i%%'; +alloc right_power; alloc right_delta; +string hud_text3,''; + +vector2f hud_engine1gauge,128,128; +vector2f hud_engine1gauge_start,128,64; + +vector2f hud_engine2gauge,384,128; +vector2f hud_engine2gauge_start,384,64; + +vector2f gauge_base,0,0; +vector2f gauge_needle,0,-48; + +color hud_text,64,255,64; +color hud_yellow,255,255,64; +color hud_border,30,30,30; diff --git a/data/gpuchip/examples/hud_fighter.txt b/data/gpuchip/examples/hud_fighter.txt new file mode 100644 index 0000000000..fd8ed7179c --- /dev/null +++ b/data/gpuchip/examples/hud_fighter.txt @@ -0,0 +1,134 @@ +//Aircraft hud +//port0 - ROLL +//port1 - PITCH +//port2 - YAW (heading) +//port3 - speed (units/sec) +//port4 - altitude (units) +//port5 - radar altitude (put ranger under your plane, and attach to this) +//port6 - flaps active, 1 or 0 +//port7 - go to "Gates - Time", and find "Derivative". Attach this to derivative, and derivative to altitude (vertical speed) + +//Artiftical horizon +in eax,0; //Roll +in ebx,1; //Pitch + +//mul ebx,0.017453292; +mul eax,0.017453292; +add eax,1.57; + +div ebx,90; +mul ebx,512; +add ebx,256; + +mov #horizon_moveoffset.y,ebx; + +drotatescale eax,1; +dmove horizon_moveoffset; + +dcolor art_sky; +drectwh horizon_sky_offset,horizon_size; +dcolor art_grnd; +drectwh horizon_grnd_offset,horizon_size; + +dcolor hud_text; +dsetsize 20; +mov eax,-45; +_horizon_text: + mov ebx,eax; + mul ebx,5.68; + sub ebx,10; + mov #horizon_textpos1.y,ebx; + mov #horizon_textpos2.y,ebx; add ebx,9; + mov #horizon_rectpos1.y,ebx; add ebx,2; + mov #horizon_rectpos2.y,ebx; + + drect horizon_rectpos1,horizon_rectpos2; + dwritei horizon_textpos1,eax; + dwritei horizon_textpos2,eax; + + add eax,15; + cmp eax,45; + jle _horizon_text; + +//Reset +dmove 0; +drotatescale 0,1; + +//Border around art horizon +dcolor border_color; +drect border_p1,border_p2; +drect border_p3,border_p4; +drect border_p5,border_p6; +drect border_p7,border_p8; +dcolor border_color2; +drect border_p9,border_p10; + +//Draw hud stuff +mov #roll,port0; +mov #pitch,port1; +mov #hdg,port2; add #hdg,180; +mov #spd,port3; div #spd,17.6; +mov #alt,port4; +add #alt,12000; +div #alt,12; +mov #ralt,port5; div #ralt,12; +mov #vspd,port7; div #vspd,17.6; +dcolor hud_text; +dwritefmt hud_pos1,hud_text1; +dsetsize 16; +dwritefmt hud_pos2,hud_text2; + +dcolor hud_text; +mov eax,port6; mul eax,0.75; add eax,0.25; +dshade eax; +dwritefmt hud_pos3,hud_text3; + + +dexit; + +vec2f hud_pos1,50,20; +string hud_text1,'ROLL %i %tPITCH %i%tHDG %i'; +alloc roll; +alloc pitch; +alloc hdg; + +vec2f hud_pos2,45,120; +string hud_text2,'SPD',10,'%ikt',10,10,'ALT',10,'%ift',10,10,'RALT',10,'%ift',10,10,'VSPD',10,'%ift/s'; +alloc spd; +alloc alt; +alloc ralt; +alloc vspd; + +vec2f hud_pos3,45,400; +string hud_text3,'FLAPS'; + + +vec2f horizon_textpos1,96,0; +vec2f horizon_textpos2,-64,0; +vec2f horizon_rectpos1,-50,0; +vec2f horizon_rectpos2,50,0; +color hud_text,64,255,64; + +color border_color2,255,255,255; +color border_color,30,30,30; +vec2f border_p1,0,0; +vec2f border_p2,128,512; +vec2f border_p3,384,0; +vec2f border_p4,512,512; + +vec2f border_p5,128,0; +vec2f border_p6,384,64; +vec2f border_p7,128,448; +vec2f border_p8,384,512; + +vec2f border_p9,128,254; +vec2f border_p10,384,258; + +vec2f horizon_sky_offset,-256,-512; +vec2f horizon_grnd_offset,-256,0; +vec2f horizon_size,512,512; + +vec2f horizon_moveoffset,256,256; + +color art_sky,24,144,255; +color art_grnd,192,72,0; diff --git a/data/gpuchip/examples/line_graph.txt b/data/gpuchip/examples/line_graph.txt new file mode 100644 index 0000000000..fbc366cdcd --- /dev/null +++ b/data/gpuchip/examples/line_graph.txt @@ -0,0 +1,62 @@ +// Author: Drunkie +// Description: A fake lag-o-meter that plots points on a grid + +Main(); + +#include + +float i; +float x, y; +float ox = 0, oy = 256; +float lines = 10; +float lineWidth = 1 +float frameWidth = 510; +float frameHeight = 300; + +void Main() +{ + glVertexMode( 1 ); // Enable vertex mode + glColor( 255, 255, 255 ); // Set draw color + + // Set texture as background + glBindTexture( "phoenix_storms/lag_sign" ); + glClearTexture(); + glBindTexture( 0 ); // Discard texture + + glFont( GL_FONT_AKBAR ); // Set font type + glFontSize( 36 ); // Set font size + glWriteString( 2, 2, "LAG-O-METER" ); // Write string to screen + + glOffset( lineWidth, 90 ); // Offset the screen coordinates + + glColor4( 0, 0, 0, 160 ); // Set draw color with alpha + glRectWH( 0, 0, frameWidth, frameHeight ); // Draw rectangle + + glColor( 255, 255, 255 ); + glLineWidth( lineWidth ); // Set line width + glORectWH( 0, 0, frameWidth, frameHeight ); // Draw outlined rectangle + + glLineWidth( 1 ); // Set line width to 1 + + // Loop and make a bunch of connected lines + for (i = 0; i < lines; i++) + { + if (i == 0) { + ox = 0; + rand oy; + oy *= frameHeight; + } + else { + ox = x; oy = y; + } + x = ((i+1) / lines) * frameWidth; + rand y; + y *= frameHeight; + glLine( ox, oy, x, y ); // Draw line on graph + } + + glOffset( 0, 0 ); // Set screen offset back to 0,0 + glWriteString( 2, 400, "INTENSE LAG DETECTED" ); + + glExit(); // Exit +} diff --git a/data/gpuchip/examples/mt2.txt b/data/gpuchip/examples/mt2.txt new file mode 100644 index 0000000000..8aac635d9c --- /dev/null +++ b/data/gpuchip/examples/mt2.txt @@ -0,0 +1,46 @@ +dcvxpipe 3; +mov #regHWClear,0; //Stop hardware clearing +dsetwidth 0.05; + +timer EAX; +mov EDX,EAX; sub EDX,#PrevTime; //EDX = Delta time +mov #PrevTime,EAX; + +mov EBP,0.4; //Speed of rotation + +mov ECX,8; +DrawLoop: + mov EAX,#Angle; mul EAX,1; + fsin #EndPoint.X,EAX; mul EAX,2; + fcos #EndPoint.Y,EAX; + + //HSL coloring + fsin #HSL.R,EAX; mul #HSL.R,127; add #HSL.R,128; add EAX,1.57;// mul EAX,2; + fsin #HSL.G,EAX; mul #HSL.G,127; add #HSL.G,128; add EAX,1.57;// mul EAX,2; + fsin #HSL.B,EAX; mul #HSL.B,127; add #HSL.B,128; + + dcolor HSL; + + //Looks very nice + dline StartPoint1,EndPoint; + dline StartPoint2,EndPoint; + dline StartPoint3,EndPoint; + dline StartPoint4,EndPoint; + + mul EDX,EBP; + add #Angle,EDX; +loop DrawLoop; + +dexit; + +alloc Angle; +alloc PrevTime; + +color HSL; + +vector2f EndPoint,0,0; +vector2f StartPoint0,0,0; +vector2f StartPoint1,1,1; +vector2f StartPoint2,1,-1; +vector2f StartPoint3,-1,-1; +vector2f StartPoint4,-1,1; diff --git a/data/gpuchip/examples/mt3.txt b/data/gpuchip/examples/mt3.txt new file mode 100644 index 0000000000..b0201313b9 --- /dev/null +++ b/data/gpuchip/examples/mt3.txt @@ -0,0 +1,106 @@ +//== 3D Graphics begin here ==================================================== + dvxpipe 3; + dcvxpipe 3; + + //Calc depth here + mov #Background.MinDepth, 0.8; //Near distance + mov #Background.MaxDepth, 6.0; //Far distance + mov #Background.ShadeStart,1.0; + mov #Background.DepthStep ,0.3; //Depth step. The lower, the higher quality is + + timer #Time; mul #Time,3; + + mov EAX,#Time; mod EAX,#Background.DepthStep; + + sub #Background.MinDepth,EAX; + sub #Background.MaxDepth,EAX; + + //Initialize depth range + mov #Background.deltaDepth,#Background.MaxDepth; + sub #Background.deltaDepth,#Background.MinDepth; + + //Compute background stuff + mov #Background.ShadeStep,#Background.deltaDepth; + div #Background.ShadeStep,#Background.DepthStep; + frnd #Background.ShadeStep; + finv #Background.ShadeStep; + mul #Background.ShadeStep,#Background.ShadeStepMul; + + //Brightness too + mov EAX,#Time; mod EAX,#Background.ShadeStep; + sub #Background.ShadeStart,EAX; + + mov #_rect.color.r,200; + mov #_rect.color.b,200; + +// Uncomment this for trippy camera +// timer EAX; div EAX,8; fsin EBX,EAX; mul EBX,2; +// drotatescale EAX,EBX; mul EBX,2; + + dsetwidth 0.8; + call Draw.Background; +dexit; + +alloc Time; + +//============================================================================== +Draw.Background: + //Draw all the rectangles + mov EAX,#Background.MinDepth; mov ECX,#Background.ShadeStart; + BackgroundLoop: + mov EDX,#Time; add EDX,EAX; + mov EBP,#Time; div EBP,6.28; fcos EBP,EBP; + + fsin EDI,EDX; mul EDI,EBP; mul EDI,0.8; sub EDI,1; + mov #_rect.offset.x,EDI; + + fcos ESI,EDX; mul ESI,EBP; mul ESI,0.4; sub ESI,1; + mov #_rect.offset.y,ESI; + + mov EDX,ECX; fpwr EDX,2; + mov #regZOffset,EAX; + + dcolor _rect.color; +// Uncomment this for trippy HSL color +// mov ESI,#Time; add ESI,EAX; +// fsin #HSL.R,ESI; mul #HSL.R,127; add #HSL.R,128; add ESI,1.57;// mul EAX,2; +// fsin #HSL.G,ESI; mul #HSL.G,127; add #HSL.G,128; add ESI,1.57;// mul EAX,2; +// fsin #HSL.B,ESI; mul #HSL.B,127; add #HSL.B,128; +// +// dcolor HSL; + dshade EDX; + dorectwh _rect.offset,_rect.wh; + + sub ECX,#Background.ShadeStep; + add EAX,#Background.DepthStep; + + cmp EAX,#Background.MaxDepth; + jl BackgroundLoop; +ret + +//============================================================================== +//Drawing parameters +scalar Background.MinDepth; +scalar Background.MaxDepth; +scalar Background.deltaDepth; +scalar Background.DepthStep; +scalar Background.ShadeStart; +scalar Background.ShadeStep; +scalar Background.ShadeStepMul,0.5; + +color HSL; + +//Generic rectangle +vector2f _rect.offset,-1,-1; +vector2f _rect.wh,2,2; + +vector2f _pad1.offset; +vector2f _pad2.offset; +vector2f _pad.wh; + +//Color scheme +color _rect.color, 200,200,200; +color _rect.color2,200,200,000; + +color _pad1.color, 000,200,000; +color _pad2.color, 200,000,000; diff --git a/data/gpuchip/examples/plasma.txt b/data/gpuchip/examples/plasma.txt new file mode 100644 index 0000000000..c80f6a4f0a --- /dev/null +++ b/data/gpuchip/examples/plasma.txt @@ -0,0 +1,116 @@ +//Plasma fractals +//Converted by dlb from http://bocoup.com/processing-js/docs/index.php?page=Plasma%20Fractals +//Which was converted by F1LT3R @ Hyper-Metrix.com from original at http://www.ic.sunysb.edu/Stu/jseyster/plasma/ + +mov #regHWClear,0; //Stop GPU clearing itself + +dentrypoint 0,_draw; //Set the entry point for the draw loop +dentrypoint 4,_async; //Set the enty point for the async loop + +mov #regAsyncFreq,2000000; //Make async run as fast as it can +mov #regAsyncClk,1; //Start async + +dexit; //End init +_draw: //Start draw +dexit; //End draw + +//Setup variables +color col; +vec2f pos; +vec2f size; +float gridSize, edge1, edge2, edge3, edge4, midPoint, newWidth, newHeight, width, height, noise; + +_async: //Enter async + +main(); //Run main function + +while(1){idle} //Infinatly loop + +void main(){ //Main function + dsetbuf_spr; //Use sprite buffer + + setColor(255,255,255); //Set the colour to white + rect(0,0,512,512); //Draw a large rectangle + + gridSize = 4; //How big each rectangle will be + width = 512; //GPU Width + height = 512; //GPU Height + noise = 5; //How noisy it will be + + //Give initial corner values + R1 = random(1); + R2 = random(1); + R3 = random(1); + R4 = random(1); + + plasma(0,0,width,height,R1,R2,R3,R4) //Start recursive function +} + +void plasma(float x, y, width, height, c1, c2, c3, c4){ //Plasma function + + //Setup local variables + float edge1, edge2, edge3, edge4, midPoint; + + //Work out the size of the next segments + float newWidth = width / 2; + float newHeight = height / 2; + + if((width > gridSize)||(height > gridSize)){ //If it is still bigger than the rectangle size + + midPoint = (c1 + c2 + c3 + c4) / 4 + displace(newWidth + newHeight); //Randomly change the midpoint + + //Calculate edges by averaging the corners + edge1 = (c1 + c2) / 2; + edge2 = (c2 + c3) / 2; + edge3 = (c3 + c4) / 2; + edge4 = (c4 + c1) / 2; + + //Make sure it doesn't displace too far + max midPoint,0; + min midPoint,1; + + //Run on the newly calculated segments + plasma(x, y, newWidth, newHeight, c1, edge1, midPoint, edge4); + plasma(x + newWidth, y, newWidth, newHeight, edge1, c2, edge2, midPoint); + plasma(x + newWidth, y + newHeight, newWidth, newHeight, midPoint, edge2, c3, edge3); + plasma(x, y + newHeight, newWidth, newHeight, edge4, midPoint, edge3, c4); + }else{ //Woo! It's the right size + float c = (c1 + c2 + c3 + c4) / 4; //Average the corners + + float grey = c*255; //Multiply the corners by 255 to get a valid color + + setColor(grey,grey,grey); //Set the color to your new color based on the "height" + rect(x,y,gridSize,gridSize); //Draw your rectangle + } +} + +float displace(float num){ //Displace function, it just works + float m = num / (width + height) * (1/noise); + R1 = random(1); + return (R1-0.5) * m; +} + +float random(float x){ //C version of the ASM rand opcode + preserve EAX; + rand EAX; + return EAX*x; +} + +void setColor(float r,float g,float b){ //C version of the dcolor opcode + mov #col.r,r; + mov #col.g,g; + mov #col.b,b; + + dcolor col; +} + +void rect(float x, float y, float width, float height){ //C version of the drectwh opcode + mov #pos.x,x; + mov #pos.y,y; + + mov #size.x,width; + mov #size.y,height; + + drectwh pos, size; + dswap; //dswap to make it show since we're drawing to the sprite buffer +} diff --git a/data/gpuchip/examples/sprite.txt b/data/gpuchip/examples/sprite.txt new file mode 100644 index 0000000000..49b8a3db47 --- /dev/null +++ b/data/gpuchip/examples/sprite.txt @@ -0,0 +1,30 @@ +// Author: Drunkie +// Description: A very simple sprite example + +Main(); + +#include + +void Main() +{ + // Enable vertex mode + glVertexMode( 1 ); + + // Draw to sprite buffer + glSetRenderTarget( GL_BUFFER_BACK ); + glClear( 0, 255, 0 ); + + // Draw to vertex buffer (world) + glSetRenderTarget( GL_BUFFER_VERTEX ); + glEnable( GL_VERTEX_TEXTURING ); + + // Sample from sprite 0 + glTextureSize( 256 ); + glTexture( 0 ); + glClear( 0, 255, 0 ); + glRectWH( 128, 128, 256, 256 ); + + glSetRenderTarget( GL_BUFFER_FRONT ); + + glExit(); +} diff --git a/data/gpuchip/examples/stargate.txt b/data/gpuchip/examples/stargate.txt new file mode 100644 index 0000000000..0aa118771c --- /dev/null +++ b/data/gpuchip/examples/stargate.txt @@ -0,0 +1,211 @@ +//STARGATE DIAL COMPUTER MAIN DISPLAY (realistic colors) +// +//How to connect: +//GPU IOBus to Data Port +//Port0 to "Open" +//Port1 to "Active" +//Port2 to "Chevron" +//Port3 to "Inbound" +//Port4 to iris +// +//That's all! + +div #65525,1.33; +mov #65485,16; //65485 is the circle quality register + +//24 means circles have 24 sides +//You can have up to 128 sides, but that LAGS +//32 sides is not even noticable comparing to 128 + +//= Misc decorations ================== + +dcolor stargate_out_ring; +dcircle center,250; +dcolor stargate_middle_ring; +dcircle center,240; +dcolor stargate_out_ring; +dcircle center,223; + +//= Rotating ring ===================== +mov #65485,12; +dcolor stargate_inner_ring; + +in ecx,2; //This block checks if chevron 7 is engaged +cmp ecx,7; //If yes, dont spin +mov eax,0; +jge _norotate; + timer eax; +_norotate: + +in ebx,1; //This one checks if stargate is active +mul eax,ebx; + +in ebx,3; neg ebx; add ebx,1; //This one checks if its inbound +mul eax,ebx; //wormhole + +drotatescale eax,1; //rotate by EAX radians +dmove center; +dcircle 0,220; + +drotatescale 0,1; //Reset scale/movment +dmove 0; + +//= Inner ring around EH ============== +mov #65485,24; +dcolor stargate_out_ring; +dcircle center,190; + + +//= EH ================================ +dcolor black; +dcircle center,180; //draw black hole instead of event horizon + +dcolor stargate_eventhorizon; + +in ebx,0; //Stargate active? +cmp ebx,0; +mov eax,0; +je _active; + rand eax; + mul eax,0.1; + add eax,0.9; +_active: + +in ebx,0; mul ebx,180; + +mul #eventhorizon_radius,0.99; +mul ebx,1.01; +add #eventhorizon_radius,ebx; +div #eventhorizon_radius,2; + + +dshade eax; +dcircle center,#eventhorizon_radius; + +//= Iris ============================== +mov edx,port4; +neg edx; add edx,1; + +mov eax,#iris_status; +sub eax,edx; +fabs eax,eax; + +dmuldt ecx,8; + +cmp eax,0.02; +jl _donothing; + cmp #iris_status,edx; + jl _lower; + sub #iris_status,ecx; + jmp _donothing; + _lower: + add #iris_status,ecx; +_donothing: + +mov #iris1.y,#iris_status; +mul #iris1.y,#iris2.y; + +dmove center; + +mov ecx,12; +_iris: + fsin ebx,ecx; fabs ebx,ebx; div ebx,10; add ebx,0.7; + + mov eax,ecx; mul eax,0.490; add eax,0.01; //0.697 + add eax,#iris_status; + + drotatescale eax,1; + + dcolor iris_color; + dshade ebx; + + drect iris1,iris2; +loop _iris; + +dmove 0; + +//= Chevrons ========================== +mov eax,1; //Chevron ID +in ebx,2; +dmove center; +_chevron_loop: + mov edx,eax; //Compute chevron angle in radians + mul edx,0.69815; + sub edx,1.23333; + + drotatescale edx,1; //Rotate chevron polygon + dcolor stargate_chevron; + + mov edx,eax:#chevron_triggers; + + cmp edx,ebx; //Check if chevron is light up + jle _noshade; + dshade 0.25; + _noshade: + + dvxpoly chevron_polygon,4; //draw chevron polygon + + inc eax; + cmp eax,9; + jle _chevron_loop; + +//= Computer text ===================== +drotatescale 0,1; //reset movement and scale +dmove 0; + +in eax,3; //Is inbound? +cmp eax,0; +je _dexit; + + timer eax; mul eax,2; fint eax; mod eax,2; + dcolor sgc_text; + dshade eax; + + dsetsize 64; //draw message + dwrite sgc_inboundpos,sgc_inbound; + +_dexit: +dexit; + +//= Helpers =========================== + +chevron_triggers: +db 9,4,5,6,7,1,2,3,8; +// 1 2 3 4 5 6 7 8 9 +// Order in which chevrons light up +// Only 1-7 are used though + +//===================================== + +color sgc_text,255,255,255; + +vector2f sgc_inboundpos,120,215; +string sgc_inbound,'INBOUND'; + +color stargate_out_ring, 116,105, 76; +color stargate_middle_ring, 93 , 85, 60; +color stargate_inner_ring, 138,137,108; +color stargate_eventhorizon, 93,114,162; +color stargate_chevron, 250,162, 54; +color iris_color, 192,192,192; + +color black,0,0,0; + +vector2f center,256,256; + +vector2f iris1,-44,0; +vector2f iris2,44,175; + +vector2f chevcenter,-16,-256; +vector2f chevsize,32,32; + +alloc eventhorizon_radius; +alloc iris_status; + +//raw chevron poly data +//format: +chevron_polygon: //n=4 +db -16,-251; +db 16,-251; +db 10,-230; +db -10,-230; diff --git a/data/gpuchip/examples/table.txt b/data/gpuchip/examples/table.txt new file mode 100644 index 0000000000..8e147d2e8a --- /dev/null +++ b/data/gpuchip/examples/table.txt @@ -0,0 +1,75 @@ +// Author: Drunkie +// Description: Draws a table; useful for calendars or spreadsheets! + +Main(); + +#include + +float rows = 6; +float cols = 5; +float sizex = 476; +float sizey = 400; +float linewidth = 3; + +float i, j, day; + +void Main() +{ + dentrypoint 0,DrawThread; + dentrypoint 4,AsyncThread; + + *regHWClear = 0 + *regAsyncFreq = 200000; + *regAsyncClk = 1; +} + +void DrawThread() +{ + dexit; +} + +void AsyncThread() +{ + glBegin(); + + glClear( 35, 35, 35 ); // Clear screen color + + glColor( 255, 255, 255 ); // Set draw color + glFont( GL_FONT_ARIAL ); // Set font type + glFontSize( 36 ); // Set font size + glWriteString( 16, 6, 'Simple-Calendar 1.0'); + + glColor( 120, 120, 120 ); + glOffset( 16, 64 ); // Set screen offset + glRectWH( 0, 0, sizex + linewidth, sizey + linewidth); // Draw rectangle + + glFont( GL_FONT_TREBUCHET ); + glFontSize( 14 ); + + // Calculate rectangle size + float sx = (sizex / rows) - linewidth; + float sy = (sizey / cols) - linewidth; + + // Loop through rows + for (i = 0; i < rows; i++) + { + // Loop through columns + for (j = 0; j < cols; j++) + { + // Calculate x,y coordinate to draw at + float x = i * (sizex / rows); + float y = j * (sizey / cols); + + glColor( 200, 200, 200 ); // Set draw color + glRectWH( x + linewidth, y + linewidth, sx, sy ); // Draw rectangle + + glColor( 0, 0, 0 ); // Set draw color + + // Write integer to screen + day = i + (j * rows) + glWriteInt( x + linewidth + 2, y + linewidth + 2, day + 1 ); + } + } + + glEnd(); +} diff --git a/data/gpuchip/examples/terrain.txt b/data/gpuchip/examples/terrain.txt new file mode 100644 index 0000000000..9b44c8d634 --- /dev/null +++ b/data/gpuchip/examples/terrain.txt @@ -0,0 +1,77 @@ +// Matrix code is based on cube example +DCPIPE 3 // -1 to 1 coordinate range, required by DDTERRAIN +DVXPIPE 5 // XYZ projection + matrix + +DENABLE 0 // Vertex buffer +DENABLE 1 // Z sorting +DENABLE 2 // Lighting +DENABLE 3 // Backface culling + +DSETLIGHT 0, Light + +MPERSPECTIVE ProjectionMatrix, Perspective + +// Rotate the terrain +TIMER EAX +DIV EAX, 4 +MOV #RotateVector.w, EAX +MROTATE RotateMatrix, RotateVector + +// Point camera at terrain +MLOOKAT ViewMatrix, LookAtArgs +MMUL ViewMatrix, RotateMatrix + +MLOAD ViewMatrix +MLOADPROJ ProjectionMatrix + +DCLRSCR Background + +DCOLOR Foreground +//DXTEXTURE Texture // Doesn't work +DDTERRAIN Terrain + +DVXFLUSH + +DEXIT + +COLOR Foreground, 253, 186, 49, 255 +COLOR Background, 1, 46, 87, 255 +COLOR White, 255, 255, 255, 255 + +//STRING Texture, "brick/brick_model"; + +// The terrain struct +Terrain: + DB 8, 8 // Terrain size + DB 16 // Draw distance, between 0 and 16 + DB 0, 0 // Terrain offset + + // 11 bytes unused + DB 0,0,0,0,0,0 + DB 0,0,0,0,0 + + // 8 x 8 heightmap + DB 0.0, 0.3, 0.3, 0.3, 0.3, 0.0, 0.0, 0.0 + DB 0.3, 0.3, 0.3, 0.5, 0.0, -0.5, 0.0, 0.0 + DB 0.0, 1.5, 1.5, 1.0, 0.3, -0.5, -0.3, 0.0 + DB 0.0, 1.0, 2.3, 1.8, 0.8, 0.3, 0.0, 0.0 + DB 0.3, 0.8, 1.3, 2.3, 1.6, 0.8, 0.5, 0.0 + DB 0.3, 0.5, 1.0, 1.3, 0.5, 0.3, 0.3, 0.0 + DB 0.0, 0.3, 0.3, 0.3, 0.0, -0.3, -0.5, 0.0 + DB 0.0, 0.0, 0.0, 0.0, -0.8, -0.8, -1.0, -0.5 + +Light: + DB 0, 50, -50, 0 // Position + DB 255, 249, 225, 0.9 // RGB + Intensity + +MATRIX ProjectionMatrix +MATRIX RotateMatrix +MATRIX ViewMatrix + +VEC4F RotateVector, 0, 0, 1, 0 // Rotate around Z axis +VEC4F Perspective, 50, 1, 1, 20 // 2nd value is aspect ratio + +LookAtArgs: + DB 0, 5, 4 // Camera + DB 0, 0, 0 // Look at + DB 0, 0, -1 // Up (terrain is upside-down for some reason) diff --git a/data/gpuchip/examples/texture.txt b/data/gpuchip/examples/texture.txt new file mode 100644 index 0000000000..f5286f5f0a --- /dev/null +++ b/data/gpuchip/examples/texture.txt @@ -0,0 +1,30 @@ +// Author: Drunkie +// Description: A very simple texture example + +Main(); + +#include + +void Main() +{ + glVertexMode( 1 ); + glColor( 255, 255, 255, 255 ); + + glBindTexture( 'brick/brick_model' ); + glColor( 255, 255, 255, 255 ); + glRectWH( 128, 128, 256, 256 ); + + glExit(); +} + +// ZASM version + +//mov #regVertexMode,1; +//dcolor white; +//dxtexture tex; +//drectwh pos,size; +//dexit; +//color white,255,255,255; +//string tex,'brick/brick_model'; +//vec2f pos,128,128; +//vec2f size,256,256; diff --git a/data/gpuchip/examples/trig.txt b/data/gpuchip/examples/trig.txt new file mode 100644 index 0000000000..f605a7fa7a --- /dev/null +++ b/data/gpuchip/examples/trig.txt @@ -0,0 +1,64 @@ +// Author: Jasongamer +// Description: A tool for helping people learn trig + +Main(); + +#include + +void Main() +{ + glClear( 0, 0, 0 ); // Clear screen + glCoordPipe( GL_CPIPE_N1_1 ); // Set coordinate pipe (-1 to 1 mode) + + timer R0; // Set time to curtime() + R0 = -R0 * 1; + + glColor( 255, 255, 255 ); // Set draw color + glCircle( *orig.x, *orig.y, 0.66, 40 ); // Draw circle (x, y, radius, quality) + + glColor( 0, 0, 0 ); + glCircle( *orig.x, *orig.y, 0.64, 40 ); + + // Set the points for the trig + fcos *cos,R0; + fsin *sin,R0; + + *PosR.x = *cos; + *PosR.x *= 0.65; + *PosR.x += *orig.x; + + *PosR.y = *sin; + *PosR.y *= 0.65; + *PosR.y += *orig.y; + + *PosX.x = *PosR.x; + + glLineWidth( 0.01 ); // Set line width + glFontSize( 24 ); // Set font size + + // X part of triangle + glColor( 0, 0, 255 ); + glLine( *orig.x, *orig.y, *PosX.x, *PosX.y ); // Draw line + glWriteFmt( -0.95, -0.95, sCos ) // Write formatted string + + // Y part of triangle + glColor( 255, 0, 0 ); + glLine( *PosR.x, *PosR.y, *PosX.x, *PosX.y ); + *sin *= -1; // Negate + glWriteFmt( -0.95, -0.85, sSin ); + + glColor( 255, 255, 255 ); + glLine( *orig.x, *orig.y, *PosR.x, *PosR.y ); // Draw line + + glExit(); // Exit +} + +vec2f orig,0,0; +vec2f PosR,0,0; +vec2f PosX,0,0; + +string sCos,"Cosine = %f"; +alloc cos; + +string sSin,"Sine = %f"; +alloc sin; diff --git a/data/gpuchip/examples/verynice1.txt b/data/gpuchip/examples/verynice1.txt new file mode 100644 index 0000000000..460f1e9939 --- /dev/null +++ b/data/gpuchip/examples/verynice1.txt @@ -0,0 +1,54 @@ +dcolor c1; +drect p1,p2; +dcolor c2; +drect p3,p4; +dcolor c3; +drect p5,p6; + +mov #textpos1.y,80; + +dcolor c4; +dsetsize 12; +dwrite textpos1,text1; + +mov ecx,0; +port_loop: + add #textpos1.y,18; + mov #textpos2.y,#textpos1.y; + + mov #textpos2.x,#textpos1.x; + add #textpos2.x,90; + dwrite textpos1,text2; + dwritei textpos2,ecx; + + in eax,ecx; + + mov #textpos2.x,#textpos1.x; + add #textpos2.x,192; + dwritef textpos2,eax; + + inc ecx; + cmp ecx,18; + jl port_loop; + +dexit; + +string text1,'VERYNICE HUD SYSTEM INITIALIZED... VER 1.0'; +string text2,'INPUT PORT VALUE'; + +vec2f textpos1,80,80; +vec2f textpos2,80,80; + +color c1,0,0,255; +color c2,0,0,127; +color c3,0,0,64; +color c4,255,255,255; + +vec2f p1,50,50; +vec2f p2,450,450; + +vec2f p3,60,60; +vec2f p4,430,430; + +vec2f p5,70,70; +vec2f p6,440,440; diff --git a/data/gpuchip/examples/verynice2.txt b/data/gpuchip/examples/verynice2.txt new file mode 100644 index 0000000000..33948e8024 --- /dev/null +++ b/data/gpuchip/examples/verynice2.txt @@ -0,0 +1,122 @@ +//Generated by WGUI tool. Get it at wiremod.com +_page_0: +dsetsize 16 +dcolor _c_0 +drect _a_1,_a_2 +dcolor _c_1 +drect _a_4,_a_5 +dcolor _c_2 +drect _a_7,_a_8 +dcolor _c_3 +drect _a_10,_a_11 +dcolor _c_3 +drect _a_13,_a_14 +dcolor _c_2 +mov #_f_17,port0 +dwrite _a_16,_s_17 +dcolor _c_4 +drect _a_19,_a_20 +dcolor _c_2 +mov #_f_23,port0 +dwrite _a_22,_s_23 +dcolor _c_4 +dwritefmt _a_25,_s_26 +dcolor _c_4 +dwritefmt _a_28,_s_29 +dcolor _c_4 +dwritefmt _a_31,_s_32 +dcolor _c_4 +dwritefmt _a_34,_s_35 +dcolor _c_4 +dwritefmt _a_37,_s_38 +dcolor _c_4 +dwritefmt _a_40,_s_41 +dcolor _c_4 +dwritefmt _a_43,_s_44 +dcolor _c_4 +dwritefmt _a_46,_s_47 +dcolor _c_3 +drect _a_49,_a_50 +dcolor _c_2 +mov #_f_53,port0 +dwrite _a_52,_s_53 +dcolor _c_3 +drect _a_55,_a_56 +dcolor _c_2 +mov #_f_59,port0 +dwrite _a_58,_s_59 +dcolor _c_2 +dwritefmt _a_61,_s_62 +dcolor _c_2 +dwritefmt _a_64,_s_65 +dcolor _c_2 +dwritefmt _a_67,_s_68 +dcolor _c_2 +dwritefmt _a_70,_s_71 +dcolor _c_2 +dwritefmt _a_73,_s_74 +dcolor _c_2 +dwritefmt _a_76,_s_77 +dexit + +color _c_0,0,0,160 +vec2f _a_1,8,8 +vec2f _a_2,504,504 +color _c_1,7,51,122 +vec2f _a_4,16,16 +vec2f _a_5,496,496 +color _c_2,0,0,0 +vec2f _a_7,24,24 +vec2f _a_8,488,488 +color _c_3,192,192,192 +vec2f _a_10,32,32 +vec2f _a_11,480,216 +vec2f _a_13,32,224 +vec2f _a_14,480,392 +vec2f _a_16,40,40 +string _s_17,'VERYNICE GUI V2.1 Initialized...' +alloc _f_17,0 +color _c_4,128,128,128 +vec2f _a_19,32,64 +vec2f _a_20,480,72 +vec2f _a_22,40,232 +string _s_23,'Raw data feed:' +alloc _f_23,0 +vec2f _a_25,88,256 +string _s_26,'Input port 0: %f' +vec2f _a_28,88,272 +string _s_29,'Input port 1: %f' +vec2f _a_31,88,288 +string _s_32,'Input port 2: %f' +vec2f _a_34,88,304 +string _s_35,'Input port 3: %f' +vec2f _a_37,88,320 +string _s_38,'Input port 4: %f' +vec2f _a_40,88,336 +string _s_41,'Input port 5: %f' +vec2f _a_43,88,352 +string _s_44,'Input port 6: %f' +vec2f _a_46,88,368 +string _s_47,'Input port 7: %f' +vec2f _a_49,32,400 +vec2f _a_50,248,480 +vec2f _a_52,40,408 +string _s_53,'Vector feed 1:' +alloc _f_53,0 +vec2f _a_55,264,400 +vec2f _a_56,480,480 +vec2f _a_58,272,408 +string _s_59,'Vector feed 2:' +alloc _f_59,0 +vec2f _a_61,40,424 +string _s_62,'X: %f' +vec2f _a_64,40,440 +string _s_65,'Y: %f' +vec2f _a_67,40,456 +string _s_68,'Z: %f' +vec2f _a_70,272,424 +string _s_71,'X: %f' +vec2f _a_73,272,440 +string _s_74,'Y: %f' +vec2f _a_76,272,456 +string _s_77,'Z: %f' diff --git a/data/gpuchip/lib/drivers/drv_gl.txt b/data/gpuchip/lib/drivers/drv_gl.txt new file mode 100644 index 0000000000..a7094eca1a --- /dev/null +++ b/data/gpuchip/lib/drivers/drv_gl.txt @@ -0,0 +1,502 @@ +// [Author] - Drunkie +// [Description] - A graphics driver that provides C-style functions for GPU +// [Documentation] - http://goo.gl/DHhYb + + +#define GL + +// Font +#define GL_FONT_LUCIDA_CONSOLE 0 +#define GL_FONT_COURIER_NEW 1 +#define GL_FONT_TREBUCHET 2 +#define GL_FONT_ARIAL 3 +#define GL_FONT_TIMES_NEW_ROMAN 4 +#define GL_FONT_COOLVETICA 5 +#define GL_FONT_AKBAR 6 +#define GL_FONT_CSD 7 + +// Buffer +#define GL_BUFFER_FRONT 0 +#define GL_BUFFER_BACK 1 +#define GL_BUFFER_SPRITE 1 +#define GL_BUFFER_VERTEX 2 + +// Coordinate pipe +#define GL_CPIPE_DIRECT 0 +#define GL_CPIPE_RESOLUTION 1 +#define GL_CPIPE_0_1 2 +#define GL_CPIPE_N1_1 3 +#define GL_CPIPE_N256_256 4 + +// Vertex pipe +#define GL_VPIPE_XY 0 +#define GL_VPIPE_YZ 1 +#define GL_VPIPE_XZ 2 +#define GL_VPIPE_XYZPROJ 3 +#define GL_VPIPE_XYTRANSFORM 4 +#define GL_VPIPE_XYZTRANSFORM 5 + +// denable / ddisable +#define GL_VERTEX_BUFFER 0 +#define GL_VERTEX_ZSORT 1 +#define GL_VERTEX_LIGHTING 2 +#define GL_VERTEX_CULLING 3 +#define GL_VERTEX_DCULLING 4 +#define GL_VERTEX_TEXTURING 5 + +// Fillmode +#define GL_FILL_SOLID 0 +#define GL_FILL_WIREFRAME 1 +#define GL_FILL_TEXTURE 2 + +// Cullmode +#define GL_CULL_FRONT 0 +#define GL_CULL_BACK 1 + +// Lightmode +#define GL_LIGHT_FRONT 1 +#define GL_LIGHT_BACK -1 + +// Horizontal font +#define GL_ALIGN_LEFT 0 +#define GL_ALIGN_CENTER 1 +#define GL_ALIGN_RIGHT 2 + +// Vertical font +#define GL_VALIGN_TOP 0 +#define GL_VALIGN_MIDDLE 1 +#define GL_VALIGN_BOTTOM 2 + +// Compatibility +#define glSetTexture glBindTexture +#define glWriteFmt glWriteFormat +#define glFontHAlign glFontAlign + + +// Clear +void glClear( float r, float g, float b ) { + mov #GL_BG.r,r; mov #GL_BG.g,g; mov #GL_BG.b,b; mov #GL_BG.a,255; + dclrscr GL_BG; +} +void glClear4( float r, float g, float b, float a ) { + mov #GL_BG.r,r; mov #GL_BG.g,g; mov #GL_BG.b,b; mov #GL_BG.a,a; + dclrscr GL_BG; +} +void glClearTexture() { + dclrtex; +} +void glHWClear( float n ) { + mov #regHWClear,n; +} + +// Color +void glColor( float r, float g, float b ) { + mov #GL_FG.r,r; mov #GL_FG.g,g; mov #GL_FG.b,b; mov #GL_FG.a,255; + dcolor GL_FG; +} +void glColor4( float r, float g, float b, float a ) { + mov #GL_FG.r,r; mov #GL_FG.g,g; mov #GL_FG.b,b; mov #GL_FG.a,a; + dcolor GL_FG; +} +void glBrightness( float r, float g, float b, float a ) { + mov #regBrightnessR,r; + mov #regBrightnessG,g; + mov #regBrightnessB,b; + mov #regBrightnessW,a; +} +void glContrast( float r, float g, float b, float a ) { + mov #regContrastR,r; + mov #regContrastG,g; + mov #regContrastB,b; + mov #regContrastW,a; +} +void glShade( float n ) { + dshade n; +} +void glShadeNorm( float n ) { + dshadenorm n; +} + +// Texture +void glBindTexture( char* str ) { + dxtexture str; +} +void glTexture( float id ) { + dtexture id; +} +void glTextureSize( float n ) { + mov #regTexSize,n; +} +void glTextureDataPtr( float n ) { + mov #regTexDataPtr,n; +} +void glTextureDataSize( float n ) { + mov #regTexDataSz,n; +} +void glTextureRotation( float n ) { + mov #regTexRotation,n; +} +void glTextureScale( float n ) { + mov #regTexScale,n; +} +void glTextureCenterUV( float u, float v ) { + mov #regTexCenterU,u; + mov #regTexCenterV,v; +} +void glTextureOffsetUV( float u, float v ) { + mov #regTexOffsetU,u; + mov #regTexOffsetV,v; +} + +// Frame +void glSleep( float ms ) { + div ms,1000; + timer #GL_CURTIME; + sub #GL_CURTIME,#GL_TIMESTAMP; + if (*GL_CURTIME <= ms) { + mov #regHWClear,0; + dexit; + } + timer #GL_TIMESTAMP; +} +void glExit() { + dexit; +} + +// Pipeline +void glCoordPipe( float c ) { + dcpipe c; +} +void glVertexPipe( float v ) { + dvxpipe v; +} + +// Hardware +void glReset( float n ) { + mov #regReset,n; +} +void glHalt( float n ) { + mov #regHalt,n; +} +void glRAMReset( float n ) { + mov #regRAMReset,n; +} +void glHScale( float n ) { + mov #regHScale,n; +} +void glVScale( float n ) { + mov #regVScale,n; +} +void glHWScale( float n ) { + mov #regHWScale,n; +} +void glHWRotate( float n ) { + mov #regRotation,n; +} + +// Offset +void glOffset( float x, float y ) { + mov #GL_V1.x,x; mov #GL_V1.y,y; + dmove GL_V1; +} +float glOffsetX() { + preserve eax; + mov eax,#regOffsetX; +} +float glOffsetY() { + preserve eax; + mov eax,#regOffsetY; +} +void glCenter( float x, float y ) { + mov #regCenterX,x; + mov #regCenterY,y; +} + +// Async +void glAsyncReset( float n ) { + mov #regAsyncReset,n; +} +void glAsyncClk( float n ) { + mov #regAsyncClk,n; +} +void glAsyncFreq( float n ) { + mov #regAsyncFreq,n; +} +void glEntryPoint( float idx, float ptr ) { + dentrypoint idx,ptr; +} +void glBegin() { + dbegin; +} +void glEnd() { + dend; +} +void glSwap() { + dswap; +} +void glSync() { + dvsync; +} + +// Cursor +void glCursor( float n ) { + mov #regCursor,n; +} +float glCursorX() { + preserve eax; + mov eax,#regCursorX; +} +float glCursorY() { + preserve eax; + mov eax,#regCursorY; +} + +// Circle +void glCircleQuality( float n ) { + mov #regCircleQuality,n; +} +void glCircleStart( float n ) { + mov #regCircleStart,n; +} +void glCircleEnd( float n ) { + mov #regCircleEnd,n; +} + +// Screen scaling +void glScreenScale( float n ) { + mov #regScale,n; +} +void glScreenScaleX( float x ) { + mov #regScaleX,x; +} +void glScreenScaleY( float y ) { + mov #regScaleY,y; +} + +// 2D graphics +void glCircle( float x, float y, float radius ) { + mov #GL_V1.x,x; mov #GL_V1.y,y; + dcircle GL_V1,radius; +} +void glRect( float x, float y, float dx, float dy ) { + mov #GL_V1.x,x; mov #GL_V1.y,y; + mov #GL_V2.x,dx; mov #GL_V2.y,dy; + drect GL_V1,GL_V2; +} +void glRectWH( float x, float y, float w, float h ) { + mov #GL_V1.x,x; mov #GL_V1.y,y; + mov #GL_V2.x,w; mov #GL_V2.y,h; + drectwh GL_V1,GL_V2; +} +void glORect( float x, float y, float dx, float dy ) { + mov #GL_V1.x,x; mov #GL_V1.y,y; + mov #GL_V2.x,dx; mov #GL_V2.y,dy; + dorect GL_V1,GL_V2; +} +void glORectWH( float x, float y, float w, float h ) { + mov #GL_V1.x,x; mov #GL_V1.y,y; + mov #GL_V2.x,w; mov #GL_V2.y,h; + dorectwh GL_V1,GL_V2; +} +void glPixel( float x, float y ) { + mov #GL_V1.x,x; mov #GL_V1.y,y; + dpixel GL_V1,GL_FG; +} +void glLine( float x, float y, float dx, float dy ) { + mov #GL_V1.x,x; mov #GL_V1.y,y; + mov #GL_V2.x,dx; mov #GL_V2.y,dy; + dline GL_V1,GL_V2; +} +void glLineWidth( float w ) { + dsetwidth w; +} +void glPoly2D( float* buffer, float count ) { + dvxdata_2f buffer,count; +} + + +// Text +void glFont( float id ) { + dsetfont id; +} +void glFontAlign( float n ) { + mov #regFontHalign,n; +} +void glFontVAlign( float n ) { + mov #regFontValign,n; +} +void glFontSize( float n ) { + dsetsize n; +} +float glTextWidth( char* str ) { + preserve eax; + dtextwidth eax,str; +} +float glTextHeight( char* str ) { + preserve eax; + dtextheight eax,str; +} +void glWriteString( float x, float y, char* str ) { + mov #GL_V1.x,x; mov #GL_V1.y,y; + dwrite GL_V1,str; +} +void glWriteFloat( float x, float y, float n ) { + mov #GL_V1.x,x; mov #GL_V1.y,y; + dwritef GL_V1,n; +} +void glWriteInt( float x, float y, float n ) { + mov #GL_V1.x,x; mov #GL_V1.y,y; + dwritei GL_V1,n; +} +void glWriteFormat( float x, float y, char* str ) { + mov #GL_V1.x,x; mov #GL_V1.y,y; + dwritefmt GL_V1,str; +} +float glParamList() { + preserve eax; + mov eax,#regParamList; +} + + +// 3D graphics +void glPoly3D( float* buffer, float count ) { + + if (*GL_MUPDATE == 1) { + mov #GL_MUPDATE,0; + mrotate GL_MROTATEMATRIX,GL_VROTATE; + mtranslate GL_MTRANSLATEMATRIX,GL_VTRANSLATE; + mscale GL_MSCALEMATRIX,GL_VSCALE; + mmov GL_MMODELMATRIX,GL_MTRANSLATEMATRIX; + mmul GL_MMODELMATRIX,GL_MROTATEMATRIX; + mmul GL_MMODELMATRIX,GL_MSCALEMATRIX; + mmov GL_MMODELVIEWMATRIX,GL_MVIEWMATRIX; + mmul GL_MMODELVIEWMATRIX,GL_MMODELMATRIX; + mload GL_MMODELVIEWMATRIX; + mloadproj GL_MPROJECTIONMATRIX; + } + + if (*GL_FILLMODE == GL_FILL_SOLID) { + dvxdata_3f buffer,count; + } + else if (*GL_FILLMODE == GL_FILL_WIREFRAME) { + dvxdata_3f_wf buffer,count; + } + else if (*GL_FILLMODE == GL_FILL_TEXTURE) { + dvxdata_3f_tex buffer,count + } +} +void glFlush() { + dvxflush; +} +void glEnable( float n ) { + denable n; +} +void glDisable( float n ) { + ddisable n; +} +void glLightPos( float x, float y, float z ) { + mov #GL_LIGHTPOS.x,x; mov #GL_LIGHTPOS.y,y; mov #GL_LIGHTPOS.z,z; + dsetlight 0,GL_LIGHTDATA; +} +void glLightColor( float r, float g, float b, float a ) { + mov #GL_LIGHTCOL.r,r; mov #GL_LIGHTCOL.g,g; + mov #GL_LIGHTCOL.b,b; mov #GL_LIGHTCOL.a,a; + dsetlight 0,GL_LIGHTDATA; +} +void glFillMode( float n ) { + mov #GL_FILLMODE,n; +} +void glLookAt( float x, float y, float z, float tx, float ty, float tz, float ux, float uy, float uz ) { + mov #GL_VLOOKAT_POS.x,x; mov #GL_VLOOKAT_POS.y,y; mov #GL_VLOOKAT_POS.z,z; + mov #GL_VLOOKAT_TARG.x,tx; mov #GL_VLOOKAT_TARG.y,ty; mov #GL_VLOOKAT_TARG.z,tz; + mov #GL_VLOOKAT_UP.x,ux; mov #GL_VLOOKAT_UP.y,uy; mov #GL_VLOOKAT_UP.z,uz; + mlookat GL_MVIEWMATRIX,GL_VLOOKAT; + mov #GL_MUPDATE,1; +} +void glPerspective( float fov, float asp, float znear, float zfar ) { + mov #GL_VPERSPECTIVE.x,fov; mov #GL_VPERSPECTIVE.y,asp; + mov #GL_VPERSPECTIVE.z,znear; mov #GL_VPERSPECTIVE.w,zfar; + mperspective GL_MPROJECTIONMATRIX,GL_VPERSPECTIVE; + mov #GL_MUPDATE,1; +} +void glRotate( float x, float y, float z, float w ) { + mov #GL_VROTATE.x,x; mov #GL_VROTATE.y,y; mov #GL_VROTATE.z,z; mov #GL_VROTATE.w,w; + mov #GL_MUPDATE,1; +} +void glTranslate( float x, float y, float z ) { + mov #GL_VTRANSLATE.x,x; mov #GL_VTRANSLATE.y,y; mov #GL_VTRANSLATE.z,z; + mov #GL_MUPDATE,1; +} +void glScale( float x, float y, float z ) { + mov #GL_VSCALE.x,x; mov #GL_VSCALE.y,y; mov #GL_VSCALE.z,z; + mov #GL_MUPDATE,1; +} +void glZOffset( float n ) { + mov #regZOffset,n; +} +void glCullDistance( float n ) { + mov #regCullDistance,n; +} +void glCullMode( float n ) { + mov #regCullMode,n; +} +void glLightMode( float n ) { + mov #regLightMode,n; +} +void glVertexArray( float n ) { + mov #regVertexArray,n; +} + +// Other +void glVertexMode( float n ) { + mov #regVertexMode,n; +} +void glSetRenderTarget( float n ) { + if (n == GL_BUFFER_FRONT) { + dsetbuf_fbo; + } + else if (n == GL_BUFFER_BACK) { + dsetbuf_spr; + } + else if (n == GL_BUFFER_VERTEX) { + dsetbuf_vx; + } +} +float glIndex() { + preserve eax; + mov eax,#regIndex; +} + + +// Allocated variables for GL +color GL_FG,255,255,255; +color GL_BG; +vec4f GL_V1; +vec4f GL_V2; + +alloc GL_TIMESTAMP; +alloc GL_CURTIME; +alloc GL_FILLMODE; + +GL_LIGHTDATA: +vec4f GL_LIGHTPOS,0,0,-10; +color GL_LIGHTCOL,255,255,255,1; + +GL_VLOOKAT: +vec3f GL_VLOOKAT_POS,0,0,-10; +vec3f GL_VLOOKAT_TARG,0,0,0; +vec3f GL_VLOOKAT_UP,0,1,0; + +matrix GL_MROTATEMATRIX; +matrix GL_MTRANSLATEMATRIX; +matrix GL_MSCALEMATRIX; +matrix GL_MPROJECTIONMATRIX; +matrix GL_MVIEWMATRIX; +matrix GL_MMODELMATRIX; +matrix GL_MMODELVIEWMATRIX; +alloc GL_MUPDATE,1; + +vec4f GL_VROTATE; +vec4f GL_VTRANSLATE; +vec4f GL_VPERSPECTIVE; +vec4f GL_VSCALE,1,1,1,0; diff --git a/data/gpuchip/lib/drivers/drv_gl_toolkit.txt b/data/gpuchip/lib/drivers/drv_gl_toolkit.txt new file mode 100644 index 0000000000..0b20ca9c14 --- /dev/null +++ b/data/gpuchip/lib/drivers/drv_gl_toolkit.txt @@ -0,0 +1,122 @@ +#ifndef GL +#include +#endif + +#ifndef GLT + +#define GLT +#define GLT_MAX_TRIANGLES 32 + +float __GLT_VERTBUFF[(GLT_MAX_TRIANGLES * 3) * 3]; +float __GLT_VERTCNT = 0; + +void gltVertex(float x, float y, float z) +{ + if ((__GLT_VERTCNT / 3) >= GLT_MAX_TRIANGLES) + return; + + float* ptr = __GLT_VERTBUFF; + ptr += (__GLT_VERTCNT * 3); + + *ptr = x; + *(++ptr) = y; + *(++ptr) = z; + + __GLT_VERTCNT++; +} + +void gltTriangle(float x1, float y1, float z1, + float x2, float y2, float z2, + float x3, float y3, float z3) +{ + gltVertex(x1, y1, z1); + gltVertex(x2, y2, z2); + gltVertex(x3, y3, z3); +} + +void gltQuad(float tlx, float tly, float tlz, + float trx, float try, float trz, + float brx, float bry, float brz, + float blx, float bly, float blz) +{ + gltTriangle( + trx, try, trz, + tlx, tly, tlz, + blx, bly, blz + ); + + gltTriangle( + brx, bry, brz, + trx, try, trz, + blx, bly, blz + ); +} + +void gltCube(float cex, float cey, float cez, float size) +{ + float s2 = size / 2; + + gltQuad( + cex - s2, cey + s2, cez - s2, + cex + s2, cey + s2, cez - s2, + cex + s2, cey - s2, cez - s2, + cex - s2, cey - s2, cez - s2 + ); + + gltQuad( + cex + s2, cey + s2, cez + s2, + cex - s2, cey + s2, cez + s2, + cex - s2, cey - s2, cez + s2, + cex + s2, cey - s2, cez + s2 + ); + + gltQuad( + cex - s2, cey + s2, cez + s2, + cex - s2, cey + s2, cez - s2, + cex - s2, cey - s2, cez - s2, + cex - s2, cey - s2, cez + s2 + ); + + gltQuad( + cex + s2, cey + s2, cez - s2, + cex + s2, cey + s2, cez + s2, + cex + s2, cey - s2, cez + s2, + cex + s2, cey - s2, cez - s2 + ); + + gltQuad( + cex - s2, cey + s2, cez + s2, + cex + s2, cey + s2, cez + s2, + cex + s2, cey + s2, cez - s2, + cex - s2, cey + s2, cez - s2 + ); + + gltQuad( + cex - s2, cey - s2, cez - s2, + cex + s2, cey - s2, cez - s2, + cex + s2, cey - s2, cez + s2, + cex - s2, cey - s2, cez + s2 + ); +} + +void gltClearBuffer() +{ + __GLT_VERTCNT = 0; +} + +void gltFlushBuffer() +{ + if (__GLT_VERTCNT <= 3) + return; + + float vcnt = __GLT_VERTCNT; + float tcnt = (vcnt / 3) - (vcnt % 3); + + if (tcnt > GLT_MAX_TRIANGLES) + tcnt = GLT_MAX_TRIANGLES; + + glPoly3D(__GLT_VERTBUFF, tcnt); + glFlush(); +} + +#endif diff --git a/data/spuchip/examples/beatbox.txt b/data/spuchip/examples/beatbox.txt new file mode 100644 index 0000000000..605f7c76b0 --- /dev/null +++ b/data/spuchip/examples/beatbox.txt @@ -0,0 +1,56 @@ +wset 4,inst1; +chwave 1,4; +chpitch 1,2.55; + +chwave 2,0; +chvolume 2,0.5; +chstart 2; +chpitch 2,0; + +mainloop: + timer r0; + mul r0,6; + mov r1,r0; + + fint r0; + mod r0,16; + add r0,0; + mod r1,1; + currentTick = r0; + currentTickTime = r1; + + instr1 = patternData1[currentTick]; + instr2 = patternData2[currentTick]; + instr3 = patternData3[currentTick]; + + if ((pinstr1 == 0) && (instr1 == 1)) { + chstart 0; + } else { + chstop 0; + } + + if ((pinstr2 == 0) && (instr2 == 1)) { + chstart 1; + } else { + chstop 1; + } + + mov r0,currentTickTime; neg r0; add r0,1; fpwr r0,4; + mul r0,0.6; // add r0,0.64; + chpitch 0,r0; + + mov r0,instr3; + mul r0,0.1; + add r0,0.2; + chpitch 2,r0; +jmp mainloop; + +float currentTick,currentTickTime; +float instr1,instr2,instr3; +float pinstr1,pinstr2; + +patternData1: db 1,1,0,0, 1,0,0,0, 1,0,0,1, 0,1,0,0, 1,1,0,0, 1,1,0,0, 1,1,0,1, 1,0,1,0; +patternData2: db 0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0; +patternData3: db 0,1,0,2, 0,1,2,2, 0,0,1,1, 1,2,1,2, 2,2,1,2, 2,2,1,1, 1,1,0,0, 0,0,1,2; + +string inst1,"synth/pink_noise.wav"; diff --git a/data/spuchip/examples/mario_theme.txt b/data/spuchip/examples/mario_theme.txt new file mode 100644 index 0000000000..db5fbd99b7 --- /dev/null +++ b/data/spuchip/examples/mario_theme.txt @@ -0,0 +1,156 @@ +// Author: Jasongamer +// Song: Mario Underwater Theme + +// Set track wave to channel 0 and start +wset 0,trackwave; +chwave 0,0; +chvolume 0,0.2; +chstart 0; + +// Set track wave to channel 1 and start +wset 1,trackwave; +chwave 1,1; +chvolume 1,0.2; +chstart 1; + +// Set bass wave to channel 2 and start +wset 2,basswave; +chwave 2,1; +chvolume 2,0.3; +chstart 2; + +// Get track length +tracklen = strlen(trackA); + +void main() +{ + // Tempo + if ((i > 120) && (i <= 230)) + tempo( 1000 ); + else + tempo( 864 ); + + // Track A + note = 2; + fpwr note,(trackA[i]/12); + note /= 100; + chpitch 0,note; + + // Track B + note = 2; + fpwr note,(trackB[i]/12); + note /= 100; + chpitch 1,note; + + // Bass + note = 2; + fpwr note,(bass[i]/12); + note /= 100; + chpitch 2,note; + + // Index + i++; mod i,tracklen; + + // Repeat + jmp main; +} + +// Accurate tempo function for beats-per-minute +void tempo( float bpm ) +{ + timer timestamp; + while ((time - timestamp) < (60 / bpm)) { timer time; } +} + +// Returns the length of a string +float strlen(char* str) +{ + char* strptr = str; + while (*strptr++); + return (strptr - str); +} + +float note, i; +float tracklen; +float time, timestamp; + +string trackwave,"synth/square.wav"; +string basswave,"synth/tri.wav"; + +trackA: + +// Intro +db 73,73,73,73, 75,75,75,75, 77,77,77,77, 78,78,78,78, 80,80,80,80, 81,81,81,81; +db 82,-1,82,-1, 82,82,82,-1, 82,82,82,-1, 82,82,82,82, 82,82,82,-1, -1,-1,78,78; + +// Part 1 +db 87,87,87,87, 87,87,87,87, 87,87,87,-1, 86,86,86,86, 86,86,86,86, 86,86,86,-1; +db 87,87,87,87, 87,87,87,87, 87,87,87,-1, -1,-1,78,78, 80,80,82,82, 83,83,85,85; +db 87,87,87,87, 87,87,87,87, 87,87,87,-1, 86,86,86,86, 86,86,86,-1, 88,88,88,-1; +db 87,87,87,87, 87,87,87,87, 87,87,87,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,78,78; +db 85,85,85,85, 85,85,85,85, 85,85,85,-1, 84,84,84,84, 84,84,84,84, 84,84,84,-1; +db 85,85,85,85, 85,85,85,85, 85,85,85,-1, -1,-1,78,78, 80,80,82,82, 83,83,84,84; +db 85,85,85,85, 85,85,85,85, 85,85,85,-1, 78,78,78,78, 78,78,78,-1, 88,88,88,-1; +db 87,87,87,87, 87,87,87,87, 87,87,87,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,78,78; + +// Part 2 +db 90,90,90,90, 90,90,90,90, 90,90,90,-1, 90,90,90,90, 90,90,90,90, 90,90,90,-1; +db 90,90,90,90, 90,90,90,90, 90,90,90,-1, 90,90,90,-1, 92,92,-1,-1, -1,-1,90,90; +db 88,88,88,88, 88,88,88,88, 88,88,88,-1, 88,88,88,88, 88,88,88,88, 88,88,88,-1; +db 88,88,88,88, 88,88,88,88, 88,88,88,-1, 88,88,88,-1, 90,90,-1,-1, -1,-1,88,88; +db 87,87,87,87, 87,87,87,87, 87,87,87,-1, 80,80,80,-1, 82,82,82,-1, 88,88,88,-1; +db 87,-1,87,-1, 87,87,87,87, 87,-1,82,82, 83,83,83,83, 83,83,83,83, 83,83,83,-1; + +db 0; // End string + +trackB: + +// Intro +db 73,73,73,73, 72,72,72,72, 71,71,71,71, 70,70,70,70, 71,71,71,71, 72,72,72,72; +db 73,-1,73,-1, 73,73,73,-1, 75,75,75,-1, 76,76,76,76, 76,76,76,-1, -1,-1,-1,-1; + +// Part 1 +db 78,78,78,78, 78,78,78,78, 78,78,78,-1, 77,77,77,77, 77,77,77,77, 77,77,77,-1; +db 78,78,78,78, 78,78,78,78, 78,78,78,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1; +db 78,78,78,78, 78,78,78,78, 78,78,78,-1, 77,77,77,77, 77,77,77,-1, 80,80,80,-1; +db 78,78,78,78, 78,78,78,78, 78,78,78,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1; +db 76,76,76,76, 76,76,76,76, 76,76,76,-1, 75,75,75,75, 75,75,75,75, 75,75,75,-1; +db 76,76,76,76, 76,76,76,76, 76,76,76,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1; +db 76,76,76,76, 76,76,76,76, 76,76,76,-1, 70,70,70,70, 70,70,70,-1, 80,80,80,-1; +db 78,78,78,78, 78,78,78,78, 78,78,78,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1; + +// Part 2 +db 87,87,87,87, 87,87,87,87, 87,87,87,-1, 85,85,85,85, 85,85,85,85, 85,85,85,-1; +db 84,84,84,84, 84,84,84,84, 84,84,84,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1; +db 85,85,85,85, 85,85,85,85, 85,85,85,-1, 84,84,84,84, 84,84,84,84, 84,84,84,-1; +db 83,83,83,83, 83,83,83,83, 83,83,83,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1; +db 71,71,71,71, 71,71,71,71, 71,71,71,-1, 76,76,76,-1, 78,78,78,-1, 82,82,82,-1; +db 82,-1,82,-1, 82,82,82,-1, -1,-1,76,76, 75,75,75,75, 75,75,75,75, 75,75,75,-1; + +db 0; // End string + +bass: + +// Intro +db -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1; +db -1,-1,-1,-1, -1,-1,-1,-1, 66,66,66,-1, 66,66,66,66, 66,66,66,66, 66,66,66,-1; + +// Part 1 +db 59,59,59,-1, 66,66,66,-1, 71,71,71,-1, 58,58,58,-1, 66,66,66,-1, 70,70,70,-1; +db 59,59,59,-1, 66,66,66,-1, 71,71,71,-1, 63,63,63,-1, 66,66,66,-1, 71,71,71,-1; +db 59,59,59,-1, 66,66,66,-1, 71,71,71,-1, 58,58,58,-1, 66,66,66,-1, 70,70,70,-1; +db 59,59,59,-1, 66,66,66,-1, 71,71,71,-1, 63,63,63,-1, 66,66,66,-1, 71,71,71,-1; +db 61,61,61,-1, 66,66,66,-1, 70,70,70,-1, 60,60,60,-1, 65,65,65,-1, 69,69,69,-1; +db 61,61,61,-1, 66,66,66,-1, 70,70,70,-1, 58,58,58,-1, 66,66,66,-1, 70,70,70,-1; +db 61,61,61,-1, 66,66,66,-1, 70,70,70,-1, 58,58,58,-1, 66,66,66,-1, 70,70,70,-1; +db 59,59,59,-1, 66,66,66,-1, 71,71,71,-1, 54,54,54,-1, 66,66,66,-1, 71,71,71,-1; + +// Part 2 +db 59,59,59,-1, 66,66,66,-1, 75,75,75,-1, 58,58,58,-1, 66,66,66,-1, 73,73,73,-1; +db 57,57,57,-1, 66,66,66,-1, 72,72,72,-1, 60,60,60,-1, 66,66,66,-1, 75,75,75,-1; +db 61,61,61,-1, 68,68,68,-1, 76,76,76,-1, 60,60,60,-1, 68,68,68,-1, 76,76,76,-1; +db 59,59,59,-1, 68,68,68,-1, 76,76,76,-1, 58,58,58,-1, 66,66,66,-1, 76,76,76,-1; +db 47,47,47,-1, 66,66,66,-1, 75,75,75,-1, 54,54,54,-1, 66,66,66,-1, 66,66,66,-1; +db 64,-1,64,-1, 64,64,64,-1, -1,-1,58,58, 59,59,59,59, 59,59,59,59, 59,59,59,-1; + +db 0; // End string diff --git a/lua/autorun/cpu_load.lua b/lua/autorun/cpu_load.lua new file mode 100644 index 0000000000..77480eb078 --- /dev/null +++ b/lua/autorun/cpu_load.lua @@ -0,0 +1,33 @@ +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") + +AddCSLuaFile("wire/cpulib.lua") +include("wire/cpulib.lua") + +AddCSLuaFile("wire/gpulib.lua") +include("wire/gpulib.lua") + +AddCSLuaFile("wire/cpu_default_data_decompressor.lua") +include("wire/cpu_default_data_decompressor.lua") + +AddCSLuaFile("wire/cpu_default_data_files.lua") + +if CLIENT then + include("wire/client/hlzasm/hc_compiler.lua") +end \ No newline at end of file diff --git a/lua/entities/gmod_wire_cpu.lua b/lua/entities/gmod_wire_cpu.lua new file mode 100644 index 0000000000..5ab5a54f81 --- /dev/null +++ b/lua/entities/gmod_wire_cpu.lua @@ -0,0 +1,288 @@ +AddCSLuaFile() +DEFINE_BASECLASS( "base_wire_entity" ) +ENT.PrintName = "Wire ZCPU" +ENT.Author = "Black Phoenix" +ENT.WireDebugName = "ZCPU" + +if CLIENT then return end -- No more client + +local cpu_max_frequency = 1400000 +local wire_cpu_max_frequency = CreateConVar("wire_cpu_max_frequency", cpu_max_frequency, FCVAR_REPLICATED) + +cvars.AddChangeCallback("wire_cpu_max_frequency",function() + cpu_max_frequency = math.Clamp(math.floor(wire_cpu_max_frequency:GetInt()),1,30000000) +end) + +function ENT:Initialize() + self:PhysicsInit(SOLID_VPHYSICS) + self:SetMoveType(MOVETYPE_VPHYSICS) + self:SetSolid(SOLID_VPHYSICS) + + self.Inputs = Wire_CreateInputs(self, { "MemBus", "IOBus", "Frequency", "Clk", "Reset", "Interrupt"}) + self.Outputs = Wire_CreateOutputs(self, { "Error" }) + + -- CPU platform settings + self.Clk = false -- whether the Clk input is on + self.VMStopped = false -- whether the VM has halted itself (e.g. by running off the end of the program) + self.Frequency = 2000 + -- Create virtual machine + self.VM = CPULib.VirtualMachine() + self.VM.SerialNo = CPULib.GenerateSN("CPU") + self.VM:Reset() + + self:SetCPUName() + self:SetMemoryModel("64krom") + self.VM.SignalError = function(VM,errorCode) + Wire_TriggerOutput(self, "Error", errorCode) + end + self.VM.SignalShutdown = function(VM) + self.VMStopped = true + end + self.VM.ExternalWrite = function(VM,Address,Value) + if Address >= 0 then -- Use MemBus + local MemBusSource = self.Inputs.MemBus.Src + if MemBusSource then + if MemBusSource.ReadCell then + local result = MemBusSource:WriteCell(Address-self.VM.RAMSize,tonumber(Value) or 0) + if result then return true + else VM:Interrupt(7,Address) return false + end + else VM:Interrupt(8,Address) return false + end + else VM:Interrupt(7,Address) return false + end + else -- Use IOBus + local IOBusSource = self.Inputs.IOBus.Src + if IOBusSource then + if IOBusSource.ReadCell then + local result = IOBusSource:WriteCell(-Address-1,tonumber(Value) or 0) + if result then return true + else VM:Interrupt(10,-Address-1) return false + end + else VM:Interrupt(8,Address+1) return false + end + else return true + end + end + end + self.VM.ExternalRead = function(VM,Address) + if Address >= 0 then -- Use MemBus + local MemBusSource = self.Inputs.MemBus.Src + if MemBusSource then + if MemBusSource.ReadCell then + local result = MemBusSource:ReadCell(Address-self.VM.RAMSize) + if isnumber(result) then return result + else VM:Interrupt(7,Address) return + end + else VM:Interrupt(8,Address) return + end + else VM:Interrupt(7,Address) return + end + else -- Use IOBus + local IOBusSource = self.Inputs.IOBus.Src + if IOBusSource then + if IOBusSource.ReadCell then + local result = IOBusSource:ReadCell(-Address-1) + if isnumber(result) then return result + else VM:Interrupt(10,-Address-1) return + end + else VM:Interrupt(8,Address+1) return + end + else return 0 + end + end + end + + local oldReset = self.VM.Reset + self.VM.Reset = function(...) + if self.Clk and self.VMStopped then + self:NextThink(CurTime()) + end + self.VMStopped = false + return oldReset(...) + end + + -- Player that debugs the processor + self.DebuggerPlayer = nil +end + +function ENT:ReadCell(Address) + Address = math.floor(Address) + return self.VM:ReadCell(Address) +end + +function ENT:WriteCell(Address,Value) + Address = math.floor(Address) + return self.VM:WriteCell(Address,tonumber(Value) or 0) +end + +local memoryModels = { + ["8k"] = { 8192, 0 }, + ["8krom"] = { 8192, 8192 }, + ["32k"] = { 32768, 0 }, + ["32krom"] = { 32768, 32768 }, + ["64krom"] = { 65536, 65536 }, + ["64k"] = { 65536, 0 }, + ["128krom"] = { 131072, 131072 }, + ["128rom"] = { 0, 128 }, + ["128"] = { 128, 128 }, + ["flat"] = { 0, 0 }, +} + +function ENT:SetMemoryModel(model) + self.VM.RAMSize = memoryModels[model][1] or 65536 + self.VM.ROMSize = memoryModels[model][2] or 65536 +end + +-- Execute ZCPU virtual machine +function ENT:Run() + -- Do not run if debugging is active + if self.DebuggerPlayer then return end + + -- Calculate time-related variables + local CurrentTime = CurTime() + local DeltaTime = math.min(1/30,CurrentTime - (self.PreviousTime or 0)) + self.PreviousTime = CurrentTime + + -- Check if need to run till specific instruction + if self.BreakpointInstructions then + self.VM.TimerDT = DeltaTime + self.VM.CPUIF = self + self.VM:Step(8,function(self) + -- self:Emit("VM.IP = "..(self.PrecompileIP or 0)) + -- self:Emit("VM.XEIP = "..(self.PrecompileTrueXEIP or 0)) + + self:Dyn_Emit("if (VM.CPUIF.Clk and not VM.CPUIF.VMStopped) and (VM.CPUIF.OnVMStep) then") + self:Dyn_EmitState() + self:Emit("VM.CPUIF.OnVMStep()") + self:Emit("end") + self:Emit("if VM.CPUIF.BreakpointInstructions[VM.IP] then") + self:Dyn_EmitState() + self:Emit("VM.CPUIF.OnBreakpointInstruction(VM.IP)") + self:Emit("VM.CPUIF.VMStopped = true") + self:Emit("VM.TMR = VM.TMR + "..self.PrecompileInstruction) + self:Emit("VM.CODEBYTES = VM.CODEBYTES + "..self.PrecompileBytes) + self:Emit("if true then return end") + self:Emit("end") + self:Emit("if VM.CPUIF.LastInstruction and ((VM.IP > VM.CPUIF.LastInstruction) or VM.CPUIF.ForceLastInstruction) then") + self:Dyn_EmitState() + self:Emit("VM.CPUIF.ForceLastInstruction = nil") + self:Emit("VM.CPUIF.OnLastInstruction()") + self:Emit("VM.CPUIF.VMStopped = true") + self:Emit("VM.TMR = VM.TMR + "..self.PrecompileInstruction) + self:Emit("VM.CODEBYTES = VM.CODEBYTES + "..self.PrecompileBytes) + self:Emit("if true then return end") + self:Emit("end") + end) + self.VM.CPUIF = nil + else + -- How many steps VM must make to keep up with execution + local Cycles = math.max(1,math.floor(self.Frequency*DeltaTime*0.5)) + self.VM.TimerDT = (DeltaTime/Cycles) + + while (Cycles > 0) and (self.Clk) and (not self.VMStopped) and (self.VM.Idle == 0) do + -- Run VM step + local previousTMR = self.VM.TMR + self.VM:Step() + Cycles = Cycles - math.max(1, self.VM.TMR - previousTMR) + end + end + + -- Update VM timer + self.VM.TIMER = self.VM.TIMER + DeltaTime + + -- Reset idle register + self.VM.Idle = 0 +end + +function ENT:Think() + if (not game.SinglePlayer()) and (self.Frequency > cpu_max_frequency) then self.Frequency = cpu_max_frequency end + self:Run() + if self.Clk and not self.VMStopped then self:NextThink(CurTime()) end + return true +end + +-- Write data to RAM and then flash ROM if required +function ENT:FlashData(data) + self.VM:Reset() + for k,v in pairs(data) do + self.VM:WriteCell(k,tonumber(v) or 0) + if (k >= 0) and (k < self.VM.ROMSize) then + self.VM.ROM[k] = tonumber(v) or 0 + end + end +end + +function ENT:SetCPUName(name) + local overlayStr = "" + local a = math.floor(self.VM.SerialNo / 100000) + local b = math.floor(self.VM.SerialNo % 100000) + if name and (name ~= "") then + self:SetOverlayText(string.format("%s\nS/N %05d%05d",name,a,b)) + else + self:SetOverlayText(string.format("Zyelios CPU\nS/N %05d%05d",a,b)) + end + self.CPUName = name +end + +function ENT:BuildDupeInfo() + local info = BaseClass.BuildDupeInfo(self) or {} + + info.SerialNo = self.VM.SerialNo + info.InternalRAMSize = self.VM.RAMSize + info.InternalROMSize = self.VM.ROMSize + info.CPUName = self.CPUName + + if self.VM.ROMSize > 0 then + info.Memory = {} + for k,v in pairs(self.VM.ROM) do if v ~= 0 then info.Memory[k] = v end end + end + + return info +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + + self.VM.SerialNo = info.SerialNo or CPULib.GenerateSN("UNK") + self.VM.RAMSize = info.InternalRAMSize or 65536 + self.VM.ROMSize = info.InternalROMSize or 65536 + self:SetCPUName(info.CPUName) + + if info.Memory then--and + --(((info.UseROM) and (info.UseROM == true)) or + -- ((info.InternalROMSize) and (info.InternalROMSize > 0))) then + self.VM.ROM = {} + for k,v in pairs(info.Memory) do self.VM.ROM[k] = tonumber(v) or 0 end + self.VM:Reset() + end +end + +-- Compatibility with old NMI input +WireLib.AddInputAlias( "NMI", "Interrupt" ) + +function ENT:TriggerInput(iname, value) + if iname == "Clk" then + self.Clk = (value >= 1) + if self.Clk then + self.VMStopped = false + self:NextThink(CurTime()) + end + elseif iname == "Frequency" then + if value > 0 then self.Frequency = math.floor(value) end + elseif iname == "Reset" then --VM may be nil + if self.VM.HWDEBUG ~= 0 then + self.VM.DBGSTATE = math.floor(value) + if (value > 0) and (value <= 1.0) then self.VM:Reset() end + else + if value >= 1.0 then self.VM:Reset() end + end + Wire_TriggerOutput(self, "Error", 0) + elseif iname == "Interrupt" then + if (value >= 32) and (value < 256) then + if (self.Clk and not self.VMStopped) then self.VM:ExternalInterrupt(math.floor(value)) end + end + end +end + +duplicator.RegisterEntityClass("gmod_wire_cpu", WireLib.MakeWireEnt, "Data") diff --git a/lua/entities/gmod_wire_gpu/cl_gpuvm.lua b/lua/entities/gmod_wire_gpu/cl_gpuvm.lua new file mode 100644 index 0000000000..a630e4c4ae --- /dev/null +++ b/lua/entities/gmod_wire_gpu/cl_gpuvm.lua @@ -0,0 +1,2360 @@ +-------------------------------------------------------------------------------- +-- Override virtual machine functions and features +-------------------------------------------------------------------------------- +local VM = {} + +surface.CreateFont( "WireGPU_ErrorFont",{ + font="Coolvetica", + size = 26, + weight = 200, + antialias = true, + additive = false +}) + +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.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[23] = "Error reading string data" + + 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 + + -- 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 + + -- 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 + + + +-------------------------------------------------------------------------------- +-- Switches to a font, creating it if it does not exist +-------------------------------------------------------------------------------- +local fontcache = {} +function VM:SetFont() + local name, size = self.FontName[self.Font], self.FontSize + if not fontcache[name] or not fontcache[name][size] then + if not fontcache[name] then fontcache[name] = {} end + + surface.CreateFont("WireGPU_"..name..size, { + font = name, + size = size, + weight = 800, + antialias = true, + additive = false, + }) + fontcache[name][size] = true + end + + surface.SetFont("WireGPU_"..name..size) +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 + + -- 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) +--self.Memory[65530] = 0 +--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[65513] = 0 (set elsewhere) + self.Memory[65512] = 0 + ---------------------- +--self.Memory[65505] = 0 (set elsewhere) +--self.Memory[65504] = 0 (set elsewhere) + 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 = {} +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", +} +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 +} + +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 + + 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() + + self:SaveAsyncThread_Util() + self.AsyncState.IP = self.EntryPoint4 + return + end + + self:SaveAsyncThread_Util() +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 + + 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() + + self:SaveAsyncThread_Util() + self.AsyncState.IP = self.EntryPoint4 + end + + self:RestoreAsyncThread_Util() +end + + + +-------------------------------------------------------------------------------- +-- Reset GPU state and clear all persisting registers +-------------------------------------------------------------------------------- +function VM:HardReset() + 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 + + self.Memory[65529] = 0 + self.Memory[65528] = 0 + self.Memory[65527] = 60000 + + 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 + + -- Is running asynchronous thread + self.ASYNC = 0 + + -- Has initialized already + self.INIT = 0 + + -- Reset async thread + self.AsyncState = nil +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 + + vertex.u = textureX+(1/texturesOnSide)*du*(1-2*uvStep)+uvStep + vertex.v = textureY+(1/texturesOnSide)*dv*(1-2*uvStep)+uvStep +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 +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 +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 +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 +end + + + + +-------------------------------------------------------------------------------- +-- Get text size (by sk89q) +-------------------------------------------------------------------------------- +function VM:TextSize(text) + self:SetFont() + + if self.WordWrapMode == 1 then + return self.Layouter:GetTextSize(text, self.TextBox.x, self.TextBox.y) + else + return surface.GetTextSize(text) + end +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 +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 +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 + + 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 +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 + + self.Color = self:ColorTransform(color) +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 + + self.Material = material +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 +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 +end + + + + + + + + + +-------------------------------------------------------------------------------- +-- GPU instruction set implementation +-------------------------------------------------------------------------------- +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") +end +VM.OpcodeTable[111] = function(self) --IDLE + 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") +end +VM.OpcodeTable[201] = function(self) --DEXIT + 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") +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") +end +VM.OpcodeTable[204] = function(self) --DVXFLUSH + self:Dyn_Emit("VM:FlushBuffer()") +end +VM.OpcodeTable[205] = function(self) --DVXCLEAR + 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") +end +VM.OpcodeTable[207] = function(self) --DSETBUF_SPR + 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") +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") +end +-------------------------------------------------------------------------------- +VM.OpcodeTable[210] = function(self) --DVXPIPE + self:Dyn_Emit("VM.VertexPipe = $1") +end +VM.OpcodeTable[211] = function(self) --DCVXPIPE + 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") +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") +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") +end +VM.OpcodeTable[215] = function(self) --DCOLOR + self:Dyn_Emit("VM:SetColor(VM:ReadVector4f($1))") + self:Dyn_EmitInterruptCheck() +end +VM.OpcodeTable[216] = function(self) --DTEXTURE + 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 +VM.OpcodeTable[218] = function(self) --DSETSIZE + 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") +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)") +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)") +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") +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") +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") +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)") +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") +end +VM.OpcodeTable[227] = function(self) --DLINE + 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)") +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])") +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() +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() +end +VM.OpcodeTable[232] = function(self) --DSCRSIZE + 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() +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])") +end +VM.OpcodeTable[235] = function(self) --DCULLMODE + self:Dyn_Emit("VM:WriteCell(65469,$1)") + self:Dyn_Emit("VM:WriteCell(65468,$2)") +end +VM.OpcodeTable[236] = function(self) --DARRAY + +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("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() +end +VM.OpcodeTable[241] = function(self) --DWRITEI + 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() +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") +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() +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() +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 (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() +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") +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") +end +-------------------------------------------------------------------------------- +VM.OpcodeTable[271] = function(self) --MLOADPROJ + 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() +end +VM.OpcodeTable[274] = function(self) --DT + 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)") +end +VM.OpcodeTable[277] = function(self) --DSETWIDTH + 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() +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)") +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)") +end +VM.OpcodeTable[283] = function(self) --DRASTER + 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") +end +VM.OpcodeTable[288] = function(self) --DSETTEXTBOX + self:Dyn_Emit("VM.Textbox = VM:ReadVector2f($1)") + self:Dyn_EmitInterruptCheck() +end +VM.OpcodeTable[289] = function(self) --DSETTEXTWRAP + self:Dyn_Emit("VM.WordWrapMode = $1") +end +-------------------------------------------------------------------------------- +VM.OpcodeTable[294] = function(self) --DMULDT + self:Dyn_EmitOperand("$2*VM.TimerDT") +end +VM.OpcodeTable[297] = function(self) --DMULDT + 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") +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") +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") +end + + + + + + +-------------------------------------------------------------------------------- +--ENT._VM = {} +--for k,v in pairs(VM) do ENT._VM[k] = v end +--VM = nil diff --git a/lua/entities/gmod_wire_gpu/cl_init.lua b/lua/entities/gmod_wire_gpu/cl_init.lua new file mode 100644 index 0000000000..6c827a077d --- /dev/null +++ b/lua/entities/gmod_wire_gpu/cl_init.lua @@ -0,0 +1,447 @@ +include("cl_gpuvm.lua") +include("shared.lua") + +local Monitors = {} +local MonitorLookup = {} +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 +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() +end +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() +end +usermessage.Hook("wire_gpu_memorymodel", GPU_MemoryModel) + +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 + + -- 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 + + -- 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 +end + + + + +-------------------------------------------------------------------------------- +-- Entity deleted +function ENT:OnRemove() + GPULib.ClientCacheCallback(self,nil) + self.GPU:Finalize() + if self.SpriteGPU then self.SpriteGPU:Finalize() end +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.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 + self.VM.IP = self.VM.EntryPoint0 + end + + self.VM.ASYNC = 0 + 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))) + local previousTMR = self.VM.TMR + self.VM:Step() + Cycles = Cycles - (self.VM.TMR - previousTMR) + + if (self.VM.ASYNC == 0) and (Cycles < 0) then self.VM:Interrupt(17,0) end + end + + -- Reset INTR register for async thread + if self.VM.ASYNC == 1 then self.VM.INTR = 0 end +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 +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() +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() +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 +end + + +local VECTOR_1_1_1 = Vector(1, 1, 1) +-------------------------------------------------------------------------------- +-- Entity drawing function +function ENT:Draw() + -- Calculate time-related variables + self.CurrentTime = CurTime() + self.DeltaTime = math.min(1/30,self.CurrentTime - (self.PreviousTime or 0)) + self.PreviousTime = self.CurrentTime + + -- Draw GPU itself + self:DrawModel() + + local tone = render.GetToneMappingScaleLinear() + render.SetToneMappingScaleLinear(VECTOR_1_1_1) + + -- Draw image from another GPU + local videoSource = MonitorLookup[self:EntIndex()] + if videoSource then + videoGPU = ents.GetByIndex(videoSource) + if videoGPU and videoGPU:IsValid() and videoGPU.GPU then + videoGPU.GPU.Entity = self + videoGPU.GPU:Render( + videoGPU.VM:ReadCell(65522), videoGPU.VM:ReadCell(65523)-videoGPU.VM:ReadCell(65518)/512, -- rotation, scale + 512*math.Clamp(videoGPU.VM:ReadCell(65525),0,1), 512*math.Clamp(videoGPU.VM:ReadCell(65524),0,1) + ) + videoGPU.GPU.Entity = videoGPU + end + else + if self.DeltaTime > 0 then + -- Run the per-frame GPU thread + if self.VM.Memory[65532] == 0 then + local FrameRate = wire_gpu_frameratio:GetFloat() or 4 + self.FramesSinceRedraw = (self.FramesSinceRedraw or 0) + 1 + self.FrameInstructions = 0 + if self.FramesSinceRedraw >= FrameRate then + self.FramesSinceRedraw = 0 + self:RenderGPU() + end + end + + -- Run asynchronous thread + if self.VM.Memory[65528] == 1 then + self.VM.VertexMode = 0 + if self.VM.LastBuffer < 2 + then self:SetRendertarget(self.VM.LastBuffer) + else self:SetRendertarget() end + + self.VM:RestoreAsyncThread() + self:Run(true) + self.VM:SaveAsyncThread() + + self:SetRendertarget() + end + end + + -- Draw GPU to world + if self.ChipType == 0 then -- Not a microchip + if self.VM.Memory[65532] == 0 then + self.GPU:Render( + self.VM:ReadCell(65522), self.VM:ReadCell(65523)-self.VM:ReadCell(65518)/512, -- rotation, scale + 1024*math.Clamp(self.VM:ReadCell(65525),0,1), 1024*math.Clamp(self.VM:ReadCell(65524),0,1), -- width, height + function(pos, ang, resolution, aspect, monitor) -- postrenderfunction + self:RenderMisc(pos, ang, resolution, aspect, monitor) + end,-0.5,-0.5 + ) + else + -- Custom render to world + local monitor, pos, ang = self.GPU:GetInfo() + + --pos = pos + ang:Up()*zoffset + pos = pos - ang:Right()*(monitor.y2-monitor.y1)/2 + pos = pos - ang:Forward()*(monitor.x2-monitor.x1)/2 + + local width,height = 1024*math.Clamp(self.VM.Memory[65525],0,1), + 1024*math.Clamp(self.VM.Memory[65524],0,1) + + local h = width and width*monitor.RatioX or height or 1024 + local w = width or h/monitor.RatioX + local x = -w/2 + local y = -h/2 + + local res = monitor.RS*1024/h + self.VertexCamSettings = { pos, ang, res } + cam.Start3D2D(pos, ang, res) + self.In3D2D = true + local ok, err = xpcall(function() + self:RenderVertex(1024,1024*monitor.RatioX) + self:RenderMisc(pos, ang, res, 1/monitor.RatioX, monitor) + end, debug.traceback) + if not ok then WireLib.ErrorNoHalt(err) end + if self.In3D2D then self.In3D2D = false cam.End3D2D() end + self.VertexCamSettings = nil + end + end + end + + render.SetToneMappingScaleLinear(tone) + Wire_Render(self) +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 +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: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 new file mode 100644 index 0000000000..12a040e609 --- /dev/null +++ b/lua/entities/gmod_wire_gpu/init.lua @@ -0,0 +1,394 @@ +-- Load shared/clientside stuff +AddCSLuaFile("cl_init.lua") +AddCSLuaFile("cl_gpuvm.lua") +AddCSLuaFile("shared.lua") +include("shared.lua") + +DEFINE_BASECLASS("base_wire_entity") + +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() +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() +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 +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) +end + +local function GPU_PlayerRespawn(player) + 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) + + + +-------------------------------------------------------------------------------- +-- Checks if address is valid +-------------------------------------------------------------------------------- +local function isValidAddress(n) + return n and (math.floor(n) == n) and (n >= -140737488355327) and (n <= 140737488355328) +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 +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 +end + + + +-------------------------------------------------------------------------------- +-- Use key support +-------------------------------------------------------------------------------- +function ENT:Use(player) +-- +end + + +-------------------------------------------------------------------------------- +-- Write advanced dupe +-------------------------------------------------------------------------------- +function ENT:BuildDupeInfo() + local info = BaseClass.BuildDupeInfo(self) or {} + + info.SerialNo = self.SerialNo + info.RAMSize = self.RAMSize + info.ChipType = self.ChipType + info.Memory = {} + + 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 +end + + +-------------------------------------------------------------------------------- +-- Read from advanced dupe +-------------------------------------------------------------------------------- +function ENT:ApplyDupeInfo(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 = {} + + 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) +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 +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 +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 +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 + + -- 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 + + -- 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_gpu/shared.lua b/lua/entities/gmod_wire_gpu/shared.lua new file mode 100644 index 0000000000..63b736e6bc --- /dev/null +++ b/lua/entities/gmod_wire_gpu/shared.lua @@ -0,0 +1,12 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire ZGPU" +ENT.Author = "Black Phoenix" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false + +ENT.RenderGroup = RENDERGROUP_BOTH diff --git a/lua/entities/gmod_wire_spu/cl_init.lua b/lua/entities/gmod_wire_spu/cl_init.lua new file mode 100644 index 0000000000..b8148b41cb --- /dev/null +++ b/lua/entities/gmod_wire_spu/cl_init.lua @@ -0,0 +1,179 @@ +include("cl_spuvm.lua") +include("shared.lua") + + + +-------------------------------------------------------------------------------- +WireSPU_MaxChannels = 32 + +local SoundEmitters = {} +local SoundEmitterLookup = {} +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 +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() +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 +end +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() +end +usermessage.Hook("wire_spu_memorymodel", SPU_MemoryModel) + + + + +-------------------------------------------------------------------------------- +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 + + -- 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 + + + + +-------------------------------------------------------------------------------- +-- 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 +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:Step() + 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 +end diff --git a/lua/entities/gmod_wire_spu/cl_spuvm.lua b/lua/entities/gmod_wire_spu/cl_spuvm.lua new file mode 100644 index 0000000000..cb7a7df4d1 --- /dev/null +++ b/lua/entities/gmod_wire_spu/cl_spuvm.lua @@ -0,0 +1,455 @@ +-------------------------------------------------------------------------------- +-- Override virtual machine functions and features +-------------------------------------------------------------------------------- +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.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.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] +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 +end + +-------------------------------------------------------------------------------- +-- Calculate ADSR Envelope +-------------------------------------------------------------------------------- + +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 +end + +-------------------------------------------------------------------------------- +-- SPU instruction set implementation +-------------------------------------------------------------------------------- +VM.OpcodeTable = {} +VM.OpcodeTable[111] = function(self) --IDLE +-- self:Dyn_Emit("VM.INTR = 1") + 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") +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") +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") +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") +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") +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("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") +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") +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") +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") +end +VM.OpcodeTable[335] = function(self) --CHMODT +end +VM.OpcodeTable[336] = function(self) --CHMODA +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") +end diff --git a/lua/entities/gmod_wire_spu/init.lua b/lua/entities/gmod_wire_spu/init.lua new file mode 100644 index 0000000000..91d26d126a --- /dev/null +++ b/lua/entities/gmod_wire_spu/init.lua @@ -0,0 +1,338 @@ +-- Load shared/clientside stuff +AddCSLuaFile("cl_init.lua") +AddCSLuaFile("cl_spuvm.lua") +AddCSLuaFile("shared.lua") +include("shared.lua") + +DEFINE_BASECLASS("base_wire_entity") + +ENT.WireDebugName = "ZSPU" + +-------------------------------------------------------------------------------- +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()) + self.SoundSources[i].DoNotDuplicate = true + 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 + + 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 + +function ENT:OnRemove() + for i=0,WireSPU_MaxChannels-1 do + self.SoundSources[i]:Remove() + 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_spu_memorymodel") + umsg.Long(self:EntIndex()) + umsg.Long (self.RAMSize) + umsg.Float(self.SerialNo) + umsg.Short(self.ChipType) + umsg.End() + 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) +end + +local function SPU_PlayerRespawn(player) + 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) + + +-------------------------------------------------------------------------------- +-- 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 +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 +end + + +-------------------------------------------------------------------------------- +-- Write advanced dupe +-------------------------------------------------------------------------------- +function ENT:BuildDupeInfo() + local info = BaseClass.BuildDupeInfo(self) or {} + + info.SerialNo = self.SerialNo + info.RAMSize = self.RAMSize + info.ChipType = self.ChipType + info.Memory = {} + + 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 +end + + +-------------------------------------------------------------------------------- +-- Read from advanced dupe +-------------------------------------------------------------------------------- +function ENT:ApplyDupeInfo(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 = {} + + 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) +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 +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 +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 +end + +duplicator.RegisterEntityClass("gmod_wire_spu", WireLib.MakeWireEnt, "Data") diff --git a/lua/entities/gmod_wire_spu/shared.lua b/lua/entities/gmod_wire_spu/shared.lua new file mode 100644 index 0000000000..a85189ad58 --- /dev/null +++ b/lua/entities/gmod_wire_spu/shared.lua @@ -0,0 +1,10 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire ZSPU" +ENT.Author = "Black Phoenix" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false diff --git a/lua/wire/client/hlzasm/hc_codetree.lua b/lua/wire/client/hlzasm/hc_codetree.lua new file mode 100644 index 0000000000..6f28460ebd --- /dev/null +++ b/lua/wire/client/hlzasm/hc_codetree.lua @@ -0,0 +1,690 @@ +-------------------------------------------------------------------------------- +-- 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 +end + +function HCOMP:NewOpcode(opcode,op1,op2) + 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: +-- Register - number of register to use (if it's an "eax" or "es:eax" operand) +-- Constant - constant value to use (if it's an "123" or "es:123" or "123:es" operand) +-- Memory - leaf of memory block to use (if it's an "ptr" or "es:ptr" or "ptr:es" operand) +-- MemoryPointer - explict pointer to memory (can be constant or a leaf) +-- Segment - number of segment register to use +-- MemoryRegister - memory by register (if it's an "#eax" or es:#eax +-- UnknownOperationByLabel - operation type will be determined by label +-- PointerToLabel - operation to retrieve pointer by label +-- Stack - stack operation (can be constant offset or a leaf) +-- TrigonometryHack - operand copied from the first one +-- PreviousLeaf - leaf that must be generated prior to calculating operand +-- ForceTemporary - forces this register to be marked busy & temporary + +-- Each leaf contains the following REQUIRED items: +-- Opcode (required) +-- Operands (required if opcode is asm one) +-- CurrentPosition (position in the sourcecode leaf corresponds to) + +-- Each leaf can contain the following OPTIONAL markers: +-- ForceType - force type of the operation (int, vector, matrix) +-- ExplictAssign (true/nil, the opcode MUST be performed. Result returned BEFORE the operation finished) +-- ReturnAfterAssign (true/nil, if this and ExplictAssign are set, result is returned AFTER the operation finished) +-- ZeroPadding (amount of zero bytes that must be padded after the leaf) +-- PreviousLeaf (leaf that must be generated prior to generating this one) +-- Comment (extra commentary inserted before leaf in assembly listing) +-- Label (label this leaf corresponds to) +-- Data (extra data to be written after the leaf) +-- SetWritePointer (command for leaf to change write pointer in the output stream) +-- SetPointerOffset (command for leaf to change offset in label pointers in output stream) +-- BusyRegisters (array of busy registers for this leaf) +-- ParentLabel (which label this leaf is assigned to, if label was not referenced, this code is not generated) + +-- Special opcodes: +-- LABEL - label leaf (has no size) +-- DATA - data leaf (allocated data) +-- MARKER - marker which tells how to resync write pointer + +-- Types of labels +-- Unknown (undefined) +-- Pointer +-- Variable +-- Stack +-- Register + +-- Label can have extra marker: +-- Array (is this variable an array?) + +-------------------------------------------------------------------------------- +-- 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 +end + + +-------------------------------------------------------------------------------- +-- Returns free (non-busy) register. Does not check EBP and ESP +function HCOMP:FreeRegister() + -- Try to find a free register + for i=1,6 do + if not self.RegisterBusy[i] then return i end + end + + -- 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() +-- pushLeaf.Opcode = "push" +-- pushLeaf.Operands[1] = { Register = i } +-- self.RegisterStackOffset[i] = 0 +-- end +-- end + -- FIXME: non-busy register must always exist? + + 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 +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 +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 +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, 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, 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 + 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 +end + + + + + + + + + +-------------------------------------------------------------------------------- +-- This is THE BEST function. Its the one which turns code tree into generated +-- code. +-- +-- Generates code for the leaf. Returns register which stores result, if required +-- +-- MASSIVE NOTE: it returns WHETHER THE REGISTER >>>MUST<<< BE MADE TEMPORARY, +-- not that it's actually temporary! +-- +function HCOMP:GenerateLeaf(leaf,needResult) + -- 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 +-- for i=1,#leaf.Operands do +-- if leaf.Operands[i].PreviousLeaf then +-- self:GenerateLeaf(leaf.Operands[i].PreviousLeaf,false) +-- leaf.Operands[i].PreviousLeaf = nil +-- 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 +-- 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 = genOperands[i].Register + 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 +-- if not (isDestTemp and (destRegister == k)) then +-- if self.RegisterBusy[k] ~= v then +-- print("Internal error 564: register mismatch "..k.." ~= "..tostring(v).." @ "..leaf.Opcode,leaf) +-- end +-- end +-- end + + return destRegister,isDestTemp +end + + +-------------------------------------------------------------------------------- +-- Generate opcode (writes it to the generated code list) +function HCOMP:GenerateOpcode(leaf,operands) +-- local registerBusyHint = {} +-- for k,v in pairs(self.RegisterBusy) do registerBusyHint[k] = v end +-- RegisterBusyHint = registerBusyHint, -- Hint on temporary registers for the optimizer + +-- local rstr = "" +-- 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, + }) +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, + }) +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 +end diff --git a/lua/wire/client/hlzasm/hc_compiler.lua b/lua/wire/client/hlzasm/hc_compiler.lua new file mode 100644 index 0000000000..b1336fefc2 --- /dev/null +++ b/lua/wire/client/hlzasm/hc_compiler.lua @@ -0,0 +1,872 @@ +-------------------------------------------------------------------------------- +-- HCOMP / HL-ZASM compiler +-- +-- This is a high-level assembly language compiler, based on ZASM2 and C syntax. +-- +-- I tried to make the compiler as understandable as possible, but you will need +-- to read the source files in this order to understand internal workings of +-- the compiler. Each of these files performs own task, and is independant of others. +-- +-- hc_opcodes: lists all opcodes that are recognized by the compiler +-- hc_compiler: compiler initialization +-- error reporting +-- parser functions +-- hc_preprocess: preprocessor macro parsing +-- hc_tokenizer: turns source code into tokens +-- hc_codetree: unwraps code tree into generated code +-- hc_output: resolves labels and generates +-- hc_syntax: parses language syntax +-- hc_expression: parses expressions +-- hc_optimize: optimizes the resulting code +-------------------------------------------------------------------------------- +HCOMP = {} + + + + +-------------------------------------------------------------------------------- +-- Files required by the compiler +include("wire/client/hlzasm/hc_opcodes.lua") +include("wire/client/hlzasm/hc_expression.lua") +include("wire/client/hlzasm/hc_preprocess.lua") +include("wire/client/hlzasm/hc_syntax.lua") +include("wire/client/hlzasm/hc_codetree.lua") +include("wire/client/hlzasm/hc_optimize.lua") +include("wire/client/hlzasm/hc_output.lua") +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 +end + + + + +-- Display an error message. There are three ways to call it: +-- +-- Error(msg) +-- Raise an error when parsing code +-- Error(msg,block) +-- Raise an error when generating or outputting block +-- 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) +end + + + + +-- Display a warning message +-- +-- Same rules for calling as for Error() +function HCOMP:Warning(msg,param1,param2,param3) + print(self:formatPrefix(param1,param2,param3)..": Warning: "..msg) +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 +end + + + + +-------------------------------------------------------------------------------- +-- Emit a code byte to the output stream +function HCOMP:WriteByte(byte,block) + 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 + + + + +-------------------------------------------------------------------------------- +-- Start compiling the sourcecode. File name will define working directory +-- +-- 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 + + -- 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.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 + + -- 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 = {} + + -- Exported function list (library generation) + self.ExportedSymbols = {} + self.LabelLookup = {} + self.LabelLookupCounter = 0 + + -- All functions defined so far + self.Functions = {} + + -- All macros defined so far + self.Defines = {} + self.Defines["__LINE__"] = 0 + self.Defines["__FILE__"] = "" + self.IFDEFLevel = {} + + -- Output text + self.OutputText = {} +end + + + + +-------------------------------------------------------------------------------- +-- Call this until the function returns false (it returns false when there +-- is nothing more to do) +function HCOMP:Compile() + return self:UnprotectedCompile() +-- local status,result = pcall(self.UnprotectedCompile,self) +-- if not status then +-- print("ERROR: "..result) +-- else +-- return result +-- end +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 +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 +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 +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 +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] +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 +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 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 + + + + +-------------------------------------------------------------------------------- +-- 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 +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 +end + + + + +-------------------------------------------------------------------------------- +-- Binary OR +function HCOMP:BinaryOr(m,n) + 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 + + return self:BinaryToInteger(bits) +end + + + + +-------------------------------------------------------------------------------- +-- Binary AND +function HCOMP:BinaryAnd(m,n) + 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 + + return self:BinaryToInteger(bits) +end + + + + +-------------------------------------------------------------------------------- +-- Binary NOT +function HCOMP:BinaryNot(n) + 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) +end + + + + +-------------------------------------------------------------------------------- +-- Binary XOR +function HCOMP:BinaryXor(m,n) + 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 + + return self:BinaryToInteger(bits) +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) +end + + + + +-------------------------------------------------------------------------------- +-- Binary shift left +function HCOMP:BinarySHL(n,cnt) + 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 + + return self:BinaryToInteger(bits) +end diff --git a/lua/wire/client/hlzasm/hc_expression.lua b/lua/wire/client/hlzasm/hc_expression.lua new file mode 100644 index 0000000000..a6b07731ec --- /dev/null +++ b/lua/wire/client/hlzasm/hc_expression.lua @@ -0,0 +1,955 @@ +-------------------------------------------------------------------------------- +-- HCOMP / HL-ZASM compiler +-- +-- Dynamic and constant expression generator +-------------------------------------------------------------------------------- + + + + +-------------------------------------------------------------------------------- +-- 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 +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 + 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 = self:NewLeaf() + pushLeaf.Opcode = "push" + pushLeaf.Operands[1] = argumentExpression[argNo] + table.insert(genLeaves,pushLeaf) + + 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 + 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 + 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 } + 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 + operationLeaf = { Memory = label, ForceType = forceType } + 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 + 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 +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 +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) + + -- 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 +end + + + + + + + + + + +-------------------------------------------------------------------------------- +-- Constant expression parser +-- Each level function returns 3 values +-- 1 Is return value constant (true/false) +-- 2 Is return value precise (false if value depends on floating pointer) +-- 3 Return value (or magic value) + +--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 + -- 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 + -- 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 +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 +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 +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 +end diff --git a/lua/wire/client/hlzasm/hc_opcodes.lua b/lua/wire/client/hlzasm/hc_opcodes.lua new file mode 100644 index 0000000000..ddd91f245e --- /dev/null +++ b/lua/wire/client/hlzasm/hc_opcodes.lua @@ -0,0 +1,43 @@ +-------------------------------------------------------------------------------- +-- HCOMP / HL-ZASM compiler +-- +-- Opcode definition file +-------------------------------------------------------------------------------- + + + + +-------------------------------------------------------------------------------- +-- Initialize opcode count lookup table +HCOMP.OperandCount = {} +for _,instruction in pairs(CPULib.InstructionTable) do + HCOMP.OperandCount[instruction.Opcode] = instruction.OperandCount +end + +-- Initialize table of single-operand instructions which write 1st operand +HCOMP.OpcodeWritesOperand = {} +for _,instruction in pairs(CPULib.InstructionTable) do + if instruction.WritesFirstOperand and (instruction.Mnemonic ~= "RESERVED") then + HCOMP.OpcodeWritesOperand[string.lower(instruction.Mnemonic)] = true + end +end + +-- Initialize opcode number lookup table +HCOMP.OpcodeNumber = {} +for _,instruction in pairs(CPULib.InstructionTable) do + if instruction.Mnemonic ~= "RESERVED" then + HCOMP.OpcodeNumber[string.lower(instruction.Mnemonic)] = instruction.Opcode + end +end + +-- Initialize list of obsolete/old opcodes +HCOMP.OpcodeObsolete = {} +HCOMP.OpcodeOld = {} +for _,instruction in pairs(CPULib.InstructionTable) 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 diff --git a/lua/wire/client/hlzasm/hc_optimize.lua b/lua/wire/client/hlzasm/hc_optimize.lua new file mode 100644 index 0000000000..64acbbda01 --- /dev/null +++ b/lua/wire/client/hlzasm/hc_optimize.lua @@ -0,0 +1,136 @@ +-------------------------------------------------------------------------------- +-- HCOMP / HL-ZASM compiler +-- +-- Optimizer +-------------------------------------------------------------------------------- + + + + +local OptimizationPattern = { +-------------------------------------------------------------------------------- + {{{"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"}, + }, + }, +-------------------------------------------------------------------------------- + {{{"mov","A","A"}, + }, + { + }, + } +} + + + + +-------------------------------------------------------------------------------- +-- 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) +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 +end diff --git a/lua/wire/client/hlzasm/hc_output.lua b/lua/wire/client/hlzasm/hc_output.lua new file mode 100644 index 0000000000..0e9cbba660 --- /dev/null +++ b/lua/wire/client/hlzasm/hc_output.lua @@ -0,0 +1,647 @@ +-------------------------------------------------------------------------------- +-- HCOMP / HL-ZASM compiler +-- +-- Code resolver and output generator +-------------------------------------------------------------------------------- + + + + +-------------------------------------------------------------------------------- +-- 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 +-- self:PrintBlock(block,"lib",true) + end + return false +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 +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) +end + + + + +-------------------------------------------------------------------------------- +-- Lookup for CPU registers +local RegisterName = {} +RegisterName[01] = "EAX" +RegisterName[02] = "EBX" +RegisterName[03] = "ECX" +RegisterName[04] = "EDX" +RegisterName[05] = "ESI" +RegisterName[06] = "EDI" +RegisterName[07] = "ESP" +RegisterName[08] = "EBP" +RegisterName[16] = "CS" +RegisterName[17] = "SS" +RegisterName[18] = "DS" +RegisterName[19] = "ES" +RegisterName[20] = "GS" +RegisterName[21] = "FS" +RegisterName[22] = "KS" +RegisterName[23] = "LS" +for port=0,1023 do RegisterName[1024+port] = "port"..port end +for reg=0,31 do RegisterName[96+reg] = "R"..reg end + + +local SegmentRegisterName = {} +SegmentRegisterName[01] = "CS" +SegmentRegisterName[02] = "SS" +SegmentRegisterName[03] = "DS" +SegmentRegisterName[04] = "ES" +SegmentRegisterName[05] = "GS" +SegmentRegisterName[06] = "FS" +SegmentRegisterName[07] = "KS" +SegmentRegisterName[08] = "LS" +SegmentRegisterName[09] = "EAX" +SegmentRegisterName[10] = "EBX" +SegmentRegisterName[11] = "ECX" +SegmentRegisterName[12] = "EDX" +SegmentRegisterName[13] = "ESI" +SegmentRegisterName[14] = "EDI" +SegmentRegisterName[15] = "ESP" +SegmentRegisterName[16] = "EBP" +for reg=0,31 do SegmentRegisterName[17+reg] = "R"..reg end + + + + +-------------------------------------------------------------------------------- +-- 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 istable(block.Operands[i].Memory) then + if block.Operands[i].Memory.Value + then printText = printText .. "#" .. block.Operands[i].Memory.Value + 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 + + + + +-------------------------------------------------------------------------------- +-- 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) + + 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 +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 + operand.Value = operand.Memory.Value + 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 + + + + +-------------------------------------------------------------------------------- +-- 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 + + -- 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) + -- 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) + + -- 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 new file mode 100644 index 0000000000..9d6d951d0e --- /dev/null +++ b/lua/wire/client/hlzasm/hc_preprocess.lua @@ -0,0 +1,164 @@ +-------------------------------------------------------------------------------- +-- HCOMP / HL-ZASM compiler +-- +-- Preprocessor macro parser +-------------------------------------------------------------------------------- + + + +-------------------------------------------------------------------------------- +-- Load file +function HCOMP:LoadFile(filename) + return file.Read("data/"..self.Settings.CurrentPlatform.."Chip/"..filename, "GAME") -- So we also get /addons/wire/data/ +end + +-- Save file +function HCOMP:SaveFile(filename,text) + file.Write(self.Settings.CurrentPlatform.."Chip/"..filename,text) +end + +-- Trim spaces at string sides +local function trimString(str) + return string.gsub(str, "^%s*(.-)%s*$", "%1") +end + + + + +-------------------------------------------------------------------------------- +-- Handle preprocessor macro +function HCOMP:ParsePreprocessMacro(lineText,macroPosition) + -- 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)) + + 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 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, 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) + 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 + 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 + 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] = 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 + + -- 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 + + -- Push this file on top of the stack + if fileText then + table.insert(self.Code, 1, { Text = fileText, Line = 1, Col = 1, File = fileName, 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 new file mode 100644 index 0000000000..44ac90589d --- /dev/null +++ b/lua/wire/client/hlzasm/hc_syntax.lua @@ -0,0 +1,1513 @@ +-------------------------------------------------------------------------------- +-- ZASM2 compatible syntax +-------------------------------------------------------------------------------- + + + + +-- 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 = {}, +} +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 = self.TOKEN + 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 } + 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 +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 +end + + +-------------------------------------------------------------------------------- +function HCOMP:DeclareRegisterVariable() + if self.BlockDepth > 0 then + for reg=1,6 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 = self.TOKEN + local varType,varSize,isStruct + if self:MatchToken(TOKEN.IDENT) then -- Define structure + varType = self.TokenData + varSize = 0 -- Depends on pointer level + isStruct = true + 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) + + 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() + 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() + 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 = self.TOKEN + -- 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 + 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) do + self.BusyRegisters[self.TokenData] = tokenType == TOKEN.PRESERVE + 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 returnLeaf = self:NewLeaf() + returnLeaf.Opcode = "mov" + returnLeaf.Operands[1] = { Register = 1 } + returnLeaf.Operands[2] = returnExpression + returnLeaf.ExplictAssign = true + self:AddLeafToTail(returnLeaf) + 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() +-- return true +-- else +-- self:Error("Unexpected bracket") +-- end +-- 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 +-- 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) + + if not needBlock then + -- Parse next statement if dont need a block + self:Statement() + + -- End the IF block early + self:BlockEnd() + + -- Add exit label + self:AddLeafToTail(conditionalBreakLeaf) + + -- 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 + 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 +end diff --git a/lua/wire/client/hlzasm/hc_tokenizer.lua b/lua/wire/client/hlzasm/hc_tokenizer.lua new file mode 100644 index 0000000000..b4ba79f86e --- /dev/null +++ b/lua/wire/client/hlzasm/hc_tokenizer.lua @@ -0,0 +1,585 @@ +-------------------------------------------------------------------------------- +-- HCOMP / HL-ZASM compiler +-- +-- Tokenizer +-------------------------------------------------------------------------------- + + + + +-------------------------------------------------------------------------------- +-- All symbols (tokens) recognized by parser +HCOMP.TOKEN_TEXT = {} +HCOMP.TOKEN_TEXT["IDENT"] = {{"ZASM","HLZASM"},{}} -- ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz _ +HCOMP.TOKEN_TEXT["NUMBER"] = {{"ZASM","HLZASM"},{}} -- 0123456789 +HCOMP.TOKEN_TEXT["LPAREN"] = {{"ZASM","HLZASM"},{"("}} +HCOMP.TOKEN_TEXT["RPAREN"] = {{"ZASM","HLZASM"},{")"}} +HCOMP.TOKEN_TEXT["LBRACKET"] = {{ "HLZASM"},{"{"}} +HCOMP.TOKEN_TEXT["RBRACKET"] = {{ "HLZASM"},{"}"}} +HCOMP.TOKEN_TEXT["LSUBSCR"] = {{"ZASM","HLZASM"},{"["}} +HCOMP.TOKEN_TEXT["RSUBSCR"] = {{"ZASM","HLZASM"},{"]"}} +HCOMP.TOKEN_TEXT["COLON"] = {{"ZASM","HLZASM"},{";"}} +HCOMP.TOKEN_TEXT["DCOLON"] = {{"ZASM","HLZASM"},{":"}} +HCOMP.TOKEN_TEXT["HASH"] = {{"ZASM","HLZASM"},{"#"}} +HCOMP.TOKEN_TEXT["TIMES"] = {{"ZASM","HLZASM"},{"*"}} +HCOMP.TOKEN_TEXT["SLASH"] = {{"ZASM","HLZASM"},{"/"}} +HCOMP.TOKEN_TEXT["MODULUS"] = {{ "HLZASM"},{"%"}} +HCOMP.TOKEN_TEXT["PLUS"] = {{"ZASM","HLZASM"},{"+"}} +HCOMP.TOKEN_TEXT["MINUS"] = {{"ZASM","HLZASM"},{"-"}} +HCOMP.TOKEN_TEXT["AND"] = {{ "HLZASM"},{"&"}} +HCOMP.TOKEN_TEXT["OR"] = {{ "HLZASM"},{"|"}} +HCOMP.TOKEN_TEXT["XOR"] = {{ "HLZASM"},{"^"}} +HCOMP.TOKEN_TEXT["POWER"] = {{ "HLZASM"},{"^^"}} +HCOMP.TOKEN_TEXT["INC"] = {{ "HLZASM"},{"++"}} +HCOMP.TOKEN_TEXT["DEC"] = {{ "HLZASM"},{"--"}} +HCOMP.TOKEN_TEXT["SHL"] = {{ "HLZASM"},{"<<"}} +HCOMP.TOKEN_TEXT["SHR"] = {{ "HLZASM"},{">>"}} +HCOMP.TOKEN_TEXT["EQL"] = {{ "HLZASM"},{"=="}} +HCOMP.TOKEN_TEXT["NEQ"] = {{ "HLZASM"},{"!="}} +HCOMP.TOKEN_TEXT["LEQ"] = {{ "HLZASM"},{"<="}} +HCOMP.TOKEN_TEXT["LSS"] = {{ "HLZASM"},{"<"}} +HCOMP.TOKEN_TEXT["GEQ"] = {{ "HLZASM"},{">="}} +HCOMP.TOKEN_TEXT["GTR"] = {{ "HLZASM"},{">"}} +HCOMP.TOKEN_TEXT["NOT"] = {{ "HLZASM"},{"!"}} +HCOMP.TOKEN_TEXT["EQUAL"] = {{ "HLZASM"},{"="}} +HCOMP.TOKEN_TEXT["LAND"] = {{ "HLZASM"},{"&&"}} +HCOMP.TOKEN_TEXT["LOR"] = {{ "HLZASM"},{"||"}} +HCOMP.TOKEN_TEXT["EQLADD"] = {{ "HLZASM"},{"+="}} +HCOMP.TOKEN_TEXT["EQLSUB"] = {{ "HLZASM"},{"-="}} +HCOMP.TOKEN_TEXT["EQLMUL"] = {{ "HLZASM"},{"*="}} +HCOMP.TOKEN_TEXT["EQLDIV"] = {{ "HLZASM"},{"/="}} +HCOMP.TOKEN_TEXT["COMMA"] = {{"ZASM","HLZASM"},{","}} +HCOMP.TOKEN_TEXT["DOT"] = {{"ZASM","HLZASM"},{"."}} + +HCOMP.TOKEN_TEXT["GOTO"] = {{"HLZASM"},{"GOTO"}} +HCOMP.TOKEN_TEXT["FOR"] = {{"HLZASM"},{"FOR"}} +HCOMP.TOKEN_TEXT["IF"] = {{"HLZASM"},{"IF"}} +HCOMP.TOKEN_TEXT["ELSE"] = {{"HLZASM"},{"ELSE"}} +HCOMP.TOKEN_TEXT["WHILE"] = {{"HLZASM"},{"WHILE"}} +HCOMP.TOKEN_TEXT["DO"] = {{"HLZASM"},{"DO"}} +HCOMP.TOKEN_TEXT["SWITCH"] = {{"HLZASM"},{"SWITCH"}} +HCOMP.TOKEN_TEXT["CASE"] = {{"HLZASM"},{"CASE"}} +HCOMP.TOKEN_TEXT["CONST"] = {{"HLZASM"},{"CONST"}} +HCOMP.TOKEN_TEXT["RETURN"] = {{"HLZASM"},{"RETURN"}} +HCOMP.TOKEN_TEXT["BREAK"] = {{"HLZASM"},{"BREAK"}} +HCOMP.TOKEN_TEXT["CONTINUE"] = {{"HLZASM"},{"CONTINUE"}} +HCOMP.TOKEN_TEXT["EXPORT"] = {{"HLZASM"},{"EXPORT"}} +HCOMP.TOKEN_TEXT["INLINE"] = {{"HLZASM"},{"INLINE"}} +HCOMP.TOKEN_TEXT["FORWARD"] = {{"HLZASM"},{"FORWARD"}} +HCOMP.TOKEN_TEXT["LREGISTER"] = {{"HLZASM"},{"REGISTER"}} +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"}} + +HCOMP.TOKEN_TEXT["STRALLOC"] = {{"ZASM","HLZASM"},{"STRING"}} +HCOMP.TOKEN_TEXT["DB"] = {{"ZASM","HLZASM"},{"DB"}} +HCOMP.TOKEN_TEXT["DEFINE"] = {{"ZASM","HLZASM"},{"DEFINE"}} +HCOMP.TOKEN_TEXT["CODE"] = {{"ZASM","HLZASM"},{"CODE"}} +HCOMP.TOKEN_TEXT["DATA"] = {{"ZASM","HLZASM"},{"DATA"}} +HCOMP.TOKEN_TEXT["ORG"] = {{"ZASM","HLZASM"},{"ORG"}} +HCOMP.TOKEN_TEXT["OFFSET"] = {{"ZASM","HLZASM"},{"OFFSET"}} +HCOMP.TOKEN_TEXT["TYPE"] = {{"ZASM","HLZASM"},{"VOID","FLOAT","CHAR","INT48","VECTOR"}} +HCOMP.TOKEN_TEXT["USERTYPE"] = {{"ZASM","HLZASM"},{}} + +HCOMP.TOKEN_TEXT["PRESERVE"] = {{"HLZASM"},{"PRESERVE"}} +HCOMP.TOKEN_TEXT["ZAP"] = {{"HLZASM"},{"ZAP"}} + +HCOMP.TOKEN_TEXT["REGISTER"] = {{"ZASM","HLZASM"},{"EAX","EBX","ECX","EDX","ESI","EDI","ESP","EBP"}} +HCOMP.TOKEN_TEXT["SEGMENT"] = {{"ZASM","HLZASM"},{"CS","SS","DS","ES","GS","FS","KS","LS"}} +HCOMP.TOKEN_TEXT["OPCODE"] = {{"ZASM","HLZASM"},{}} -- mov, cmp, etc... +HCOMP.TOKEN_TEXT["COMMENT1"] = {{"ZASM","HLZASM"},{"//"}} -- comment 1 +HCOMP.TOKEN_TEXT["COMMENT2"] = {{"ZASM","HLZASM"},{"/*"}} -- comment 2 +HCOMP.TOKEN_TEXT["COMMENT3"] = {{"ZASM","HLZASM"},{"*/"}} -- comment 3 +HCOMP.TOKEN_TEXT["MACRO"] = {{"ZASM","HLZASM"},{}} -- preprocessor macro +HCOMP.TOKEN_TEXT["STRING"] = {{"ZASM","HLZASM"},{}} -- buffer of chars +HCOMP.TOKEN_TEXT["CHAR"] = {{"ZASM","HLZASM"},{}} -- single character +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 +end + +-- Add extended registers +for reg=0,31 do + HCOMP.TOKEN_TEXT["REGISTER"][2][96+reg] = "R"..reg +end + + + + +-------------------------------------------------------------------------------- +-- Generate table of all possible tokens +HCOMP.TOKEN = {} +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 +end + +-- 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 +end + + +-- Create lookup table for symbols and double-character tokens +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 +end + + +-- 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 + + + + +-------------------------------------------------------------------------------- +-- 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 +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 +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 +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 + (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 } + + -- 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 + + 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 +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 +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 +end + + + + +-- Returns true and skips a token if it matches this one +function HCOMP:MatchToken(tok) + 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 + + + + +-- Go to next token +function HCOMP:NextToken() + self.CurrentToken = self.CurrentToken + 1 +end + +-- Go to previous token +function HCOMP:PreviousToken() + 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 +end + + + + +-- Store current parser state (so code could be reparsed again later) +function HCOMP:SaveParserState() + self.SavedToken = self.CurrentToken +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 +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 +end + + + + +-- Returns current position in source file +function HCOMP:CurrentSourcePosition() + if self.Tokens[self.CurrentToken-1] then + return self.Tokens[self.CurrentToken-1].Position + else + return { Line = 1, Col = 1, File = "HL-ZASM" } + end +end diff --git a/lua/wire/client/text_editor/modes/zcpu.lua b/lua/wire/client/text_editor/modes/zcpu.lua new file mode 100644 index 0000000000..d9fc97af57 --- /dev/null +++ b/lua/wire/client/text_editor/modes/zcpu.lua @@ -0,0 +1,325 @@ +local math_floor = math.floor +local string_gmatch = string.gmatch +local string_gsub = string.gsub +local draw_WordBox = draw.WordBox + +local EDITOR = {} + +-- 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 +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}, +} + +-- 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 +end + +-- 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" +} + +local keywordsTable = {} +for k,v in pairs(keywordsList) do + keywordsTable[v] = true +end + +-- 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 +} +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, +} + +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 +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 } +end + +function EDITOR:ShowContextHelp(word) + E2Helper.Show() + E2Helper.UseCPU(self:GetParent().EditorType) + 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 +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" + + 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] then + tokenname = "keyword" + elseif tonumber(self.tokendata) then + tokenname = "number" + else + tokenname = "normal" + end + 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 +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 +end + +WireTextEditor.Modes.ZCPU = EDITOR diff --git a/lua/wire/cpu_default_data_decompressor.lua b/lua/wire/cpu_default_data_decompressor.lua new file mode 100644 index 0000000000..27a991be7a --- /dev/null +++ b/lua/wire/cpu_default_data_decompressor.lua @@ -0,0 +1,49 @@ +-- 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 +} + +-- 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("addons/wire-cpu/data/"..root.."*","GAME") + for _, f in pairs(files) do + f = root..f + tab[f] = file.Read("addons/wire-cpu/data/"..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 + file.Write(f, contents) + else + file.CreateDir(f) + WriteDir(contents) + end + end +end + +-- Only expand the files if they aren't present already +if CLIENT and not file.Exists("cpuchip/examples/helloworld.txt", "DATA") then + local compressed = file.Read("wire/cpu_default_data_files.lua","LUA") + -- The client cannot read lua files sent by the server (for security?), so clientside this'll only work + -- if the client actually has Wiremod installed, though with workshop autodownload that'll be common + if compressed ~= nil then + WriteDir(util.JSONToTable(string.sub(compressed, 3))) + end +end \ No newline at end of file diff --git a/lua/wire/cpu_default_data_files.lua b/lua/wire/cpu_default_data_files.lua new file mode 100644 index 0000000000..ffb611bed6 --- /dev/null +++ b/lua/wire/cpu_default_data_files.lua @@ -0,0 +1 @@ +--{"gpuchip/":{"gpuchip/examples/":{"gpuchip/examples/sprite.txt":"// Author: Drunkie\n// Description: A very simple sprite example\n\nMain();\n\n#include \n\nvoid Main()\n{\n // Enable vertex mode\n glVertexMode( 1 );\n\n // Draw to sprite buffer\n glSetRenderTarget( GL_BUFFER_BACK );\n glClear( 0, 255, 0 );\n\n // Draw to vertex buffer (world)\n glSetRenderTarget( GL_BUFFER_VERTEX );\n glEnable( GL_VERTEX_TEXTURING );\n\n // Sample from sprite 0\n glTextureSize( 256 );\n glTexture( 0 );\n glClear( 0, 255, 0 );\n glRectWH( 128, 128, 256, 256 );\n\n glSetRenderTarget( GL_BUFFER_FRONT );\n\n glExit();\n}\n","gpuchip/examples/3d_icosahedron.txt":"// Author: Drunkie\n// Description: Draws a 3D icosahedron model (solid and wireframe)\n\nMain();\n\n#include \n\nvoid Main()\n{\n glSleep( 40 ); // Sleep for 40 milliseconds (Reduces fps lag)\n glClear( 0, 0, 0 ); // Clear screen\n\n glCoordPipe( GL_CPIPE_N1_1 ); // Set coordinate pipe to [-1 to 1] mode\n glVertexPipe( GL_VPIPE_XYZTRANSFORM ); // Set vertex pipe to xyz transformation\n\n glLightPos( 0, 0, -50 ); // Set the light position\n glLightColor( 255, 255, 255, 1 ); // Set the light color\n\n glLookAt(\n 0, 0, -2.25, // Camera pos\n 0, 0, 0, // Camera target\n 0, 1, 0 // Camera up\n );\n\n // Create variable to hold curtime\n float time;\n timer time;\n\n // Create perspective and matrix transformations\n glPerspective( 30, 1, 1, 20 ); // FOV, ASPECT RATIO, ZNEAR, ZFAR\n glRotate( 1, 1, 0, time ); // AXIS X, Y, Z, ANGLE W\n glTranslate( 0, 0, 0 ); // TRANSLATION X, Y, Z\n glScale( 1, 1, 1, 0 ); // SCALE X, Y, Z\n\n glEnable( GL_VERTEX_BUFFER ); // Enable vertex buffer\n glEnable( GL_VERTEX_ZSORT ); // Enable Z sorting\n glEnable( GL_VERTEX_LIGHTING ); // Enable vertex lighting\n //glEnable( GL_VERTEX_CULLING ); // Enable face culling\n\n // Solid 3D polygon\n glFillMode( GL_FILL_SOLID ); // Set fillmode as solid\n glColor4( 100, 149, 237, 180 ); // Set the draw color with alpha\n glPoly3D( vertexBuffer, 20 ); // Draw 3D polygon\n glFlush(); // Flush the vertex buffer to the screen\n\n glDisable( GL_VERTEX_LIGHTING ); // Enable vertex lighting\n\n // Wireframe 3D polygon\n glLineWidth( 1 ); // Set line width of wireframe\n glFillMode( GL_FILL_WIREFRAME ); // Set fillmode as wireframe\n glColor4( 255, 255, 255, 255 ); // Set the draw color with alpha\n glPoly3D( vertexBuffer, 20 ); // Draw 3D polygon\n glFlush(); // Flush the vertex buffer to the screen\n\n glExit(); // Exit\n}\n\n// The vertex data for our model\nvertexBuffer:\ndb 0,0,1; db 0,0.9,0.5; db 0.9,0.3,0.4;\ndb 0,0,1; db -0.9,0.3,0.4; db 0,0.9,0.5;\ndb 0,0,1; db -0.5,-0.7,0.4; db -0.9,0.3,0.4;\ndb 0,0,1; db 0.5,-0.7,0.4; db -0.5,-0.7,0.4;\ndb 0,0,1; db 0.9,0.3,0.4; db 0.5,-0.7,0.4;\ndb 0.9,-0.3,-0.4; db 0.9,0.3,0.4; db 0.5,0.7,-0.4;\ndb 0,0.9,0.5; db 0.5,0.7,-0.4; db 0.9,0.3,0.4;\ndb 0,0.9,0.5; db -0.5,0.7,-0.4; db 0.5,0.7,-0.4;\ndb 0,0.9,0.5; db -0.9,0.3,0.4; db -0.5,0.7,-0.4;\ndb -0.9,-0.3,-0.4; db -0.5,0.7,-0.4; db -0.9,0.3,0.4;\ndb -0.9,-0.3,-0.4; db -0.9,0.3,0.4; db -0.5,-0.7,0.4;\ndb -0.9,-0.3,-0.4; db -0.5,-0.7,0.4; db 0,-0.9,-0.5;\ndb 0.5,-0.7,0.4; db 0,-0.9,-0.5; db -0.5,-0.7,0.4;\ndb 0.5,-0.7,0.4; db 0.9,-0.3,-0.4; db 0,-0.9,-0.5;\ndb 0.5,-0.7,0.4; db 0.9,0.3,0.4; db 0.9,-0.3,-0.4;\ndb 0,0,-1; db 0,-0.9,-0.5; db 0.9,-0.3,-0.4;\ndb 0,0,-1; db 0.9,-0.3,-0.4; db 0.5,0.7,-0.4;\ndb 0,0,-1; db 0.5,0.7,-0.4 db -0.5,0.7,-0.4;\ndb 0,0,-1; db -0.5,0.7,-0.4; db -0.9,-0.3,-0.4;\ndb 0,0,-1; db -0.9,-0.3,-0.4; db 0,-0.9,-0.5;\n","gpuchip/examples/mt3.txt":"//== 3D Graphics begin here ====================================================\n dvxpipe 3;\n dcvxpipe 3;\n\n //Calc depth here\n mov #Background.MinDepth, 0.8; //Near distance\n mov #Background.MaxDepth, 6.0; //Far distance\n mov #Background.ShadeStart,1.0;\n mov #Background.DepthStep ,0.3; //Depth step. The lower, the higher quality is\n\n timer #Time; mul #Time,3;\n\n mov EAX,#Time; mod EAX,#Background.DepthStep;\n\n sub #Background.MinDepth,EAX;\n sub #Background.MaxDepth,EAX;\n\n //Initialize depth range\n mov #Background.deltaDepth,#Background.MaxDepth;\n sub #Background.deltaDepth,#Background.MinDepth;\n\n //Compute background stuff\n mov #Background.ShadeStep,#Background.deltaDepth;\n div #Background.ShadeStep,#Background.DepthStep;\n frnd #Background.ShadeStep;\n finv #Background.ShadeStep;\n mul #Background.ShadeStep,#Background.ShadeStepMul;\n\n //Brightness too\n mov EAX,#Time; mod EAX,#Background.ShadeStep;\n sub #Background.ShadeStart,EAX;\n\n mov #_rect.color.r,200;\n mov #_rect.color.b,200;\n\n// Uncomment this for trippy camera\n// timer EAX; div EAX,8; fsin EBX,EAX; mul EBX,2;\n// drotatescale EAX,EBX; mul EBX,2;\n\n dsetwidth 0.8;\n call Draw.Background;\ndexit;\n\nalloc Time;\n\n//==============================================================================\nDraw.Background:\n //Draw all the rectangles\n mov EAX,#Background.MinDepth; mov ECX,#Background.ShadeStart;\n BackgroundLoop:\n mov EDX,#Time; add EDX,EAX;\n mov EBP,#Time; div EBP,6.28; fcos EBP,EBP;\n\n fsin EDI,EDX; mul EDI,EBP; mul EDI,0.8; sub EDI,1;\n mov #_rect.offset.x,EDI;\n\n fcos ESI,EDX; mul ESI,EBP; mul ESI,0.4; sub ESI,1;\n mov #_rect.offset.y,ESI;\n\n mov EDX,ECX; fpwr EDX,2;\n mov #regZOffset,EAX;\n\n dcolor _rect.color;\n// Uncomment this for trippy HSL color\n// mov ESI,#Time; add ESI,EAX;\n// fsin #HSL.R,ESI; mul #HSL.R,127; add #HSL.R,128; add ESI,1.57;// mul EAX,2;\n// fsin #HSL.G,ESI; mul #HSL.G,127; add #HSL.G,128; add ESI,1.57;// mul EAX,2;\n// fsin #HSL.B,ESI; mul #HSL.B,127; add #HSL.B,128;\n//\n// dcolor HSL;\n dshade EDX;\n dorectwh _rect.offset,_rect.wh;\n\n sub ECX,#Background.ShadeStep;\n add EAX,#Background.DepthStep;\n\n cmp EAX,#Background.MaxDepth;\n jl BackgroundLoop;\nret\n\n//==============================================================================\n//Drawing parameters\nscalar Background.MinDepth;\nscalar Background.MaxDepth;\nscalar Background.deltaDepth;\nscalar Background.DepthStep;\nscalar Background.ShadeStart;\nscalar Background.ShadeStep;\nscalar Background.ShadeStepMul,0.5;\n\ncolor HSL;\n\n//Generic rectangle\nvector2f _rect.offset,-1,-1;\nvector2f _rect.wh,2,2;\n\nvector2f _pad1.offset;\nvector2f _pad2.offset;\nvector2f _pad.wh;\n\n//Color scheme\ncolor _rect.color, 200,200,200;\ncolor _rect.color2,200,200,000;\n\ncolor _pad1.color, 000,200,000;\ncolor _pad2.color, 200,000,000;\n","gpuchip/examples/cube.txt":"//timer EAX;// div EAX,8;\n//fsin EAX,EAX;\n//mul EAX,512;\n//fabs EAX,EAX;\n//neg EAX;\n//add EAX,512;\n\ndcvxpipe 3; //-1..1 (opengl screen)\ndvxpipe 5; //matrix projection\n\n//Initialize transform\nmperspective mProjectionMatrix,vPerspective;\n\n//Render starts\ndclrscr bg_color;\nmlookat mViewMatrix,vLookAt; //View matrix\n\ntimer eax;\nmov #vRotate.w,eax;\n\n//Rotate translate\nmrotate mRotateMatrix,vRotate;\nmtranslate mTranslateMatrix,vTranslate;\n\n//Create model matrix\nmmov mModelMatrix,mTranslateMatrix;\nmmul mModelMatrix,mRotateMatrix;\n\n//modelViewMatrix = ViewMatrix * modelMatrx\nmmov mModelViewMatrix,mViewMatrix;\nmmul mModelViewMatrix,mModelMatrix;\n\n//load matrix\nmload mModelViewMatrix;\nmloadproj mProjectionMatrix;\n\n//setup light\ndsetlight 0,lightdata;\n\n//setup buffer\ndenable 0; //Vertex buffer\ndenable 1; //ZSorting\ndenable 2; //Lighting\ndenable 3; //Face culling\n\n//render cube\ndcolor fg_color;\ndvxdata_3f cube2,12;\ndvxflush;\n\nddisable 0; //Disable everything!\nddisable 1;\nddisable 2;\nddisable 3;\n\ndcvxpipe 0;\ndvxpipe 0;\n\n//You can write some text here now\n//\ndexit;\n\n//========\ncube2:\ndb -1,-1,-1;\ndb 1,-1,-1;\ndb 1,1,-1;\ncube3:\ndb -1,-1,-1;\ndb 1,1,-1;\ndb -1,1,-1;\ncube4:\ndb 1,-1,1;\ndb -1,-1,1;\ndb 1,1,1;\ncube5:\ndb -1,-1,1;\ndb -1,1,1;\ndb 1,1,1;\ncube6:\ndb 1,-1,-1;\ndb -1,-1,-1;\ndb 1,-1,1;\ncube7:\ndb -1,-1,-1;\ndb -1,-1,1;\ndb 1,-1,1;\ncube8:\ndb -1,1,-1;\ndb 1,1,-1;\ndb 1,1,1;\ncube9:\ndb -1,1,1;\ndb -1,1,-1;\ndb 1,1,1;\ncube10:\ndb -1,-1,-1;\ndb -1,1,-1;\ndb -1,1,1;\ncube11:\ndb -1,-1,1;\ndb -1,-1,-1;\ndb -1,1,1;\ncube12:\ndb 1,1,-1;\ndb 1,-1,-1;\ndb 1,1,1;\ncube13:\ndb 1,-1,-1;\ndb 1,-1,1;\ndb 1,1,1;\n\nlightdata:\nvector4f lightpos, 0,50,-50, 0; //x y z \ncolor lightcol,255,255,255, 1; //R G B Brightness\n//========\n\nmatrix mRotateMatrix;\nmatrix mTranslateMatrix;\n\nmatrix mProjectionMatrix;\t//This defines our projection to screen\nmatrix mViewMatrix;\t\t//This defines our camera transformations\n\nmatrix mModelMatrix;\t\t//This is our model transformations\nmatrix mModelViewMatrix;\t//This is our model relatively to camera transform\n\n\nvector4f vRotate, 1, 1, 1, 0; // \nvector4f vTranslate, 0, 0, 0, 0; // <0>\nvector4f vPerspective, 30, 1.6, 1, 20; // \n\nvLookAt:\nvector3f vLookAt_Eye, 0, 0, -5; //Where our camera is\nvector3f vLookAt_Center, 0, 0, 0; //What we look at\nvector3f vLookAt_Up, 0, 1, 0; //Where our matt-hat is\n\ncolor fg_color,255,255,25;\ncolor bg_color,64,32,12;\n","gpuchip/examples/verynice2.txt":"//Generated by WGUI tool. Get it at wiremod.com\n_page_0:\ndsetsize 16\ndcolor _c_0\ndrect _a_1,_a_2\ndcolor _c_1\ndrect _a_4,_a_5\ndcolor _c_2\ndrect _a_7,_a_8\ndcolor _c_3\ndrect _a_10,_a_11\ndcolor _c_3\ndrect _a_13,_a_14\ndcolor _c_2\nmov #_f_17,port0\ndwrite _a_16,_s_17\ndcolor _c_4\ndrect _a_19,_a_20\ndcolor _c_2\nmov #_f_23,port0\ndwrite _a_22,_s_23\ndcolor _c_4\ndwritefmt _a_25,_s_26\ndcolor _c_4\ndwritefmt _a_28,_s_29\ndcolor _c_4\ndwritefmt _a_31,_s_32\ndcolor _c_4\ndwritefmt _a_34,_s_35\ndcolor _c_4\ndwritefmt _a_37,_s_38\ndcolor _c_4\ndwritefmt _a_40,_s_41\ndcolor _c_4\ndwritefmt _a_43,_s_44\ndcolor _c_4\ndwritefmt _a_46,_s_47\ndcolor _c_3\ndrect _a_49,_a_50\ndcolor _c_2\nmov #_f_53,port0\ndwrite _a_52,_s_53\ndcolor _c_3\ndrect _a_55,_a_56\ndcolor _c_2\nmov #_f_59,port0\ndwrite _a_58,_s_59\ndcolor _c_2\ndwritefmt _a_61,_s_62\ndcolor _c_2\ndwritefmt _a_64,_s_65\ndcolor _c_2\ndwritefmt _a_67,_s_68\ndcolor _c_2\ndwritefmt _a_70,_s_71\ndcolor _c_2\ndwritefmt _a_73,_s_74\ndcolor _c_2\ndwritefmt _a_76,_s_77\ndexit\n\ncolor _c_0,0,0,160\nvec2f _a_1,8,8\nvec2f _a_2,504,504\ncolor _c_1,7,51,122\nvec2f _a_4,16,16\nvec2f _a_5,496,496\ncolor _c_2,0,0,0\nvec2f _a_7,24,24\nvec2f _a_8,488,488\ncolor _c_3,192,192,192\nvec2f _a_10,32,32\nvec2f _a_11,480,216\nvec2f _a_13,32,224\nvec2f _a_14,480,392\nvec2f _a_16,40,40\nstring _s_17,'VERYNICE GUI V2.1 Initialized...'\nalloc _f_17,0\ncolor _c_4,128,128,128\nvec2f _a_19,32,64\nvec2f _a_20,480,72\nvec2f _a_22,40,232\nstring _s_23,'Raw data feed:'\nalloc _f_23,0\nvec2f _a_25,88,256\nstring _s_26,'Input port 0: %f'\nvec2f _a_28,88,272\nstring _s_29,'Input port 1: %f'\nvec2f _a_31,88,288\nstring _s_32,'Input port 2: %f'\nvec2f _a_34,88,304\nstring _s_35,'Input port 3: %f'\nvec2f _a_37,88,320\nstring _s_38,'Input port 4: %f'\nvec2f _a_40,88,336\nstring _s_41,'Input port 5: %f'\nvec2f _a_43,88,352\nstring _s_44,'Input port 6: %f'\nvec2f _a_46,88,368\nstring _s_47,'Input port 7: %f'\nvec2f _a_49,32,400\nvec2f _a_50,248,480\nvec2f _a_52,40,408\nstring _s_53,'Vector feed 1:'\nalloc _f_53,0\nvec2f _a_55,264,400\nvec2f _a_56,480,480\nvec2f _a_58,272,408\nstring _s_59,'Vector feed 2:'\nalloc _f_59,0\nvec2f _a_61,40,424\nstring _s_62,'X: %f'\nvec2f _a_64,40,440\nstring _s_65,'Y: %f'\nvec2f _a_67,40,456\nstring _s_68,'Z: %f'\nvec2f _a_70,272,424\nstring _s_71,'X: %f'\nvec2f _a_73,272,440\nstring _s_74,'Y: %f'\nvec2f _a_76,272,456\nstring _s_77,'Z: %f'\n","gpuchip/examples/verynice1.txt":"dcolor c1;\ndrect p1,p2;\ndcolor c2;\ndrect p3,p4;\ndcolor c3;\ndrect p5,p6;\n\nmov #textpos1.y,80;\n\ndcolor c4;\ndsetsize 12;\ndwrite textpos1,text1;\n\nmov ecx,0;\nport_loop:\n add #textpos1.y,18;\n mov #textpos2.y,#textpos1.y;\n\n mov #textpos2.x,#textpos1.x;\n add #textpos2.x,90;\n dwrite textpos1,text2;\n dwritei textpos2,ecx;\n\n in eax,ecx;\n\n mov #textpos2.x,#textpos1.x;\n add #textpos2.x,192;\n dwritef textpos2,eax;\n\n inc ecx;\n cmp ecx,18;\n jl port_loop;\n\ndexit;\n\nstring text1,'VERYNICE HUD SYSTEM INITIALIZED... VER 1.0';\nstring text2,'INPUT PORT VALUE';\n\nvec2f textpos1,80,80;\nvec2f textpos2,80,80;\n\ncolor c1,0,0,255;\ncolor c2,0,0,127;\ncolor c3,0,0,64;\ncolor c4,255,255,255;\n\nvec2f p1,50,50;\nvec2f p2,450,450;\n\nvec2f p3,60,60;\nvec2f p4,430,430;\n\nvec2f p5,70,70;\nvec2f p6,440,440;\n","gpuchip/examples/trig.txt":"// Author: Jasongamer\n// Description: A tool for helping people learn trig\n\nMain();\n\n#include \n\nvoid Main()\n{\n glClear( 0, 0, 0 ); // Clear screen\n glCoordPipe( GL_CPIPE_N1_1 ); // Set coordinate pipe (-1 to 1 mode)\n\n timer R0; // Set time to curtime()\n R0 = -R0 * 1;\n\n glColor( 255, 255, 255 ); // Set draw color\n glCircle( *orig.x, *orig.y, 0.66, 40 ); // Draw circle (x, y, radius, quality)\n\n glColor( 0, 0, 0 );\n glCircle( *orig.x, *orig.y, 0.64, 40 );\n\n // Set the points for the trig\n fcos *cos,R0;\n fsin *sin,R0;\n\n *PosR.x = *cos;\n *PosR.x *= 0.65;\n *PosR.x += *orig.x;\n\n *PosR.y = *sin;\n *PosR.y *= 0.65;\n *PosR.y += *orig.y;\n\n *PosX.x = *PosR.x;\n\n glLineWidth( 0.01 ); // Set line width\n glFontSize( 24 ); // Set font size\n\n // X part of triangle\n glColor( 0, 0, 255 );\n glLine( *orig.x, *orig.y, *PosX.x, *PosX.y ); // Draw line\n glWriteFmt( -0.95, -0.95, sCos ) // Write formatted string\n\n // Y part of triangle\n glColor( 255, 0, 0 );\n glLine( *PosR.x, *PosR.y, *PosX.x, *PosX.y );\n *sin *= -1; // Negate\n glWriteFmt( -0.95, -0.85, sSin );\n\n glColor( 255, 255, 255 );\n glLine( *orig.x, *orig.y, *PosR.x, *PosR.y ); // Draw line\n\n glExit(); // Exit\n}\n\nvec2f orig,0,0;\nvec2f PosR,0,0;\nvec2f PosX,0,0;\n\nstring sCos,\"Cosine = %f\";\nalloc cos;\n\nstring sSin,\"Sine = %f\";\nalloc sin;\n","gpuchip/examples/texture.txt":"// Author: Drunkie\n// Description: A very simple texture example\n\nMain();\n\n#include \n\nvoid Main()\n{\n glVertexMode( 1 );\n glColor( 255, 255, 255, 255 );\n\n glBindTexture( 'brick/brick_model' );\n glColor( 255, 255, 255, 255 );\n glRectWH( 128, 128, 256, 256 );\n\n glExit();\n}\n\n// ZASM version\n\n//mov #regVertexMode,1;\n//dcolor white;\n//dxtexture tex;\n//drectwh pos,size;\n//dexit;\n//color white,255,255,255;\n//string tex,'brick/brick_model';\n//vec2f pos,128,128;\n//vec2f size,256,256;\n","gpuchip/examples/terrain.txt":"// Matrix code is based on cube example\nDCPIPE 3 // -1 to 1 coordinate range, required by DDTERRAIN\nDVXPIPE 5 // XYZ projection + matrix\n\nDENABLE 0 // Vertex buffer\nDENABLE 1 // Z sorting\nDENABLE 2 // Lighting\nDENABLE 3 // Backface culling\n\nDSETLIGHT 0, Light\n\nMPERSPECTIVE ProjectionMatrix, Perspective\n\n// Rotate the terrain\nTIMER EAX\nDIV EAX, 4\nMOV #RotateVector.w, EAX\nMROTATE RotateMatrix, RotateVector\n\n// Point camera at terrain\nMLOOKAT ViewMatrix, LookAtArgs\nMMUL ViewMatrix, RotateMatrix\n\nMLOAD ViewMatrix\nMLOADPROJ ProjectionMatrix\n\nDCLRSCR Background\n\nDCOLOR Foreground\n//DXTEXTURE Texture // Doesn't work\nDDTERRAIN Terrain\n\nDVXFLUSH\n\nDEXIT\n\nCOLOR Foreground, 253, 186, 49, 255\nCOLOR Background, 1, 46, 87, 255\nCOLOR White, 255, 255, 255, 255\n\n//STRING Texture, \"brick/brick_model\";\n\n// The terrain struct\nTerrain:\n DB 8, 8 // Terrain size\n DB 16 // Draw distance, between 0 and 16\n DB 0, 0 // Terrain offset\n\n // 11 bytes unused\n DB 0,0,0,0,0,0\n DB 0,0,0,0,0\n\n // 8 x 8 heightmap\n DB 0.0, 0.3, 0.3, 0.3, 0.3, 0.0, 0.0, 0.0\n DB 0.3, 0.3, 0.3, 0.5, 0.0, -0.5, 0.0, 0.0\n DB 0.0, 1.5, 1.5, 1.0, 0.3, -0.5, -0.3, 0.0\n DB 0.0, 1.0, 2.3, 1.8, 0.8, 0.3, 0.0, 0.0\n DB 0.3, 0.8, 1.3, 2.3, 1.6, 0.8, 0.5, 0.0\n DB 0.3, 0.5, 1.0, 1.3, 0.5, 0.3, 0.3, 0.0\n DB 0.0, 0.3, 0.3, 0.3, 0.0, -0.3, -0.5, 0.0\n DB 0.0, 0.0, 0.0, 0.0, -0.8, -0.8, -1.0, -0.5\n\nLight:\n DB 0, 50, -50, 0 // Position\n DB 255, 249, 225, 0.9 // RGB + Intensity\n\nMATRIX ProjectionMatrix\nMATRIX RotateMatrix\nMATRIX ViewMatrix\n\nVEC4F RotateVector, 0, 0, 1, 0 // Rotate around Z axis\nVEC4F Perspective, 50, 1, 1, 20 // 2nd value is aspect ratio\n\nLookAtArgs:\n DB 0, 5, 4 // Camera\n DB 0, 0, 0 // Look at\n DB 0, 0, -1 // Up (terrain is upside-down for some reason)\n","gpuchip/examples/foxlogo.txt":"//Fox game console logo (also example on how to work with polygons)\n\ndclrscr chassis;\n\ndcolor fox1c;\ndvxdata_2f fox1a,16; //16 max!!\ndvxdata_2f fox2a,3;\ndvxdata_2f fox3a,3;\ndvxdata_2f fox4a,3;\ndvxdata_2f fox5a,3;\ndvxdata_2f fox6a,3;\ndvxdata_2f fox7a,6;\ndvxdata_2f fox8a,3;\n\ndcolor fox2c;\ndvxdata_2f fox2,4;\n\ndexit;\n\n//===========================================\ncolor chassis,0,0,0;\n\ncolor fox1c,60,60,60;\ncolor fox2c,100,100,100;\n//===========================================\nfox1a: //N=16\ndb 60,218\ndb 62,173\ndb 32,36\ndb 214,119\ndb 268,128\ndb 318,168\ndb 352,233\ndb 494,243\ndb 499,254\ndb 496,266\ndb 478,321\ndb 335,374\ndb 265,408\ndb 223,419\ndb 95,430\ndb 109,408\n\nfox2a: //N = 3\ndb 109,408\ndb 57,432\ndb 69,376\nfox3a:\ndb 69,376\ndb 33,394\ndb 59,327\nfox4a:\ndb 59,327\ndb 24,348\ndb 54,273\nfox5a:\ndb 54,273\ndb 29,286\ndb 57,240\nfox6a:\ndb 57,240\ndb 26,245\ndb 60,218\n\nfox7a: //N=6\ndb 109,408\ndb 69,376\ndb 59,327\ndb 54,273\ndb 57,240\ndb 60,218\n\nfox8a: //N=3\ndb 177,150;\ndb 269,150;\ndb 190,47;\n\n//===========================================\nfox2: //N=4\ndb 340,238\ndb 286,257\ndb 274,203\ndb 311,213\n//===========================================\n","gpuchip/examples/3d_letter_a.txt":"// Author: Drunkie\n// Description: Draws a 3D model of the letter A\n\nMain();\n\n#include \n\nvoid Main()\n{\n glSleep( 40 ); // Sleep for 40 milliseconds (reduces fps lag)\n glClear( 100, 149, 237 ); // Clear screen\n\n glCoordPipe( GL_CPIPE_N1_1 ); // Set coordinate pipe to [-1 to 1] mode\n glVertexPipe( GL_VPIPE_XYZTRANSFORM ); // Set vertex pipe to xyz transformation\n\n glLightPos( 0, 0, -50 ); // Set the light position\n glLightColor( 255, 255, 255, 1 ); // Set the light color\n\n glLookAt(\n 0, 0, -2.8, // Camera pos\n 0, 0, 0, // Camera target\n 0, 1, 0 // Camera up\n );\n\n // Create variable to hold curtime\n float time;\n timer time;\n\n // Create perspective and matrix transformations\n glPerspective( 30, 1, 1, 20 ); // FOV, ASPECT RATIO, ZNEAR, ZFAR\n glRotate( 0, 1, 0, time ); // AXIS X, Y, Z, ANGLE W\n glTranslate( 0, -0.1, 0, 0 ); // TRANSLATION X, Y, Z\n glScale( 1, 1, 1, 0 ); // SCALE X, Y, Z\n\n glEnable( GL_VERTEX_BUFFER ); // Enable vertex buffer\n glEnable( GL_VERTEX_ZSORT ); // Enable Z sorting\n glEnable( GL_VERTEX_LIGHTING ); // Enable vertex lighting\n glEnable( GL_VERTEX_CULLING ); // Enable face culling\n\n // Solid 3D polygon\n glFillMode( GL_FILL_SOLID ); // Set fillmode as solid\n glColor4( 255, 255, 255, 255 ); // Set draw color with alpha\n glPoly3D( vertexBuffer, 30 ); // Draw 3D polygon\n glFlush(); // Flush the vertex buffer to the screen\n\n glExit(); // Exit\n}\n\n// The vertex data for our model\nvertexBuffer:\ndb 1,1,0; db 0.75,1,0; db 0.25,-1,0;\ndb 0.75,1,0; db 0,-1,0; db 0.25,-1,0;\ndb -1,1,0; db -0.25,-1,0; db -0.75,1,0;\ndb -0.75,1,0; db -0.25,-1,0; db 0,-1,0;\ndb 1,1,0.25; db 0.25,-1,0.25; db 0.75,1,0.25;\ndb 0.75,1,0.25; db 0.25,-1,0.25; db 0,-1,0.25;\ndb -1,1,0.25; db -0.75,1,0.25; db -0.25,-1,0.25;\ndb -0.75,1,0.25; db 0,-1,0.25; db -0.25,-1,0.25;\ndb 0.25,-1,0; db -0.25,-1,0; db 0.25,-1,0.25;\ndb -0.25,-1,0; db -0.25,-1,0.25; db 0.25,-1,0.25;\ndb -1,1,0; db -1,1,0.25; db -0.25,-1,0;\ndb -1,1,0.25; db -0.25,-1,0.25; db -0.25,-1,0;\ndb 1,1,0.25; db 1,1,0; db 0.25,-1,0;\ndb 1,1,0.25; db 0.25,-1,0; db 0.25,-1,0.25;\ndb -0.75,1,0; db 0,-1,0; db -0.75,1,0.25;\ndb -0.75,1,0.25; db 0,-1,0; db 0,-1,0.25;\ndb 0.75,1,0; db 0.75,1,0.25; db 0,-1,0;\ndb 0.75,1,0.25; db 0,-1,0.25; db 0,-1,0;\ndb -0.47,0.25,0; db -0.38,0.01,0; db 0.38,0.01,0;\ndb 0.38,0.01,0; db 0.47,0.25,0; db -0.47,0.25,0;\ndb -0.47,0.25,0.25; db 0.38,0.01,0.25; db -0.38,0.01,0.25;\ndb 0.38,0.01,0.25; db -0.47,0.25,0.25; db 0.47,0.25,0.25;\ndb -0.38,0.01,0; db -0.38,0.01,0.25; db 0.38,0.01,0;\ndb 0.38,0.01,0; db -0.38,0.01,0.25; db 0.38,0.01,0.25;\ndb -0.47,0.25,0; db 0.47,0.25,0; db -0.47,0.25,0.25;\ndb -0.47,0.25,0.25; db 0.47,0.25,0; db 0.47,0.25,0.25;\ndb -1,1,0; db -0.75,1,0; db -1,1,0.25;\ndb -0.75,1,0; db -0.75,1,0.25; db -1,1,0.25;\ndb 1,1,0; db 1,1,0.25; db 0.75,1,0;\ndb 0.75,1,0; db 1,1,0.25; db 0.75,1,0.25;\n","gpuchip/examples/3d_tunnel.txt":"// Author: Drunkie\n// Description: Draws a never ending tunnel in 3D!\n\nMain();\n\n#include \n\nvoid Main()\n{\n glSleep( 60 ); // Sleep for 60 milliseconds (reduces fps lag)\n glClear( 0, 0, 0 ); // Clear screen\n\n glCoordPipe( GL_CPIPE_N1_1 ); // Set coordinate pipe to [-1 to 1] mode\n glVertexPipe( GL_VPIPE_XYZTRANSFORM ); // Set vertex pipe to xyz transformation\n\n glLightPos( -1, -1, -1 ); // Set the light position\n glLightColor( 255, 255, 255, 1.25 ); // Set the light color\n\n glLookAt(\n 0, 0, -25, // Camera pos\n 0, 0, 0, // Camera target\n 0, 1, 0 // Camera up\n );\n\n // Loop and draw 4 models\n for (i = 0; i < 4; i++)\n {\n // Set translations for each model\n timer zTranslate;\n zTranslate *= -16;\n mod zTranslate,16;\n zTranslate += (i * 16);\n\n // Create perspective and matrix transformations\n glPerspective( 8, 1, 0.4, 20 ); // FOV, ASPECT RATIO, ZNEAR, ZFAR\n glRotate( 0, 0, 0, 0 ); // AXIS X, Y, Z, ANGLE W\n glTranslate( 0, 0, zTranslate ); // TRANSLATION X, Y, Z\n glScale( 1.2, 1, 8 ); // SCALE X, Y, Z\n\n glEnable( GL_VERTEX_BUFFER ); // Enable vertex buffer\n glEnable( GL_VERTEX_ZSORT ); // Enable Z sorting\n glEnable( GL_VERTEX_LIGHTING ); // Enable vertex lighting\n glEnable( GL_VERTEX_CULLING ); // Enable face culling\n\n // Solid 3D polygon\n glFillMode( GL_FILL_SOLID ); // Set fillmode as solid\n glColor4( 255, 255, 255, 150 ); // Set the draw color with alpha\n glPoly3D( VertexBuffer, 12 ); // Draw 3D polygon\n glFlush(); // Send our vertex buffer to screen\n\n // Wireframe 3D polygon\n glLineWidth( 1 ); // Set line width of wireframe\n glFillMode( GL_FILL_WIREFRAME ); // Set fillmode to wireframe\n glColor4( 255, 255, 255, 255 ); // Set the draw color with alpha\n glPoly3D( vertexBuffer, 8 ); // Draw 3D polygon\n glFlush(); // Send our vertex buffer to screen\n }\n\n glExit(); // Exit\n}\n\nfloat i;\nfloat zTranslate;\n\nvertexBuffer:\ndb 1,1,-1; db -1,1,-1; db 1,1,1;\ndb -1,1,-1; db -1,1,1; db 1,1,1;\ndb 1,1,-1; db 1,1,1; db 1,-1,-1;\ndb 1,-1,-1; db 1,1,1; db 1,-1,1;\ndb -1,1,-1; db -1,-1,-1; db -1,1,1;\ndb -1,-1,-1; db -1,-1,1; db -1,1,1;\ndb 1,-1,-1; db 1,-1,1; db -1,-1,-1;\ndb -1,-1,-1; db 1,-1,1; db -1,-1,1;\n","gpuchip/examples/hud_engine.txt":"//mov #65522,1;\n//mov #65525,0.66;\n//port0 & port1 - engine left/right throttle (0..1)\n//port2 & port3 - delta (not used)\n\n//This displays engine window in PhoenixWings airplane\n\nmov #65485,16; //set circle quality\n\ndclrscr hud_border;\n\ndcolor hud_text;\ndcircle hud_engine1gauge,68;\ndcircle hud_engine2gauge,68;\ndcolor hud_border;\ndcircle hud_engine1gauge,64;\ndcircle hud_engine2gauge,64;\n\ndcolor hud_text;\ndsetwidth 1;\ndline hud_engine1gauge_start,hud_engine1gauge;\ndline hud_engine2gauge_start,hud_engine2gauge;\n\ndsetwidth 2;\n\n//===\nmov eax,port0; mul eax,100;\nmul eax,0.1;\nmul #left_power,1.9;\nadd #left_power,eax;\ndiv #left_power,2;\n\nmov eax,#left_power; div eax,100;\nmul eax,6.00;\nadd eax,1.57;\n\ndrotatescale eax,1;\ndmove hud_engine1gauge;\n\ndline gauge_base,gauge_needle;\n//==\nmov #right_power,#left_power; //comment this and..\n//uncomment if your left/right engines are not synchronized\n//mov eax,port1; mul eax,100;\n//mul eax,0.1;\n//mul #right_power,1.9;\n//add #right_power,eax;\n//div #right_power,2;\n\n//mov eax,#right_power; div eax,100;\n//mul eax,6.00;\n//add eax,1.57;\n\ndrotatescale eax,1;\ndmove hud_engine2gauge;\n\ndline gauge_base,gauge_needle;\n//==\n\n//use this for whatever you wanna\n//mov #left_delta,port2; sub #left_delta,7.6; mul #left_delta,10;\n//mov #right_delta,port3; sub #right_delta,7.6; mul #right_delta,10;\n\ndrotatescale 0,1; //reset!\ndmove 0;\n\ndsetfont 4;\ndsetsize 28;\ndwritefmt hud_text1pos,hud_text1;\ndwritefmt hud_text2pos,hud_text2;\n\ncmp port4,1;\ndcolor hud_yellow;\nje _nsh;\n dshade 0.25;\n_nsh:\ndwrite hud_text3pos,hud_text3;\ndexit;\n\nvector2f hud_text1pos,70,212;\nvector2f hud_text2pos,310,212;\nvector2f hud_text3pos,20,460;\n\nstring hud_text1,'Left Engine',10,10,'N1 = %i%%',10,'Delta = %i%%';\nalloc left_power; alloc left_delta;\nstring hud_text2,'Right Engine',10,10,'N1 = %i%%',10,'Delta = %i%%';\nalloc right_power; alloc right_delta;\nstring hud_text3,'';\n\nvector2f hud_engine1gauge,128,128;\nvector2f hud_engine1gauge_start,128,64;\n\nvector2f hud_engine2gauge,384,128;\nvector2f hud_engine2gauge_start,384,64;\n\nvector2f gauge_base,0,0;\nvector2f gauge_needle,0,-48;\n\ncolor hud_text,64,255,64;\ncolor hud_yellow,255,255,64;\ncolor hud_border,30,30,30;\n","gpuchip/examples/table.txt":"// Author: Drunkie\n// Description: Draws a table; useful for calendars or spreadsheets!\n\nMain();\n\n#include \n\nfloat rows = 6;\nfloat cols = 5;\nfloat sizex = 476;\nfloat sizey = 400;\nfloat linewidth = 3;\n\nfloat i, j, day;\n\nvoid Main()\n{\n dentrypoint 0,DrawThread;\n dentrypoint 4,AsyncThread;\n\n *regHWClear = 0\n *regAsyncFreq = 200000;\n *regAsyncClk = 1;\n}\n\nvoid DrawThread()\n{\n dexit;\n}\n\nvoid AsyncThread()\n{\n glBegin();\n\n glClear( 35, 35, 35 ); // Clear screen color\n\n glColor( 255, 255, 255 ); // Set draw color\n glFont( GL_FONT_ARIAL ); // Set font type\n glFontSize( 36 ); // Set font size\n glWriteString( 16, 6, 'Simple-Calendar 1.0');\n\n glColor( 120, 120, 120 );\n glOffset( 16, 64 ); // Set screen offset\n glRectWH( 0, 0, sizex + linewidth, sizey + linewidth); // Draw rectangle\n\n glFont( GL_FONT_TREBUCHET );\n glFontSize( 14 );\n\n // Calculate rectangle size\n float sx = (sizex / rows) - linewidth;\n float sy = (sizey / cols) - linewidth;\n\n // Loop through rows\n for (i = 0; i < rows; i++)\n {\n // Loop through columns\n for (j = 0; j < cols; j++)\n {\n // Calculate x,y coordinate to draw at\n float x = i * (sizex / rows);\n float y = j * (sizey / cols);\n\n glColor( 200, 200, 200 ); // Set draw color\n glRectWH( x + linewidth, y + linewidth, sx, sy ); // Draw rectangle\n\n glColor( 0, 0, 0 ); // Set draw color\n\n // Write integer to screen\n day = i + (j * rows)\n glWriteInt( x + linewidth + 2, y + linewidth + 2, day + 1 );\n }\n }\n\n glEnd();\n}\n","gpuchip/examples/3d_cube.txt":"// Author: Drunkie\n// Description: 3D Cube\n\nMain();\n\n#include \n\nvoid Main()\n{\n glClear( 0, 0, 0 ); // Clear screen\n\n glCoordPipe( GL_CPIPE_N1_1 ); // Set coordinate pipe to [-1 to 1] mode\n glVertexPipe( GL_VPIPE_XYZTRANSFORM ); // Set vertex pipe to xyz transformation\n\n glLightPos( 0, 0, -20 ); // Set the light position\n glLightColor( 255, 255, 255, 1 ); // Set the light color\n\n glLookAt(\n 0, 0, -5, // Camera pos\n 0, 0, 0, // Camera target\n 0, 1, 0 // Camera up\n );\n\n // Create variable to hold curtime\n float time;\n timer time;\n\n // Create perspective and matrix transformations\n glPerspective( 30, 1, 1, 20 ); // FOV, ASPECT RATIO, ZNEAR, ZFAR\n glRotate( 1, 1, 0, time ); // AXIS X, Y, Z, ANGLE W\n glTranslate( 0, 0, 0 ); // TRANSLATION X, Y, Z\n glScale( 1, 1, 1 ); // SCALE X, Y, Z\n\n glEnable( GL_VERTEX_BUFFER ); // Enable vertex buffer\n glEnable( GL_VERTEX_ZSORT ); // Enable Z sorting\n\n // Solid 3D polygon\n glFillMode( GL_FILL_SOLID ); // Set fillmode as solid\n glColor4( 100, 149, 237, 180 ); // Set draw color with alpha\n glPoly3D( vertexBuffer, 12 ); // Draw 3D polygon\n glFlush(); // Flush the vertex buffer to the screen\n\n // Wireframe 3D polygon\n glLineWidth( 1 ); // Set line width\n glFillMode( GL_FILL_WIREFRAME ); // Set fillmode as solid\n glColor4( 255, 255, 255, 255 ); // Set draw color with alpha\n glPoly3D( vertexBuffer, 12 ); // Draw 3D polygon\n glFlush(); // Flush the vertex buffer to the screen\n\n glExit(); // Exit\n}\n\n// The vertex data for our model\nvertexBuffer:\ndb -1,-1,-1; db 1,-1,-1; db 1,1,-1;\ndb -1,-1,-1; db 1,1,-1; db -1,1,-1;\ndb 1,-1,1; db -1,-1,1; db 1,1,1;\ndb -1,-1,1; db -1,1,1; db 1,1,1;\ndb 1,-1,-1; db -1,-1,-1; db 1,-1,1;\ndb -1,-1,-1; db -1,-1,1; db 1,-1,1;\ndb -1,1,-1; db 1,1,-1; db 1,1,1;\ndb -1,1,1; db -1,1,-1; db 1,1,1;\ndb -1,-1,-1; db -1,1,-1; db -1,1,1;\ndb -1,-1,1; db -1,-1,-1; db -1,1,1;\ndb 1,1,-1; db 1,-1,-1; db 1,1,1;\ndb 1,-1,-1; db 1,-1,1; db 1,1,1;\n","gpuchip/examples/hud_fighter.txt":"//Aircraft hud\n//port0 - ROLL\n//port1 - PITCH\n//port2 - YAW (heading)\n//port3 - speed (units/sec)\n//port4 - altitude (units)\n//port5 - radar altitude (put ranger under your plane, and attach to this)\n//port6 - flaps active, 1 or 0\n//port7 - go to \"Gates - Time\", and find \"Derivative\". Attach this to derivative, and derivative to altitude (vertical speed)\n\n//Artiftical horizon\nin eax,0; //Roll\nin ebx,1; //Pitch\n\n//mul ebx,0.017453292;\nmul eax,0.017453292;\nadd eax,1.57;\n\ndiv ebx,90;\nmul ebx,512;\nadd ebx,256;\n\nmov #horizon_moveoffset.y,ebx;\n\ndrotatescale eax,1;\ndmove horizon_moveoffset;\n\ndcolor art_sky;\ndrectwh horizon_sky_offset,horizon_size;\ndcolor art_grnd;\ndrectwh horizon_grnd_offset,horizon_size;\n\ndcolor hud_text;\ndsetsize 20;\nmov eax,-45;\n_horizon_text:\n mov ebx,eax;\n mul ebx,5.68;\n sub ebx,10;\n mov #horizon_textpos1.y,ebx;\n mov #horizon_textpos2.y,ebx; add ebx,9;\n mov #horizon_rectpos1.y,ebx; add ebx,2;\n mov #horizon_rectpos2.y,ebx;\n\n drect horizon_rectpos1,horizon_rectpos2;\n dwritei horizon_textpos1,eax;\n dwritei horizon_textpos2,eax;\n\n add eax,15;\n cmp eax,45;\n jle _horizon_text;\n\n//Reset\ndmove 0;\ndrotatescale 0,1;\n\n//Border around art horizon\ndcolor border_color;\ndrect border_p1,border_p2;\ndrect border_p3,border_p4;\ndrect border_p5,border_p6;\ndrect border_p7,border_p8;\ndcolor border_color2;\ndrect border_p9,border_p10;\n\n//Draw hud stuff\nmov #roll,port0;\nmov #pitch,port1;\nmov #hdg,port2; add #hdg,180;\nmov #spd,port3; div #spd,17.6;\nmov #alt,port4;\nadd #alt,12000;\ndiv #alt,12;\nmov #ralt,port5; div #ralt,12;\nmov #vspd,port7; div #vspd,17.6;\ndcolor hud_text;\ndwritefmt hud_pos1,hud_text1;\ndsetsize 16;\ndwritefmt hud_pos2,hud_text2;\n\ndcolor hud_text;\nmov eax,port6; mul eax,0.75; add eax,0.25;\ndshade eax;\ndwritefmt hud_pos3,hud_text3;\n\n\ndexit;\n\nvec2f hud_pos1,50,20;\nstring hud_text1,'ROLL %i %tPITCH %i%tHDG %i';\nalloc roll;\nalloc pitch;\nalloc hdg;\n\nvec2f hud_pos2,45,120;\nstring hud_text2,'SPD',10,'%ikt',10,10,'ALT',10,'%ift',10,10,'RALT',10,'%ift',10,10,'VSPD',10,'%ift/s';\nalloc spd;\nalloc alt;\nalloc ralt;\nalloc vspd;\n\nvec2f hud_pos3,45,400;\nstring hud_text3,'FLAPS';\n\n\nvec2f horizon_textpos1,96,0;\nvec2f horizon_textpos2,-64,0;\nvec2f horizon_rectpos1,-50,0;\nvec2f horizon_rectpos2,50,0;\ncolor hud_text,64,255,64;\n\ncolor border_color2,255,255,255;\ncolor border_color,30,30,30;\nvec2f border_p1,0,0;\nvec2f border_p2,128,512;\nvec2f border_p3,384,0;\nvec2f border_p4,512,512;\n\nvec2f border_p5,128,0;\nvec2f border_p6,384,64;\nvec2f border_p7,128,448;\nvec2f border_p8,384,512;\n\nvec2f border_p9,128,254;\nvec2f border_p10,384,258;\n\nvec2f horizon_sky_offset,-256,-512;\nvec2f horizon_grnd_offset,-256,0;\nvec2f horizon_size,512,512;\n\nvec2f horizon_moveoffset,256,256;\n\ncolor art_sky,24,144,255;\ncolor art_grnd,192,72,0;\n","gpuchip/examples/line_graph.txt":"// Author: Drunkie\n// Description: A fake lag-o-meter that plots points on a grid\n\nMain();\n\n#include \n\nfloat i;\nfloat x, y;\nfloat ox = 0, oy = 256;\nfloat lines = 10;\nfloat lineWidth = 1\nfloat frameWidth = 510;\nfloat frameHeight = 300;\n\nvoid Main()\n{\n glVertexMode( 1 ); // Enable vertex mode\n glColor( 255, 255, 255 ); // Set draw color\n\n // Set texture as background\n glBindTexture( \"phoenix_storms/lag_sign\" );\n glClearTexture();\n glBindTexture( 0 ); // Discard texture\n\n glFont( GL_FONT_AKBAR ); // Set font type\n glFontSize( 36 ); // Set font size\n glWriteString( 2, 2, \"LAG-O-METER\" ); // Write string to screen\n\n glOffset( lineWidth, 90 ); // Offset the screen coordinates\n\n glColor4( 0, 0, 0, 160 ); // Set draw color with alpha\n glRectWH( 0, 0, frameWidth, frameHeight ); // Draw rectangle\n\n glColor( 255, 255, 255 );\n glLineWidth( lineWidth ); // Set line width\n glORectWH( 0, 0, frameWidth, frameHeight ); // Draw outlined rectangle\n\n glLineWidth( 1 ); // Set line width to 1\n\n // Loop and make a bunch of connected lines\n for (i = 0; i < lines; i++)\n {\n if (i == 0) {\n ox = 0;\n rand oy;\n oy *= frameHeight;\n }\n else {\n ox = x; oy = y;\n }\n x = ((i+1) / lines) * frameWidth;\n rand y;\n y *= frameHeight;\n glLine( ox, oy, x, y ); // Draw line on graph\n }\n\n glOffset( 0, 0 ); // Set screen offset back to 0,0\n glWriteString( 2, 400, \"INTENSE LAG DETECTED\" );\n\n glExit(); // Exit\n}\n","gpuchip/examples/mt2.txt":"dcvxpipe 3;\nmov #regHWClear,0; //Stop hardware clearing\ndsetwidth 0.05;\n\ntimer EAX;\nmov EDX,EAX; sub EDX,#PrevTime; //EDX = Delta time\nmov #PrevTime,EAX;\n\nmov EBP,0.4; //Speed of rotation\n\nmov ECX,8;\nDrawLoop:\n mov EAX,#Angle; mul EAX,1;\n fsin #EndPoint.X,EAX; mul EAX,2;\n fcos #EndPoint.Y,EAX;\n\n //HSL coloring\n fsin #HSL.R,EAX; mul #HSL.R,127; add #HSL.R,128; add EAX,1.57;// mul EAX,2;\n fsin #HSL.G,EAX; mul #HSL.G,127; add #HSL.G,128; add EAX,1.57;// mul EAX,2;\n fsin #HSL.B,EAX; mul #HSL.B,127; add #HSL.B,128;\n\n dcolor HSL;\n\n //Looks very nice\n dline StartPoint1,EndPoint;\n dline StartPoint2,EndPoint;\n dline StartPoint3,EndPoint;\n dline StartPoint4,EndPoint;\n\n mul EDX,EBP;\n add #Angle,EDX;\nloop DrawLoop;\n\ndexit;\n\nalloc Angle;\nalloc PrevTime;\n\ncolor HSL;\n\nvector2f EndPoint,0,0;\nvector2f StartPoint0,0,0;\nvector2f StartPoint1,1,1;\nvector2f StartPoint2,1,-1;\nvector2f StartPoint3,-1,-1;\nvector2f StartPoint4,-1,1;\n","gpuchip/examples/bounce.txt":"//////////////////////////////////\n// BOUNCING BALL GPU EXAMPLE //\n//////////////////////////////////\ndentrypoint 0,_draw;\t\t// Set draw start entrypoint to \"_draw\"\n\t\t\t\t//\nrand #ball.x;\t\t\t// Set random ball start point\nrand #ball.y;\t\t\t//\n\t\t\t\t//\ndexit;\t\t\t\t// Exit the initialization routine...\n//////////////////////////////////\n_draw:\t\t\t\t// Entrypoint for the drawing function\n\t\t\t\t//\ndcvxpipe 2;\t\t\t// Set coordinate pipe to 2 (to use coordinates 0...1)\ndclrscr bg_color;\t\t// Clear screen with background color\n\t\t\t\t//\ndmuldt eax,#d.x;\t\t// EAX = Direction Vector * Delta (change of coords per frame)\nadd #ball.x,eax;\t\t// Move the ball\ndmuldt eax,#d.y;\t\t//\nadd #ball.y,eax;\t\t//\n\t\t\t\t//\ncmp #ball.x,0.9;\t\t// Check hits against walls\ncge bounce.x;\t\t\t// Call bounce routine...\ncmp #ball.x,0.0;\t\t//\ncle bounce.x;\t\t\t//\n\t\t\t\t//\ncmp #ball.y,0.9;\t\t// Bounce on other axis\ncge bounce.y;\t\t\t//\ncmp #ball.y,0.0;\t\t//\ncle bounce.y;\t\t\t//\n\t\t\t\t//\ndcolor ball_color;\t\t// Set color to color of ball\ndrectwh ball,ball_wh;\t\t// Draw the ball\n\t\t\t\t//\ndsetsize 24;\t\t\t// Set font size\ndwrite textpos,text;\n\t\t\t\t//\ndexit;\t\t\t\t// Exit the draw function\n//////////////////////////////////\nbounce.x:\t\t\t// Bounce function (change X speed)\n neg #d.x; \t\t\t//\n min #ball.x,0.9;\t\t//\n max #ball.x,0.0;\t\t//\nret\t\t\t\t//\n\t\t\t\t//\nbounce.y:\t\t\t// Bounce function (change Y speed)\n neg #d.y;\t\t\t//\n min #ball.y,0.9;\t\t//\n max #ball.y,0.0;\t\t//\nret\t\t\t\t//\n//////////////////////////////////\n// Data and resources\t\t//\n//////////////////////////////////\n\t\t\t\t//\ncolor ball_color,255,255,255;\t// Ball color (white)\ncolor bg_color, 64, 32,128;\t// Background color (neon violet)\n\t\t\t\t//\nvector2f ball;\t\t\t// Ball position\nvector2f ball_wh,0.1,0.1;\t// Ball width/height\n\t\t\t\t//\nvector2f textpos,0.1,0.1;\t// Text position\n\t\t\t\t//\nvector2f d,1.0,1.0;\t\t// Movement direction & speed\n\t\t\t\t//\nstring text,'Bouncing ball!';\t// \"Bouncing ball!\"\n//////////////////////////////////\n","gpuchip/examples/plasma.txt":"//Plasma fractals\n//Converted by dlb from http://bocoup.com/processing-js/docs/index.php?page=Plasma%20Fractals\n//Which was converted by F1LT3R @ Hyper-Metrix.com from original at http://www.ic.sunysb.edu/Stu/jseyster/plasma/\n\nmov #regHWClear,0; //Stop GPU clearing itself\n\ndentrypoint 0,_draw; //Set the entry point for the draw loop\ndentrypoint 4,_async; //Set the enty point for the async loop\n\nmov #regAsyncFreq,2000000; //Make async run as fast as it can\nmov #regAsyncClk,1; //Start async\n\ndexit; //End init\n_draw: //Start draw\ndexit; //End draw\n\n//Setup variables\ncolor col;\nvec2f pos;\nvec2f size;\nfloat gridSize, edge1, edge2, edge3, edge4, midPoint, newWidth, newHeight, width, height, noise;\n\n_async: //Enter async\n\nmain(); //Run main function\n\nwhile(1){idle} //Infinatly loop\n\nvoid main(){ //Main function\n dsetbuf_spr; //Use sprite buffer\n\n setColor(255,255,255); //Set the colour to white\n rect(0,0,512,512); //Draw a large rectangle\n\n gridSize = 4; //How big each rectangle will be\n width = 512; //GPU Width\n height = 512; //GPU Height\n noise = 5; //How noisy it will be\n\n //Give initial corner values\n R1 = random(1);\n R2 = random(1);\n R3 = random(1);\n R4 = random(1);\n\n plasma(0,0,width,height,R1,R2,R3,R4) //Start recursive function\n}\n\nvoid plasma(float x, y, width, height, c1, c2, c3, c4){ //Plasma function\n\n //Setup local variables\n float edge1, edge2, edge3, edge4, midPoint;\n\n //Work out the size of the next segments\n float newWidth = width / 2;\n float newHeight = height / 2;\n\n if((width > gridSize)||(height > gridSize)){ //If it is still bigger than the rectangle size\n\n midPoint = (c1 + c2 + c3 + c4) / 4 + displace(newWidth + newHeight); //Randomly change the midpoint\n\n //Calculate edges by averaging the corners\n edge1 = (c1 + c2) / 2;\n edge2 = (c2 + c3) / 2;\n edge3 = (c3 + c4) / 2;\n edge4 = (c4 + c1) / 2;\n\n //Make sure it doesn't displace too far\n max midPoint,0;\n min midPoint,1;\n\n //Run on the newly calculated segments\n plasma(x, y, newWidth, newHeight, c1, edge1, midPoint, edge4);\n plasma(x + newWidth, y, newWidth, newHeight, edge1, c2, edge2, midPoint);\n plasma(x + newWidth, y + newHeight, newWidth, newHeight, midPoint, edge2, c3, edge3);\n plasma(x, y + newHeight, newWidth, newHeight, edge4, midPoint, edge3, c4);\n }else{ //Woo! It's the right size\n float c = (c1 + c2 + c3 + c4) / 4; //Average the corners\n\n float grey = c*255; //Multiply the corners by 255 to get a valid color\n\n setColor(grey,grey,grey); //Set the color to your new color based on the \"height\"\n rect(x,y,gridSize,gridSize); //Draw your rectangle\n }\n}\n\nfloat displace(float num){ //Displace function, it just works\n float m = num / (width + height) * (1/noise);\n R1 = random(1);\n return (R1-0.5) * m;\n}\n\nfloat random(float x){ //C version of the ASM rand opcode\n preserve EAX;\n rand EAX;\n return EAX*x;\n}\n\nvoid setColor(float r,float g,float b){ //C version of the dcolor opcode\n mov #col.r,r;\n mov #col.g,g;\n mov #col.b,b;\n\n dcolor col;\n}\n\nvoid rect(float x, float y, float width, float height){ //C version of the drectwh opcode\n mov #pos.x,x;\n mov #pos.y,y;\n\n mov #size.x,width;\n mov #size.y,height;\n\n drectwh pos, size;\n dswap; //dswap to make it show since we're drawing to the sprite buffer\n}\n","gpuchip/examples/stargate.txt":"//STARGATE DIAL COMPUTER MAIN DISPLAY (realistic colors)\n//\n//How to connect:\n//GPU IOBus to Data Port\n//Port0 to \"Open\"\n//Port1 to \"Active\"\n//Port2 to \"Chevron\"\n//Port3 to \"Inbound\"\n//Port4 to iris\n//\n//That's all!\n\ndiv #65525,1.33;\nmov #65485,16; //65485 is the circle quality register\n\n//24 means circles have 24 sides\n//You can have up to 128 sides, but that LAGS\n//32 sides is not even noticable comparing to 128\n\n//= Misc decorations ==================\n\ndcolor stargate_out_ring;\ndcircle center,250;\ndcolor stargate_middle_ring;\ndcircle center,240;\ndcolor stargate_out_ring;\ndcircle center,223;\n\n//= Rotating ring =====================\nmov #65485,12;\ndcolor stargate_inner_ring;\n\nin ecx,2; //This block checks if chevron 7 is engaged\ncmp ecx,7; //If yes, dont spin\nmov eax,0;\njge _norotate;\n timer eax;\n_norotate:\n\nin ebx,1; //This one checks if stargate is active\nmul eax,ebx;\n\nin ebx,3; neg ebx; add ebx,1; //This one checks if its inbound\nmul eax,ebx; //wormhole\n\ndrotatescale eax,1; //rotate by EAX radians\ndmove center;\ndcircle 0,220;\n\ndrotatescale 0,1; //Reset scale/movment\ndmove 0;\n\n//= Inner ring around EH ==============\nmov #65485,24;\ndcolor stargate_out_ring;\ndcircle center,190;\n\n\n//= EH ================================\ndcolor black;\ndcircle center,180; //draw black hole instead of event horizon\n\ndcolor stargate_eventhorizon;\n\nin ebx,0; //Stargate active?\ncmp ebx,0;\nmov eax,0;\nje _active;\n rand eax;\n mul eax,0.1;\n add eax,0.9;\n_active:\n\nin ebx,0; mul ebx,180;\n\nmul #eventhorizon_radius,0.99;\nmul ebx,1.01;\nadd #eventhorizon_radius,ebx;\ndiv #eventhorizon_radius,2;\n\n\ndshade eax;\ndcircle center,#eventhorizon_radius;\n\n//= Iris ==============================\nmov edx,port4;\nneg edx; add edx,1;\n\nmov eax,#iris_status;\nsub eax,edx;\nfabs eax,eax;\n\ndmuldt ecx,8;\n\ncmp eax,0.02;\njl _donothing;\n cmp #iris_status,edx;\n jl _lower;\n sub #iris_status,ecx;\n jmp _donothing;\n _lower:\n add #iris_status,ecx;\n_donothing:\n\nmov #iris1.y,#iris_status;\nmul #iris1.y,#iris2.y;\n\ndmove center;\n\nmov ecx,12;\n_iris:\n fsin ebx,ecx; fabs ebx,ebx; div ebx,10; add ebx,0.7;\n\n mov eax,ecx; mul eax,0.490; add eax,0.01; //0.697\n add eax,#iris_status;\n\n drotatescale eax,1;\n\n dcolor iris_color;\n dshade ebx;\n\n drect iris1,iris2;\nloop _iris;\n\ndmove 0;\n\n//= Chevrons ==========================\nmov eax,1; //Chevron ID\nin ebx,2;\ndmove center;\n_chevron_loop:\n mov edx,eax; //Compute chevron angle in radians\n mul edx,0.69815;\n sub edx,1.23333;\n\n drotatescale edx,1; //Rotate chevron polygon\n dcolor stargate_chevron;\n\n mov edx,eax:#chevron_triggers;\n\n cmp edx,ebx; //Check if chevron is light up\n jle _noshade;\n dshade 0.25;\n _noshade:\n\n dvxpoly chevron_polygon,4; //draw chevron polygon\n\n inc eax;\n cmp eax,9;\n jle _chevron_loop;\n\n//= Computer text =====================\ndrotatescale 0,1; //reset movement and scale\ndmove 0;\n\nin eax,3; //Is inbound?\ncmp eax,0;\nje _dexit;\n\n timer eax; mul eax,2; fint eax; mod eax,2;\n dcolor sgc_text;\n dshade eax;\n\n dsetsize 64; //draw message\n dwrite sgc_inboundpos,sgc_inbound;\n\n_dexit:\ndexit;\n\n//= Helpers ===========================\n\nchevron_triggers:\ndb 9,4,5,6,7,1,2,3,8;\n// 1 2 3 4 5 6 7 8 9\n// Order in which chevrons light up\n// Only 1-7 are used though\n\n//=====================================\n\ncolor sgc_text,255,255,255;\n\nvector2f sgc_inboundpos,120,215;\nstring sgc_inbound,'INBOUND';\n\ncolor stargate_out_ring, 116,105, 76;\ncolor stargate_middle_ring, 93 , 85, 60;\ncolor stargate_inner_ring, 138,137,108;\ncolor stargate_eventhorizon, 93,114,162;\ncolor stargate_chevron, 250,162, 54;\ncolor iris_color, 192,192,192;\n\ncolor black,0,0,0;\n\nvector2f center,256,256;\n\nvector2f iris1,-44,0;\nvector2f iris2,44,175;\n\nvector2f chevcenter,-16,-256;\nvector2f chevsize,32,32;\n\nalloc eventhorizon_radius;\nalloc iris_status;\n\n//raw chevron poly data\n//format: \nchevron_polygon: //n=4\ndb -16,-251;\ndb 16,-251;\ndb 10,-230;\ndb -10,-230;\n"},"gpuchip/lib/":{"gpuchip/lib/drivers/":{"gpuchip/lib/drivers/drv_gl_toolkit.txt":"#ifndef GL\n#include \n#endif\n\n#ifndef GLT\n\n#define GLT\n#define GLT_MAX_TRIANGLES 32\n\nfloat __GLT_VERTBUFF[(GLT_MAX_TRIANGLES * 3) * 3];\nfloat __GLT_VERTCNT = 0;\n\nvoid gltVertex(float x, float y, float z)\n{\n if ((__GLT_VERTCNT / 3) >= GLT_MAX_TRIANGLES)\n return;\n\n float* ptr = __GLT_VERTBUFF;\n ptr += (__GLT_VERTCNT * 3);\n\n *ptr = x;\n *(++ptr) = y;\n *(++ptr) = z;\n\n __GLT_VERTCNT++;\n}\n\nvoid gltTriangle(float x1, float y1, float z1,\n float x2, float y2, float z2,\n float x3, float y3, float z3)\n{\n gltVertex(x1, y1, z1);\n gltVertex(x2, y2, z2);\n gltVertex(x3, y3, z3);\n}\n\nvoid gltQuad(float tlx, float tly, float tlz,\n float trx, float try, float trz,\n float brx, float bry, float brz,\n float blx, float bly, float blz)\n{\n gltTriangle(\n trx, try, trz,\n tlx, tly, tlz,\n blx, bly, blz\n );\n\n gltTriangle(\n brx, bry, brz,\n trx, try, trz,\n blx, bly, blz\n );\n}\n\nvoid gltCube(float cex, float cey, float cez, float size)\n{\n float s2 = size / 2;\n\n gltQuad(\n cex - s2, cey + s2, cez - s2,\n cex + s2, cey + s2, cez - s2,\n cex + s2, cey - s2, cez - s2,\n cex - s2, cey - s2, cez - s2\n );\n\n gltQuad(\n cex + s2, cey + s2, cez + s2,\n cex - s2, cey + s2, cez + s2,\n cex - s2, cey - s2, cez + s2,\n cex + s2, cey - s2, cez + s2\n );\n\n gltQuad(\n cex - s2, cey + s2, cez + s2,\n cex - s2, cey + s2, cez - s2,\n cex - s2, cey - s2, cez - s2,\n cex - s2, cey - s2, cez + s2\n );\n\n gltQuad(\n cex + s2, cey + s2, cez - s2,\n cex + s2, cey + s2, cez + s2,\n cex + s2, cey - s2, cez + s2,\n cex + s2, cey - s2, cez - s2\n );\n\n gltQuad(\n cex - s2, cey + s2, cez + s2,\n cex + s2, cey + s2, cez + s2,\n cex + s2, cey + s2, cez - s2,\n cex - s2, cey + s2, cez - s2\n );\n\n gltQuad(\n cex - s2, cey - s2, cez - s2,\n cex + s2, cey - s2, cez - s2,\n cex + s2, cey - s2, cez + s2,\n cex - s2, cey - s2, cez + s2\n );\n}\n\nvoid gltClearBuffer()\n{\n __GLT_VERTCNT = 0;\n}\n\nvoid gltFlushBuffer()\n{\n if (__GLT_VERTCNT <= 3)\n return;\n\n float vcnt = __GLT_VERTCNT;\n float tcnt = (vcnt / 3) - (vcnt % 3);\n\n if (tcnt > GLT_MAX_TRIANGLES)\n tcnt = GLT_MAX_TRIANGLES;\n\n glPoly3D(__GLT_VERTBUFF, tcnt);\n glFlush();\n}\n\n#endif\n","gpuchip/lib/drivers/drv_gl.txt":"// [Author] - Drunkie\n// [Description] - A graphics driver that provides C-style functions for GPU\n// [Documentation] - http://goo.gl/DHhYb\n\n\n#define GL\n\n// Font\n#define GL_FONT_LUCIDA_CONSOLE 0\n#define GL_FONT_COURIER_NEW 1\n#define GL_FONT_TREBUCHET 2\n#define GL_FONT_ARIAL 3\n#define GL_FONT_TIMES_NEW_ROMAN 4\n#define GL_FONT_COOLVETICA 5\n#define GL_FONT_AKBAR 6\n#define GL_FONT_CSD 7\n\n// Buffer\n#define GL_BUFFER_FRONT 0\n#define GL_BUFFER_BACK 1\n#define GL_BUFFER_SPRITE 1\n#define GL_BUFFER_VERTEX 2\n\n// Coordinate pipe\n#define GL_CPIPE_DIRECT 0\n#define GL_CPIPE_RESOLUTION 1\n#define GL_CPIPE_0_1 2\n#define GL_CPIPE_N1_1 3\n#define GL_CPIPE_N256_256 4\n\n// Vertex pipe\n#define GL_VPIPE_XY 0\n#define GL_VPIPE_YZ 1\n#define GL_VPIPE_XZ 2\n#define GL_VPIPE_XYZPROJ 3\n#define GL_VPIPE_XYTRANSFORM 4\n#define GL_VPIPE_XYZTRANSFORM 5\n\n// denable / ddisable\n#define GL_VERTEX_BUFFER 0\n#define GL_VERTEX_ZSORT 1\n#define GL_VERTEX_LIGHTING 2\n#define GL_VERTEX_CULLING 3\n#define GL_VERTEX_DCULLING 4\n#define GL_VERTEX_TEXTURING 5\n\n// Fillmode\n#define GL_FILL_SOLID 0\n#define GL_FILL_WIREFRAME 1\n#define GL_FILL_TEXTURE 2\n\n// Cullmode\n#define GL_CULL_FRONT 0\n#define GL_CULL_BACK 1\n\n// Lightmode\n#define GL_LIGHT_FRONT 1\n#define GL_LIGHT_BACK -1\n\n// Horizontal font\n#define GL_ALIGN_LEFT 0\n#define GL_ALIGN_CENTER 1\n#define GL_ALIGN_RIGHT 2\n\n// Vertical font\n#define GL_VALIGN_TOP 0\n#define GL_VALIGN_MIDDLE 1\n#define GL_VALIGN_BOTTOM 2\n\n// Compatibility\n#define glSetTexture glBindTexture\n#define glWriteFmt glWriteFormat\n#define glFontHAlign glFontAlign\n\n\n// Clear\nvoid glClear( float r, float g, float b ) {\n mov #GL_BG.r,r; mov #GL_BG.g,g; mov #GL_BG.b,b; mov #GL_BG.a,255;\n dclrscr GL_BG;\n}\nvoid glClear4( float r, float g, float b, float a ) {\n mov #GL_BG.r,r; mov #GL_BG.g,g; mov #GL_BG.b,b; mov #GL_BG.a,a;\n dclrscr GL_BG;\n}\nvoid glClearTexture() {\n dclrtex;\n}\nvoid glHWClear( float n ) {\n mov #regHWClear,n;\n}\n\n// Color\nvoid glColor( float r, float g, float b ) {\n mov #GL_FG.r,r; mov #GL_FG.g,g; mov #GL_FG.b,b; mov #GL_FG.a,255;\n dcolor GL_FG;\n}\nvoid glColor4( float r, float g, float b, float a ) {\n mov #GL_FG.r,r; mov #GL_FG.g,g; mov #GL_FG.b,b; mov #GL_FG.a,a;\n dcolor GL_FG;\n}\nvoid glBrightness( float r, float g, float b, float a ) {\n mov #regBrightnessR,r;\n mov #regBrightnessG,g;\n mov #regBrightnessB,b;\n mov #regBrightnessW,a;\n}\nvoid glContrast( float r, float g, float b, float a ) {\n mov #regContrastR,r;\n mov #regContrastG,g;\n mov #regContrastB,b;\n mov #regContrastW,a;\n}\nvoid glShade( float n ) {\n dshade n;\n}\nvoid glShadeNorm( float n ) {\n dshadenorm n;\n}\n\n// Texture\nvoid glBindTexture( char* str ) {\n dxtexture str;\n}\nvoid glTexture( float id ) {\n dtexture id;\n}\nvoid glTextureSize( float n ) {\n mov #regTexSize,n;\n}\nvoid glTextureDataPtr( float n ) {\n mov #regTexDataPtr,n;\n}\nvoid glTextureDataSize( float n ) {\n mov #regTexDataSz,n;\n}\nvoid glTextureRotation( float n ) {\n mov #regTexRotation,n;\n}\nvoid glTextureScale( float n ) {\n mov #regTexScale,n;\n}\nvoid glTextureCenterUV( float u, float v ) {\n mov #regTexCenterU,u;\n mov #regTexCenterV,v;\n}\nvoid glTextureOffsetUV( float u, float v ) {\n mov #regTexOffsetU,u;\n mov #regTexOffsetV,v;\n}\n\n// Frame\nvoid glSleep( float ms ) {\n div ms,1000;\n timer #GL_CURTIME;\n sub #GL_CURTIME,#GL_TIMESTAMP;\n if (*GL_CURTIME <= ms) {\n mov #regHWClear,0;\n dexit;\n }\n timer #GL_TIMESTAMP;\n}\nvoid glExit() {\n dexit;\n}\n\n// Pipeline\nvoid glCoordPipe( float c ) {\n dcpipe c;\n}\nvoid glVertexPipe( float v ) {\n dvxpipe v;\n}\n\n// Hardware\nvoid glReset( float n ) {\n mov #regReset,n;\n}\nvoid glHalt( float n ) {\n mov #regHalt,n;\n}\nvoid glRAMReset( float n ) {\n mov #regRAMReset,n;\n}\nvoid glHScale( float n ) {\n mov #regHScale,n;\n}\nvoid glVScale( float n ) {\n mov #regVScale,n;\n}\nvoid glHWScale( float n ) {\n mov #regHWScale,n;\n}\nvoid glHWRotate( float n ) {\n mov #regRotation,n;\n}\n\n// Offset\nvoid glOffset( float x, float y ) {\n mov #GL_V1.x,x; mov #GL_V1.y,y;\n dmove GL_V1;\n}\nfloat glOffsetX() {\n preserve eax;\n mov eax,#regOffsetX;\n}\nfloat glOffsetY() {\n preserve eax;\n mov eax,#regOffsetY;\n}\nvoid glCenter( float x, float y ) {\n mov #regCenterX,x;\n mov #regCenterY,y;\n}\n\n// Async\nvoid glAsyncReset( float n ) {\n mov #regAsyncReset,n;\n}\nvoid glAsyncClk( float n ) {\n mov #regAsyncClk,n;\n}\nvoid glAsyncFreq( float n ) {\n mov #regAsyncFreq,n;\n}\nvoid glEntryPoint( float idx, float ptr ) {\n dentrypoint idx,ptr;\n}\nvoid glBegin() {\n dbegin;\n}\nvoid glEnd() {\n dend;\n}\nvoid glSwap() {\n dswap;\n}\nvoid glSync() {\n dvsync;\n}\n\n// Cursor\nvoid glCursor( float n ) {\n mov #regCursor,n;\n}\nfloat glCursorX() {\n preserve eax;\n mov eax,#regCursorX;\n}\nfloat glCursorY() {\n preserve eax;\n mov eax,#regCursorY;\n}\n\n// Circle\nvoid glCircleQuality( float n ) {\n mov #regCircleQuality,n;\n}\nvoid glCircleStart( float n ) {\n mov #regCircleStart,n;\n}\nvoid glCircleEnd( float n ) {\n mov #regCircleEnd,n;\n}\n\n// Screen scaling\nvoid glScreenScale( float n ) {\n mov #regScale,n;\n}\nvoid glScreenScaleX( float x ) {\n mov #regScaleX,x;\n}\nvoid glScreenScaleY( float y ) {\n mov #regScaleY,y;\n}\n\n// 2D graphics\nvoid glCircle( float x, float y, float radius ) {\n mov #GL_V1.x,x; mov #GL_V1.y,y;\n dcircle GL_V1,radius;\n}\nvoid glRect( float x, float y, float dx, float dy ) {\n mov #GL_V1.x,x; mov #GL_V1.y,y;\n mov #GL_V2.x,dx; mov #GL_V2.y,dy;\n drect GL_V1,GL_V2;\n}\nvoid glRectWH( float x, float y, float w, float h ) {\n mov #GL_V1.x,x; mov #GL_V1.y,y;\n mov #GL_V2.x,w; mov #GL_V2.y,h;\n drectwh GL_V1,GL_V2;\n}\nvoid glORect( float x, float y, float dx, float dy ) {\n mov #GL_V1.x,x; mov #GL_V1.y,y;\n mov #GL_V2.x,dx; mov #GL_V2.y,dy;\n dorect GL_V1,GL_V2;\n}\nvoid glORectWH( float x, float y, float w, float h ) {\n mov #GL_V1.x,x; mov #GL_V1.y,y;\n mov #GL_V2.x,w; mov #GL_V2.y,h;\n dorectwh GL_V1,GL_V2;\n}\nvoid glPixel( float x, float y ) {\n mov #GL_V1.x,x; mov #GL_V1.y,y;\n dpixel GL_V1,GL_FG;\n}\nvoid glLine( float x, float y, float dx, float dy ) {\n mov #GL_V1.x,x; mov #GL_V1.y,y;\n mov #GL_V2.x,dx; mov #GL_V2.y,dy;\n dline GL_V1,GL_V2;\n}\nvoid glLineWidth( float w ) {\n dsetwidth w;\n}\nvoid glPoly2D( float* buffer, float count ) {\n dvxdata_2f buffer,count;\n}\n\n\n// Text\nvoid glFont( float id ) {\n dsetfont id;\n}\nvoid glFontAlign( float n ) {\n mov #regFontHalign,n;\n}\nvoid glFontVAlign( float n ) {\n mov #regFontValign,n;\n}\nvoid glFontSize( float n ) {\n dsetsize n;\n}\nfloat glTextWidth( char* str ) {\n preserve eax;\n dtextwidth eax,str;\n}\nfloat glTextHeight( char* str ) {\n preserve eax;\n dtextheight eax,str;\n}\nvoid glWriteString( float x, float y, char* str ) {\n mov #GL_V1.x,x; mov #GL_V1.y,y;\n dwrite GL_V1,str;\n}\nvoid glWriteFloat( float x, float y, float n ) {\n mov #GL_V1.x,x; mov #GL_V1.y,y;\n dwritef GL_V1,n;\n}\nvoid glWriteInt( float x, float y, float n ) {\n mov #GL_V1.x,x; mov #GL_V1.y,y;\n dwritei GL_V1,n;\n}\nvoid glWriteFormat( float x, float y, char* str ) {\n mov #GL_V1.x,x; mov #GL_V1.y,y;\n dwritefmt GL_V1,str;\n}\nfloat glParamList() {\n preserve eax;\n mov eax,#regParamList;\n}\n\n\n// 3D graphics\nvoid glPoly3D( float* buffer, float count ) {\n\n if (*GL_MUPDATE == 1) {\n mov #GL_MUPDATE,0;\n mrotate GL_MROTATEMATRIX,GL_VROTATE;\n mtranslate GL_MTRANSLATEMATRIX,GL_VTRANSLATE;\n mscale GL_MSCALEMATRIX,GL_VSCALE;\n mmov GL_MMODELMATRIX,GL_MTRANSLATEMATRIX;\n mmul GL_MMODELMATRIX,GL_MROTATEMATRIX;\n mmul GL_MMODELMATRIX,GL_MSCALEMATRIX;\n mmov GL_MMODELVIEWMATRIX,GL_MVIEWMATRIX;\n mmul GL_MMODELVIEWMATRIX,GL_MMODELMATRIX;\n mload GL_MMODELVIEWMATRIX;\n mloadproj GL_MPROJECTIONMATRIX;\n }\n\n if (*GL_FILLMODE == GL_FILL_SOLID) {\n dvxdata_3f buffer,count;\n }\n else if (*GL_FILLMODE == GL_FILL_WIREFRAME) {\n dvxdata_3f_wf buffer,count;\n }\n else if (*GL_FILLMODE == GL_FILL_TEXTURE) {\n dvxdata_3f_tex buffer,count\n }\n}\nvoid glFlush() {\n dvxflush;\n}\nvoid glEnable( float n ) {\n denable n;\n}\nvoid glDisable( float n ) {\n ddisable n;\n}\nvoid glLightPos( float x, float y, float z ) {\n mov #GL_LIGHTPOS.x,x; mov #GL_LIGHTPOS.y,y; mov #GL_LIGHTPOS.z,z;\n dsetlight 0,GL_LIGHTDATA;\n}\nvoid glLightColor( float r, float g, float b, float a ) {\n mov #GL_LIGHTCOL.r,r; mov #GL_LIGHTCOL.g,g;\n mov #GL_LIGHTCOL.b,b; mov #GL_LIGHTCOL.a,a;\n dsetlight 0,GL_LIGHTDATA;\n}\nvoid glFillMode( float n ) {\n mov #GL_FILLMODE,n;\n}\nvoid glLookAt( float x, float y, float z, float tx, float ty, float tz, float ux, float uy, float uz ) {\n mov #GL_VLOOKAT_POS.x,x; mov #GL_VLOOKAT_POS.y,y; mov #GL_VLOOKAT_POS.z,z;\n mov #GL_VLOOKAT_TARG.x,tx; mov #GL_VLOOKAT_TARG.y,ty; mov #GL_VLOOKAT_TARG.z,tz;\n mov #GL_VLOOKAT_UP.x,ux; mov #GL_VLOOKAT_UP.y,uy; mov #GL_VLOOKAT_UP.z,uz;\n mlookat GL_MVIEWMATRIX,GL_VLOOKAT;\n mov #GL_MUPDATE,1;\n}\nvoid glPerspective( float fov, float asp, float znear, float zfar ) {\n mov #GL_VPERSPECTIVE.x,fov; mov #GL_VPERSPECTIVE.y,asp;\n mov #GL_VPERSPECTIVE.z,znear; mov #GL_VPERSPECTIVE.w,zfar;\n mperspective GL_MPROJECTIONMATRIX,GL_VPERSPECTIVE;\n mov #GL_MUPDATE,1;\n}\nvoid glRotate( float x, float y, float z, float w ) {\n mov #GL_VROTATE.x,x; mov #GL_VROTATE.y,y; mov #GL_VROTATE.z,z; mov #GL_VROTATE.w,w;\n mov #GL_MUPDATE,1;\n}\nvoid glTranslate( float x, float y, float z ) {\n mov #GL_VTRANSLATE.x,x; mov #GL_VTRANSLATE.y,y; mov #GL_VTRANSLATE.z,z;\n mov #GL_MUPDATE,1;\n}\nvoid glScale( float x, float y, float z ) {\n mov #GL_VSCALE.x,x; mov #GL_VSCALE.y,y; mov #GL_VSCALE.z,z;\n mov #GL_MUPDATE,1;\n}\nvoid glZOffset( float n ) {\n mov #regZOffset,n;\n}\nvoid glCullDistance( float n ) {\n mov #regCullDistance,n;\n}\nvoid glCullMode( float n ) {\n mov #regCullMode,n;\n}\nvoid glLightMode( float n ) {\n mov #regLightMode,n;\n}\nvoid glVertexArray( float n ) {\n mov #regVertexArray,n;\n}\n\n// Other\nvoid glVertexMode( float n ) {\n mov #regVertexMode,n;\n}\nvoid glSetRenderTarget( float n ) {\n if (n == GL_BUFFER_FRONT) {\n dsetbuf_fbo;\n }\n else if (n == GL_BUFFER_BACK) {\n dsetbuf_spr;\n }\n else if (n == GL_BUFFER_VERTEX) {\n dsetbuf_vx;\n }\n}\nfloat glIndex() {\n preserve eax;\n mov eax,#regIndex;\n}\n\n\n// Allocated variables for GL\ncolor GL_FG,255,255,255;\ncolor GL_BG;\nvec4f GL_V1;\nvec4f GL_V2;\n\nalloc GL_TIMESTAMP;\nalloc GL_CURTIME;\nalloc GL_FILLMODE;\n\nGL_LIGHTDATA:\nvec4f GL_LIGHTPOS,0,0,-10;\ncolor GL_LIGHTCOL,255,255,255,1;\n\nGL_VLOOKAT:\nvec3f GL_VLOOKAT_POS,0,0,-10;\nvec3f GL_VLOOKAT_TARG,0,0,0;\nvec3f GL_VLOOKAT_UP,0,1,0;\n\nmatrix GL_MROTATEMATRIX;\nmatrix GL_MTRANSLATEMATRIX;\nmatrix GL_MSCALEMATRIX;\nmatrix GL_MPROJECTIONMATRIX;\nmatrix GL_MVIEWMATRIX;\nmatrix GL_MMODELMATRIX;\nmatrix GL_MMODELVIEWMATRIX;\nalloc GL_MUPDATE,1;\n\nvec4f GL_VROTATE;\nvec4f GL_VTRANSLATE;\nvec4f GL_VPERSPECTIVE;\nvec4f GL_VSCALE,1,1,1,0;\n"}}},"spuchip/":{"spuchip/examples/":{"spuchip/examples/mario_theme.txt":"// Author: Jasongamer\n// Song: Mario Underwater Theme\n\n// Set track wave to channel 0 and start\nwset 0,trackwave;\nchwave 0,0;\nchvolume 0,0.2;\nchstart 0;\n\n// Set track wave to channel 1 and start\nwset 1,trackwave;\nchwave 1,1;\nchvolume 1,0.2;\nchstart 1;\n\n// Set bass wave to channel 2 and start\nwset 2,basswave;\nchwave 2,1;\nchvolume 2,0.3;\nchstart 2;\n\n// Get track length\ntracklen = strlen(trackA);\n\nvoid main()\n{\n // Tempo\n if ((i > 120) && (i <= 230))\n tempo( 1000 );\n else\n tempo( 864 );\n\n // Track A\n note = 2;\n fpwr note,(trackA[i]/12);\n note /= 100;\n chpitch 0,note;\n\n // Track B\n note = 2;\n fpwr note,(trackB[i]/12);\n note /= 100;\n chpitch 1,note;\n\n // Bass\n note = 2;\n fpwr note,(bass[i]/12);\n note /= 100;\n chpitch 2,note;\n\n // Index\n i++; mod i,tracklen;\n\n // Repeat\n jmp main;\n}\n\n// Accurate tempo function for beats-per-minute\nvoid tempo( float bpm )\n{\n timer timestamp;\n while ((time - timestamp) < (60 / bpm)) { timer time; }\n}\n\n// Returns the length of a string\nfloat strlen(char* str)\n{\n char* strptr = str;\n while (*strptr++);\n return (strptr - str);\n}\n\nfloat note, i;\nfloat tracklen;\nfloat time, timestamp;\n\nstring trackwave,\"synth/square.wav\";\nstring basswave,\"synth/tri.wav\";\n\ntrackA:\n\n// Intro\ndb 73,73,73,73, 75,75,75,75, 77,77,77,77, 78,78,78,78, 80,80,80,80, 81,81,81,81;\ndb 82,-1,82,-1, 82,82,82,-1, 82,82,82,-1, 82,82,82,82, 82,82,82,-1, -1,-1,78,78;\n\n// Part 1\ndb 87,87,87,87, 87,87,87,87, 87,87,87,-1, 86,86,86,86, 86,86,86,86, 86,86,86,-1;\ndb 87,87,87,87, 87,87,87,87, 87,87,87,-1, -1,-1,78,78, 80,80,82,82, 83,83,85,85;\ndb 87,87,87,87, 87,87,87,87, 87,87,87,-1, 86,86,86,86, 86,86,86,-1, 88,88,88,-1;\ndb 87,87,87,87, 87,87,87,87, 87,87,87,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,78,78;\ndb 85,85,85,85, 85,85,85,85, 85,85,85,-1, 84,84,84,84, 84,84,84,84, 84,84,84,-1;\ndb 85,85,85,85, 85,85,85,85, 85,85,85,-1, -1,-1,78,78, 80,80,82,82, 83,83,84,84;\ndb 85,85,85,85, 85,85,85,85, 85,85,85,-1, 78,78,78,78, 78,78,78,-1, 88,88,88,-1;\ndb 87,87,87,87, 87,87,87,87, 87,87,87,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,78,78;\n\n// Part 2\ndb 90,90,90,90, 90,90,90,90, 90,90,90,-1, 90,90,90,90, 90,90,90,90, 90,90,90,-1;\ndb 90,90,90,90, 90,90,90,90, 90,90,90,-1, 90,90,90,-1, 92,92,-1,-1, -1,-1,90,90;\ndb 88,88,88,88, 88,88,88,88, 88,88,88,-1, 88,88,88,88, 88,88,88,88, 88,88,88,-1;\ndb 88,88,88,88, 88,88,88,88, 88,88,88,-1, 88,88,88,-1, 90,90,-1,-1, -1,-1,88,88;\ndb 87,87,87,87, 87,87,87,87, 87,87,87,-1, 80,80,80,-1, 82,82,82,-1, 88,88,88,-1;\ndb 87,-1,87,-1, 87,87,87,87, 87,-1,82,82, 83,83,83,83, 83,83,83,83, 83,83,83,-1;\n\ndb 0; // End string\n\ntrackB:\n\n// Intro\ndb 73,73,73,73, 72,72,72,72, 71,71,71,71, 70,70,70,70, 71,71,71,71, 72,72,72,72;\ndb 73,-1,73,-1, 73,73,73,-1, 75,75,75,-1, 76,76,76,76, 76,76,76,-1, -1,-1,-1,-1;\n\n// Part 1\ndb 78,78,78,78, 78,78,78,78, 78,78,78,-1, 77,77,77,77, 77,77,77,77, 77,77,77,-1;\ndb 78,78,78,78, 78,78,78,78, 78,78,78,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1;\ndb 78,78,78,78, 78,78,78,78, 78,78,78,-1, 77,77,77,77, 77,77,77,-1, 80,80,80,-1;\ndb 78,78,78,78, 78,78,78,78, 78,78,78,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1;\ndb 76,76,76,76, 76,76,76,76, 76,76,76,-1, 75,75,75,75, 75,75,75,75, 75,75,75,-1;\ndb 76,76,76,76, 76,76,76,76, 76,76,76,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1;\ndb 76,76,76,76, 76,76,76,76, 76,76,76,-1, 70,70,70,70, 70,70,70,-1, 80,80,80,-1;\ndb 78,78,78,78, 78,78,78,78, 78,78,78,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1;\n\n// Part 2\ndb 87,87,87,87, 87,87,87,87, 87,87,87,-1, 85,85,85,85, 85,85,85,85, 85,85,85,-1;\ndb 84,84,84,84, 84,84,84,84, 84,84,84,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1;\ndb 85,85,85,85, 85,85,85,85, 85,85,85,-1, 84,84,84,84, 84,84,84,84, 84,84,84,-1;\ndb 83,83,83,83, 83,83,83,83, 83,83,83,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1;\ndb 71,71,71,71, 71,71,71,71, 71,71,71,-1, 76,76,76,-1, 78,78,78,-1, 82,82,82,-1;\ndb 82,-1,82,-1, 82,82,82,-1, -1,-1,76,76, 75,75,75,75, 75,75,75,75, 75,75,75,-1;\n\ndb 0; // End string\n\nbass:\n\n// Intro\ndb -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1;\ndb -1,-1,-1,-1, -1,-1,-1,-1, 66,66,66,-1, 66,66,66,66, 66,66,66,66, 66,66,66,-1;\n\n// Part 1\ndb 59,59,59,-1, 66,66,66,-1, 71,71,71,-1, 58,58,58,-1, 66,66,66,-1, 70,70,70,-1;\ndb 59,59,59,-1, 66,66,66,-1, 71,71,71,-1, 63,63,63,-1, 66,66,66,-1, 71,71,71,-1;\ndb 59,59,59,-1, 66,66,66,-1, 71,71,71,-1, 58,58,58,-1, 66,66,66,-1, 70,70,70,-1;\ndb 59,59,59,-1, 66,66,66,-1, 71,71,71,-1, 63,63,63,-1, 66,66,66,-1, 71,71,71,-1;\ndb 61,61,61,-1, 66,66,66,-1, 70,70,70,-1, 60,60,60,-1, 65,65,65,-1, 69,69,69,-1;\ndb 61,61,61,-1, 66,66,66,-1, 70,70,70,-1, 58,58,58,-1, 66,66,66,-1, 70,70,70,-1;\ndb 61,61,61,-1, 66,66,66,-1, 70,70,70,-1, 58,58,58,-1, 66,66,66,-1, 70,70,70,-1;\ndb 59,59,59,-1, 66,66,66,-1, 71,71,71,-1, 54,54,54,-1, 66,66,66,-1, 71,71,71,-1;\n\n// Part 2\ndb 59,59,59,-1, 66,66,66,-1, 75,75,75,-1, 58,58,58,-1, 66,66,66,-1, 73,73,73,-1;\ndb 57,57,57,-1, 66,66,66,-1, 72,72,72,-1, 60,60,60,-1, 66,66,66,-1, 75,75,75,-1;\ndb 61,61,61,-1, 68,68,68,-1, 76,76,76,-1, 60,60,60,-1, 68,68,68,-1, 76,76,76,-1;\ndb 59,59,59,-1, 68,68,68,-1, 76,76,76,-1, 58,58,58,-1, 66,66,66,-1, 76,76,76,-1;\ndb 47,47,47,-1, 66,66,66,-1, 75,75,75,-1, 54,54,54,-1, 66,66,66,-1, 66,66,66,-1;\ndb 64,-1,64,-1, 64,64,64,-1, -1,-1,58,58, 59,59,59,59, 59,59,59,59, 59,59,59,-1;\n\ndb 0; // End string\n","spuchip/examples/beatbox.txt":"wset 4,inst1;\nchwave 1,4;\nchpitch 1,2.55;\n\nchwave 2,0;\nchvolume 2,0.5;\nchstart 2;\nchpitch 2,0;\n\nmainloop:\n timer r0;\n mul r0,6;\n mov r1,r0;\n\n fint r0;\n mod r0,16;\n add r0,0;\n mod r1,1;\n currentTick = r0;\n currentTickTime = r1;\n\n instr1 = patternData1[currentTick];\n instr2 = patternData2[currentTick];\n instr3 = patternData3[currentTick];\n\n if ((pinstr1 == 0) && (instr1 == 1)) {\n chstart 0;\n } else {\n chstop 0;\n }\n\n if ((pinstr2 == 0) && (instr2 == 1)) {\n chstart 1;\n } else {\n chstop 1;\n }\n\n mov r0,currentTickTime; neg r0; add r0,1; fpwr r0,4;\n mul r0,0.6; // add r0,0.64;\n chpitch 0,r0;\n\n mov r0,instr3;\n mul r0,0.1;\n add r0,0.2;\n chpitch 2,r0;\njmp mainloop;\n\nfloat currentTick,currentTickTime;\nfloat instr1,instr2,instr3;\nfloat pinstr1,pinstr2;\n\npatternData1: db 1,1,0,0, 1,0,0,0, 1,0,0,1, 0,1,0,0, 1,1,0,0, 1,1,0,0, 1,1,0,1, 1,0,1,0;\npatternData2: db 0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0;\npatternData3: db 0,1,0,2, 0,1,2,2, 0,0,1,1, 1,2,1,2, 2,2,1,2, 2,2,1,1, 1,1,0,0, 0,0,1,2;\n\nstring inst1,\"synth/pink_noise.wav\";\n"}},"cpuchip/":{"cpuchip/examples/":{"cpuchip/examples/udh_test.txt":"//------------------------------------------------------------------------------\n// Universal Device Host driver test application\n//------------------------------------------------------------------------------\n#pragma CRT ZCRT\n\n//Include drivers for console screen and device host\n#include \n#include \n\nvoid main() {\n float i;\n udhSetBusAddress(65536);\n\n cscrInitialize(0);\n\n udhQueryDevices();\n\n for (i = 0; i < MAX_CONSOLE_SCREENS; i++) {\n cscrSelect(i);\n cscrSetActive(1);\n cscrClear();\n\n cscrSetCursor(0,0);\n cscrPrintLine(\"Screen \",930);\n cscrPrintNumber(i,930);\n }\n\n cscrSelect(0);\n cscrSetCursor(0,2);\n cscrPrintLine(\"UDH driver test\\n\",039);\n for (i = 0; i < 8; i++) {\n cscrPrintLine(\"DEVICE \",999);\n cscrPrintNumber(i,999);\n cscrPrintLine(\": \",999);\n cscrPrintLine(udhGetDeviceName(i),666);\n cscrPrintLine(\"\\n\",999);\n }\n}\n","cpuchip/examples/helloworld.txt":"//Wired Hello World!\n//Connect CPU membus input to console screen\n//Connect CPUs CLK input to button (toggle)\n//Notice how you can store your\n//subroutines/calls in DATA area\njmp _code;\nmessage:\n db 'Hello World!',0;\nWriteString: //ESI - String pointer, EDX - Param\n mov eax,65536;\n AWriteLoop:\n cmp #esi,0; //Terminate on char 0\n je AEnd;\n mov #eax,#esi; //Output char\n inc eax;\n mov #eax,edx; //Output char param\n inc eax;\n inc esi;\n jmp AWriteLoop;\n AEnd:\nret //Return from call\n\n_code:\n mov esi,message;\n mov edx,000999; //White foreground on black background\n call WriteString;\n\n//More about colors:\n//Lower 3 digits are foreground,\n//and higher 3 digits are background\n//Each of 3 digits shows amount of\n//RED, GREEN, and BLUE (in order)\n//Each color has 10 shades - from 0 to 9\n//\n//For example, 999044 will be dark yellow (044) on\n//a white background (999)\n//\n//Experiment with colors!\n//\n//Also, the 7th digit (if its not equal to 0) will\n//cause the character to blink by changing foreground and\n//background places (actual data in memory wont change)\n"},"cpuchip/lib/":{"cpuchip/lib/zcrt/":{"cpuchip/lib/zcrt/string.txt":"//------------------------------------------------------------------------------\n// ZCPU CRT sourcecode (for HL-ZASM compiler) (C) 2011 by Black Phoenix\n//\n// String library. Contains functions to work with C strings (C89-compatible)\n//------------------------------------------------------------------------------\n\n#define NULL 0\n\n//copies n bytes between two memory areas; if there is overlap, the behavior is undefined\nvoid *memcpy(void *dest, void *src, float n) {\n preserve esi,edi;\n register float rem;\n\n esi = src;\n edi = dest;\n rem = n;\n while (rem) {\n register float count = rem;\n min count,8192;\n mcopy count;\n rem = rem - count;\n }\n return dest;\n}\n\n//copies n bytes between two memory areas; unlike with memcpy the areas may overlap\n//void *memmove(void *dest, void *src, float n);\n#define memmove memcpy\n\n//returns a pointer to the first occurrence of c in the first n bytes of s, or NULL if not found\nvoid* memchr(void *s, float c, float n) {\n register void *r = s;\n register float rem = n;\n\n while (rem) {\n if (*r == c) {\n return r;\n }\n ++r;\n --rem;\n }\n\n return NULL;\n}\n\n//compares the first n bytes of two memory areas\n//int memcmp(const void *s1, const void *s2, float n);\n#define memcmp strcmp\n\n//overwrites a memory area with n copies of c\nvoid* memset(void *ptr, float c, float n) {\n register void *p = ptr;\n register float rem = n;\n register float ch = c;\n\n while (rem) {\n *p++ = ch;\n --rem;\n }\n\n return ptr;\n}\n\n//appends the string src to dest\nchar* strcat(char *src, *dest) {\n register char *srcptr, *destptr;\n\n srcptr = src;\n while (*++srcptr) ;\n\n destptr = dest;\n while (*srcptr++ = *destptr++) ;\n return src;\n}\n\n//appends at most n bytes of the string src to dest\nchar* strncat(char *src, *dest, float n) {\n register char *srcptr, *destptr;\n register float i;\n\n srcptr = src;\n srcptr--;\n while (*++srcptr) ;\n\n destptr = dest;\n i = n;\n while (i--) {\n if (*srcptr++ = *destptr++) continue;\n }\n *srcptr = 0;\n return src;\n}\n\n//locates character c in a string, searching from the beginning\nchar* strchr(char *str, c) {\n register char *strptr, ch;\n strptr = str;\n ch = c;\n while(*strptr) {\n if (*strptr == ch) return strptr;\n ++strptr;\n }\n return 0;\n}\n\n//locates character c in a string, searching from the end\nchar* strrchr(char *str, c) {\n register char *strptr, ch;\n register char *findptr;\n\n findptr = 0;\n strptr = str;\n ch = c;\n while (*strptr) {\n if (*strptr == ch) findptr = strptr;\n ++strptr;\n }\n return findptr;\n}\n\n//compares two strings lexicographically\nfloat strcmp(char *src, *dest) {\n register char *srcptr, *destptr;\n\n srcptr = src;\n destptr = dest;\n while (*srcptr == *destptr) {\n if (*srcptr == 0) return 0;\n ++srcptr; ++destptr;\n }\n return (*srcptr - *destptr);\n}\n\n//compares up to the first n bytes of two strings lexicographically\nfloat strncmp(char *src, *dest, float n) {\n register char *srcptr, *destptr;\n register float i;\n\n srcptr = src;\n destptr = dest;\n i = n;\n\n while (i && (*srcptr == *destptr)) {\n if (*srcptr == 0) return 0;\n ++srcptr; ++destptr; --i;\n }\n if (i) return (*srcptr - *destptr);\n return 0;\n}\n\n//copies a string from one location to another\nchar* strcpy(char *dest, *src) {\n register char *srcptr, *destptr;\n\n destptr = dest;\n srcptr = src;\n while (*destptr++ = *srcptr++) ;\n return dest;\n}\n\n\n//write exactly n bytes to dest, copying from src or add 0's\nchar* strncpy(char *dest, *src, float n) {\n register char *srcptr, *destptr;\n register float i;\n\n destptr = dest;\n srcptr = src;\n i = n;\n\n while (i-- > 0) {\n if (*destptr++ = *srcptr++) continue;\n while (i-- > 0) *destptr++ = 0;\n }\n *destptr = 0;\n return dest;\n}\n\n//returns the string representation of an error number e.g. errno\n//char *strerror(int);\n\n//finds the length of a C string\nfloat strlen(char* str) {\n register char* strptr;\n register float n;\n\n strptr = str;\n n = 0;\n while (*strptr++) n++;\n return n;\n}\n\n//determines the length of the maximal initial substring consisting entirely of characters in accept\nfloat strspn(char *str, *accept) {\n register char *s = str;\n register char *p = accept;\n\n while (*p) {\n if (*p++ == *s) {\n ++s;\n p = accept;\n }\n }\n return s - str;\n}\n\n//determines the length of the maximal initial substring consisting entirely of characters not in reject\nfloat strcspn(char *str, char *reject) {\n register char *s, *p;\n\n for (s=str; *s; s++) {\n for (p=reject; *p; p++) {\n if (*p == *s) goto done;\n }\n }\n done:\n return s - str;\n}\n\n//finds the first occurrence of any character in accept\nchar* strpbrk(char *str, char *accept) {\n register char *s;\n register char *p;\n\n for (s=str; *s; s++) {\n for (p=accept; *p; p++) {\n if (*p == *s) return s;\n }\n }\n return NULL;\n}\n\n//finds the first occurrence of the string \"needle\" in the longer string \"haystack\"\nchar *strstr(char *haystack, char *needle) {\n register char *s = haystack;\n register char *p = needle;\n\n while (1) {\n if (!*p) {\n return haystack;\n }\n if (*p == *s) {\n ++p;\n ++s;\n } else {\n p = needle;\n if (!*s) {\n return NULL;\n }\n s = ++haystack;\n }\n }\n}\n\n//parses a string into a sequence of tokens; non-thread safe in the spec, non-reentrant\n//char *strtok(char *, const char * delim);\n\n//transforms src into a collating form, such that the numerical sort order of the transformed string is equivalent to the collating order of src\n//float strxfrm(char *dest, const char *src, float n);\n","cpuchip/lib/zcrt/init.txt":"//------------------------------------------------------------------------------\n// ZCPU CRT sourcecode (for HL-ZASM compiler) (C) 2011 by Black Phoenix\n//\n// C runtime library initialization\n//------------------------------------------------------------------------------\n\n#ifdef ZCRT_EXTENDED_MODE\n // Initialize extended mode\n mov edi,&zcrtInterruptTable; //Need \"&\" because array is defined below\n mov esi,&zcrtInterruptTable; add esi,1024;\n @InitTable:\n mov #edi,zcrtErrorHandler; inc edi;\n mov #edi,0; inc edi;\n mov #edi,0; inc edi;\n mov #edi,32; inc edi;\n cmp edi,esi;\n jl @InitTable;\n\n lidtr zcrtInterruptTable;\n stef;\n#endif\n\n// Call main function\nmain();\n\n// Stop the processor execution\n#ifdef ZCRT_EXTENDED_MODE\n clef;\n#endif\nint 1;\n\n//------------------------------------------------------------------------------\n// Allocate the interrupt table\n#ifdef ZCRT_EXTENDED_MODE\n float zcrtInterruptTable[1024];\n char* zcrtInterruptEntrypoint;\n\n // Default interrupt handlers\n zcrtErrorHandler:\n //Execute handler if required\n if (zcrtInterruptEntrypoint) {\n float errorNo,errorCode;\n cpuget errorNo,28;\n cpuget errorCode,27;\n\n zcrtInterruptEntrypoint(errorNo,errorCode);\n }\n iret\n#endif\n","cpuchip/lib/zcrt/ctype.txt":"//------------------------------------------------------------------------------\n// ZCPU CRT sourcecode (for HL-ZASM compiler) (C) 2011 by Black Phoenix\n//\n// Character classification functions.\n//------------------------------------------------------------------------------\n\n#define _CONTROL 1\n#define _SPACE 2\n#define _BLANK 4\n#define _DIGIT 8\n#define _HEX 16\n#define _PUNCT 32\n#define _UPPER 64\n#define _LOWER 128\n#define _GRAPH 256\n\n#define _MAXCHARS 0x83\n\n//test for alphanumeric character\nfloat isalnum(char c) {\n preserve eax;\n eax = c; max eax,0; min eax,_MAXCHARS;\n eax = __ctype_characters[eax];\n\n band eax,_LOWER+_UPPER+_DIGIT;\n}\n\n//test for alphabetic character\nfloat isalpha(char c) {\n preserve eax;\n eax = c; max eax,0; min eax,_MAXCHARS;\n eax = __ctype_characters[eax];\n\n band eax,_LOWER+_UPPER;\n}\n\n//test for blank character\nfloat isblank(char c) {\n preserve eax;\n eax = c; max eax,0; min eax,_MAXCHARS;\n eax = __ctype_characters[eax];\n\n band eax,_BLANK;\n}\n\n//test for control character\nfloat iscontrol(char c) {\n preserve eax;\n eax = c; max eax,0; min eax,_MAXCHARS;\n eax = __ctype_characters[eax];\n\n band eax,_CONTROL;\n}\n\n//test for digit\nfloat isdigit(char c) {\n preserve eax;\n eax = c; max eax,0; min eax,_MAXCHARS;\n eax = __ctype_characters[eax];\n\n band eax,_DIGIT;\n}\n\n//test for graphic character, excluding the space character\nfloat isgraph(char c) {\n preserve eax;\n eax = c; max eax,0; min eax,_MAXCHARS;\n eax = __ctype_characters[eax];\n\n band eax,_GRAPH;\n}\n\n//test for lowercase character\nfloat islower(char c) {\n preserve eax;\n eax = c; max eax,0; min eax,_MAXCHARS;\n eax = __ctype_characters[eax];\n\n band eax,_LOWER;\n}\n\n//test for printable character, including the space character.\nfloat isprint(char c) {\n preserve eax;\n eax = c; max eax,0; min eax,_MAXCHARS;\n eax = __ctype_characters[eax];\n\n band eax,_LOWER+_UPPER+_DIGIT+_PUNCT+_BLANK+_GRAPH;\n}\n\n//test for punctuation character\nfloat ispunct(char c) {\n preserve eax;\n eax = c; max eax,0; min eax,_MAXCHARS;\n eax = __ctype_characters[eax];\n\n band eax,_PUNCT;\n}\n\n//test for any whitespace character\nfloat isspace(char c) {\n preserve eax;\n eax = c; max eax,0; min eax,_MAXCHARS;\n eax = __ctype_characters[eax];\n\n band eax,_SPACE;\n}\n\n//test for uppercase character\nfloat isupper(char c) {\n preserve eax;\n eax = c; max eax,0; min eax,_MAXCHARS;\n eax = __ctype_characters[eax];\n\n band eax,_UPPER;\n}\n\n//test for hexadecimal digit. Not locale-specific.\nfloat isxdigit(char c) {\n preserve eax;\n eax = c; max eax,0; min eax,_MAXCHARS;\n eax = __ctype_characters[eax];\n\n band eax,_HEX;\n}\n\n//convert character to lowercase\nchar tolower(char c) {\n if (islower(c)) return c - 0x20;\n return c;\n}\n\n//convert character to uppercase\nchar toupper(char c) {\n if (isupper(c)) return c + 0x20;\n return c;\n}\n\n__ctype_characters:\n db _CONTROL, //00 (NUL)\n db _CONTROL; //01 (SOH)\n db _CONTROL; //02 (STX)\n db _CONTROL; //03 (ETX)\n db _CONTROL; //04 (EOT)\n db _CONTROL; //05 (ENQ)\n db _CONTROL; //06 (ACK)\n db _CONTROL; //07 (BEL)\n db _CONTROL; //08 (BS)\n db _SPACE+_CONTROL; //09 (HT)\n db _SPACE+_CONTROL; //0A (LF)\n db _SPACE+_CONTROL; //0B (VT)\n db _SPACE+_CONTROL; //0C (FF)\n db _SPACE+_CONTROL; //0D (CR)\n db _CONTROL; //0E (SI)\n db _CONTROL; //0F (SO)\n db _CONTROL; //10 (DLE)\n db _CONTROL; //11 (DC1)\n db _CONTROL; //12 (DC2)\n db _CONTROL; //13 (DC3)\n db _CONTROL; //14 (DC4)\n db _CONTROL; //15 (NAK)\n db _CONTROL; //16 (SYN)\n db _CONTROL; //17 (ETB)\n db _CONTROL; //18 (CAN)\n db _CONTROL; //19 (EM)\n db _CONTROL; //1A (SUB)\n db _CONTROL; //1B (ESC)\n db _CONTROL; //1C (FS)\n db _CONTROL; //1D (GS)\n db _CONTROL; //1E (RS)\n db _CONTROL; //1F (US)\n db _SPACE+_BLANK; //20 SPACE\n db _PUNCT; //21 !\n db _PUNCT; //22 \"\n db _PUNCT; //23 #\n db _PUNCT; //24 $\n db _PUNCT; //25 %\n db _PUNCT; //26 &\n db _PUNCT; //27 '\n db _PUNCT; //28 (\n db _PUNCT; //29 )\n db _PUNCT; //2A *\n db _PUNCT; //2B +\n db _PUNCT; //2C ;\n db _PUNCT; //2D -\n db _PUNCT; //2E .\n db _PUNCT; //2F /\n db _DIGIT+_HEX; //30 0\n db _DIGIT+_HEX; //31 1\n db _DIGIT+_HEX; //32 2\n db _DIGIT+_HEX; //33 3\n db _DIGIT+_HEX; //34 4\n db _DIGIT+_HEX; //35 5\n db _DIGIT+_HEX; //36 6\n db _DIGIT+_HEX; //37 7\n db _DIGIT+_HEX; //38 8\n db _DIGIT+_HEX; //39 9\n db _PUNCT; //3A :\n db _PUNCT; //3B ;\n db _PUNCT; //3C <\n db _PUNCT; //3D =\n db _PUNCT; //3E >\n db _PUNCT; //3F ?\n db _PUNCT; //40 @\n db _UPPER+_HEX; //41 A\n db _UPPER+_HEX; //42 B\n db _UPPER+_HEX; //43 C\n db _UPPER+_HEX; //44 D\n db _UPPER+_HEX; //45 E\n db _UPPER+_HEX; //46 F\n db _UPPER; //47 G\n db _UPPER; //48 H\n db _UPPER; //49 I\n db _UPPER; //4A J\n db _UPPER; //4B K\n db _UPPER; //4C L\n db _UPPER; //4D M\n db _UPPER; //4E N\n db _UPPER; //4F O\n db _UPPER; //50 P\n db _UPPER; //51 Q\n db _UPPER; //52 R\n db _UPPER; //53 S\n db _UPPER; //54 T\n db _UPPER; //55 U\n db _UPPER; //56 V\n db _UPPER; //57 W\n db _UPPER; //58 X\n db _UPPER; //59 Y\n db _UPPER; //5A Z\n db _PUNCT; //5B [\n db _PUNCT; //5C \\\n db _PUNCT; //5D ]\n db _PUNCT; //5E ^\n db _PUNCT; //5F _\n db _PUNCT; //60 `\n db _LOWER+_HEX; //61 a\n db _LOWER+_HEX; //62 b\n db _LOWER+_HEX; //63 c\n db _LOWER+_HEX; //64 d\n db _LOWER+_HEX; //65 e\n db _LOWER+_HEX; //66 f\n db _LOWER; //67 g\n db _LOWER; //68 h\n db _LOWER; //69 i\n db _LOWER; //6A j\n db _LOWER; //6B k\n db _LOWER; //6C l\n db _LOWER; //6D m\n db _LOWER; //6E n\n db _LOWER; //6F o\n db _LOWER; //70 p\n db _LOWER; //71 q\n db _LOWER; //72 r\n db _LOWER; //73 s\n db _LOWER; //74 t\n db _LOWER; //75 u\n db _LOWER; //76 v\n db _LOWER; //77 w\n db _LOWER; //78 x\n db _LOWER; //79 y\n db _LOWER; //7A z\n db _PUNCT; //7B {\n db _PUNCT; //7C |\n db _PUNCT; //7D }\n db _PUNCT; //7E ~\n db _CONTROL; //7F (DEL)\n\n db _GRAPH; //80\n db _GRAPH; //81\n db _GRAPH; //82\n db _GRAPH; //83\n"},"cpuchip/lib/drivers/":{"cpuchip/lib/drivers/drv_cscr.txt":"//------------------------------------------------------------------------------\n// ZCPU standard library and drivers set (C) 2011 by Black Phoenix\n//\n// UDH-enabled console screen highspeed driver\n//------------------------------------------------------------------------------\n\n//Define to check if console screen driver is available\n#define CSCR_DRIVER\n\n//Maximum number of console screens supported\n#define MAX_CONSOLE_SCREENS 8\n\n//Console screen registers\n#define CURSOR_RATE 2043\n#define CURSOR_SIZE 2044\n#define CURSOR_POSITION 2045\n#define CURSOR_VISIBLE 2046\n#define LOW_SHIFT_COL 2031\n#define HIGH_SHIFT_COL 2032\n#define LOW_SHIFT_ROW 2033\n#define HIGH_SHIFT_ROW 2034\n#define SHIFT_ROWS 2038\n#define SHIFT_CELLS 2037\n#define CLEAR_SCREEN 2041\n#define BACKGROUND_COLOR 2042\n#define SCREEN_ACTIVE 2047\n#define SCREEN_ROTATION 2024\n#define SCREEN_BRIGHTNESS 2036\n\n//Driver data\nchar* cscrOffsets[MAX_CONSOLE_SCREENS];\nfloat cscrDevices[MAX_CONSOLE_SCREENS];\nchar* cscrCharacterPointer[MAX_CONSOLE_SCREENS];\nfloat cscrSelectedScreen;\n\n#ifdef UDH_DRIVER\n//Update console screen offsets\nvoid cscrUDHQueryFunction() {\n float i,n;\n n = udhGetDevices(11,MAX_CONSOLE_SCREENS,cscrDevices);\n for (i = 0; i < n; i++) {\n cscrOffsets[i] = udhGetDeviceOffset(cscrDevices[i]);\n }\n}\n#endif\n\n//Initialize console screen driver. screenOffset may be 0 if using UDH\nvoid cscrInitialize(char* screenOffset) {\n float i;\n\n for (i = 0; i < MAX_CONSOLE_SCREENS; i++) {\n cscrOffsets[i] = screenOffset;\n }\n\n#ifdef UDH_DRIVER\n if (!screenOffset) {\n udhRegisterDriver(cscrUDHQueryFunction);\n cscrUDHQueryFunction();\n }\n#endif\n cscrSelectedScreen = 0;\n}\n\nfloat cscrPresent(float screen) {\n return cscrOffsets[cscrSelectedScreen] != 0;\n}\n\nvoid cscrSelect(float screen) {\n cscrSelectedScreen = screen;\n max cscrSelectedScreen,0;\n min cscrSelectedScreen,MAX_CONSOLE_SCREENS;\n}\n\nvoid cscrSetActive(float clk) {\n if (!cscrOffsets[cscrSelectedScreen]) return;\n *(cscrOffsets[cscrSelectedScreen]+SCREEN_ACTIVE) = clk;\n}\n\nvoid cscrClear() {\n if (!cscrOffsets[cscrSelectedScreen]) return;\n *(cscrOffsets[cscrSelectedScreen]+CLEAR_SCREEN) = 1;\n cscrCharacterPointer[cscrSelectedScreen] = 0;\n}\n\nvoid cscrSetBackground(float col) {\n if (!cscrOffsets[cscrSelectedScreen]) return;\n *(cscrOffsets[cscrSelectedScreen]+BACKGROUND_COLOR) = col;\n}\n\nvoid cscrSetRotation(float rot) {\n *(cscrOffsets[cscrSelectedScreen]+SCREEN_ROTATION) = rot;\n}\n\nvoid cscrSetBrightness(float bright) {\n *(cscrOffsets[cscrSelectedScreen]+SCREEN_BRIGHTNESS) = bright;\n}\n\nvoid cscrLoadImage(char* imgdata) {\n if (!cscrOffsets[cscrSelectedScreen]) return;\n\n preserve ESI,EDI;\n ESI = imgdata;\n EDI = cscrOffsets[cscrSelectedScreen];\n mcopy 30*18*2;\n}\n\nvoid cscrPutLine(char* scrptr, float col, char* str) {\n if (!cscrOffsets[cscrSelectedScreen]) return;\n char* curptr = scrptr;\n\n while (*str) {\n *(cscrOffsets[cscrSelectedScreen]+curptr*2+0) = *str;\n *(cscrOffsets[cscrSelectedScreen]+curptr*2+1) = col;\n\n str++;\n curptr++;\n }\n}\n\nvoid cscrPutChar(char* scrptr, float col, char ch) {\n if (!cscrOffsets[cscrSelectedScreen]) return;\n\n *(cscrOffsets[cscrSelectedScreen]+scrptr*2+0) = ch;\n *(cscrOffsets[cscrSelectedScreen]+scrptr*2+1) = col;\n}\n\nvoid cscrNewLine() {\n if (!cscrOffsets[cscrSelectedScreen]) return;\n\n cscrCharacterPointer[cscrSelectedScreen] /= 30;\n fint cscrCharacterPointer[cscrSelectedScreen];\n cscrCharacterPointer[cscrSelectedScreen] = (cscrCharacterPointer[cscrSelectedScreen]+1)*30;\n\n if (cscrCharacterPointer[cscrSelectedScreen] >= 30*18) {\n cscrCharacterPointer[cscrSelectedScreen] = cscrCharacterPointer[cscrSelectedScreen] - 30;\n *(cscrOffsets[cscrSelectedScreen]+SHIFT_ROWS) = 1;\n }\n}\n\nvoid cscrPrintLine(char* str, float col) {\n if (!cscrOffsets[cscrSelectedScreen]) return;\n\n while (*str) {\n if (*str == '\\n') {\n cscrNewLine();\n str++;\n if (*str == 0) return;\n }\n\n *(cscrOffsets[cscrSelectedScreen]+cscrCharacterPointer[cscrSelectedScreen]*2+0) = *str;\n *(cscrOffsets[cscrSelectedScreen]+cscrCharacterPointer[cscrSelectedScreen]*2+1) = col;\n\n cscrCharacterPointer[cscrSelectedScreen]++;\n if (cscrCharacterPointer[cscrSelectedScreen] >= 30*18) cscrNewLine();\n str++;\n }\n}\n\nvoid cscrPrintNumber(float num, float col) {\n if (!cscrOffsets[cscrSelectedScreen]) return;\n\n float ndig,a;\n a = num;\n ndig = 0;\n while (a > 0) {\n ndig++;\n a /= 10;\n fint a;\n }\n max ndig,1;\n a = num;\n\n cscrCharacterPointer[cscrSelectedScreen] = cscrCharacterPointer[cscrSelectedScreen] + ndig;\n char* charPtr = cscrCharacterPointer[cscrSelectedScreen] - 1;\n while (ndig > 0) {\n preserve EDX;\n mov EDX,a;\n mod EDX,10;\n add EDX,48;\n\n *(cscrOffsets[cscrSelectedScreen]+charPtr*2+0) = EDX;\n *(cscrOffsets[cscrSelectedScreen]+charPtr*2+1) = col;\n charPtr--;\n\n a /= 10;\n fint a;\n\n ndig--;\n }\n}\n\nvoid cscrSetCursor(float x, y) {\n if (!cscrOffsets[cscrSelectedScreen]) return;\n cscrCharacterPointer[cscrSelectedScreen] = x+y*30;\n}\n","cpuchip/lib/drivers/drv_udh.txt":"//------------------------------------------------------------------------------\n// ZCPU standard library and drivers set (C) 2011 by Black Phoenix\n//\n// Universal device host driver. Only supports 8 devices right now\n//------------------------------------------------------------------------------\n\n#define UDH_DRIVER\n\n//Maximum number of devices supported\n#define MAX_UDH_DEVICES 8\n\n//Address range of a single device\n#define MAX_UDH_ADDRESS_RANGE 4*1024\n\n//Maximum number of drivers that may register with UDH\n#define MAX_UDH_DRIVERS 8\n\n//Device name/string data\nstring udhDeviceString0,\"None\";\nstring udhDeviceString1,\"Unknown\";\nstring udhDeviceString2,\"Extended bus\";\nstring udhDeviceString3,\"Address bus\";\nstring udhDeviceString4,\"Zyelios CPU\";\nstring udhDeviceString5,\"Zyelios GPU\";\nstring udhDeviceString6,\"Zyelios SPU\";\nstring udhDeviceString7,\"Flash EEPROM\";\nstring udhDeviceString8,\"ROM\";\nstring udhDeviceString9,\"Data bus\";\nstring udhDeviceString10,\"CD Ray\";\nstring udhDeviceString11,\"Console screen\";\nstring udhDeviceString12,\"Digital screen\";\nstring udhDeviceString13,\"Data plug\";\nstring udhDeviceString14,\"Data socket\";\nstring udhDeviceString15,\"Keyboard\";\nstring udhDeviceString16,\"Oscilloscope\";\nstring udhDeviceString17,\"Sound emitter\";\nstring udhDeviceString18,\"Constant value\";\nstring udhDeviceString19,\"Data port\";\nstring udhDeviceString20,\"RAM\";\nudhDeviceName:\n db udhDeviceString0, udhDeviceString1, udhDeviceString2;\n db udhDeviceString3, udhDeviceString4, udhDeviceString5;\n db udhDeviceString6, udhDeviceString7, udhDeviceString8;\n db udhDeviceString9, udhDeviceString10,udhDeviceString11;\n db udhDeviceString12,udhDeviceString13,udhDeviceString14;\n db udhDeviceString15,udhDeviceString16,udhDeviceString17;\n db udhDeviceString18,udhDeviceString19,udhDeviceString20;\n\n//Extended bus offset\nchar* udhBusOffset;\n\n//List of callbacks to call when querying devices\nvoid* udhQueryCallback[MAX_UDH_DRIVERS];\nfloat udhQueryCallbackCount = 0;\n\nfloat udhSetBusAddress(char* extOffset) {\n udhBusOffset = extOffset;\n udhQueryDevices();\n}\n\nvoid udhQueryDevices() {\n float i;\n\n //Run the query\n udhBusOffset[16] = 32+MAX_UDH_DEVICES;\n udhBusOffset[17] = 1;\n\n //Reconfigure all devices\n //FIXME: only supports single extended bus right now\n for (i = 0; i < 8; i++) {\n udhBusOffset[i*2+0] = (4*1024)*i;\n udhBusOffset[i*2+1] = (4*1024)*i+((4*1024)-1);\n }\n\n //Update all drivers\n for (i = 0; i < udhQueryCallbackCount; i++) {\n void* functionPtr = udhQueryCallback[i];\n functionPtr();\n }\n}\n\nvoid udhRegisterDriver(void* queryDeviceFunction) {\n udhQueryCallback[udhQueryCallbackCount] = queryDeviceFunction;\n if (udhQueryCallbackCount < MAX_UDH_DRIVERS) udhQueryCallbackCount++;\n}\n\nfloat udhGetDeviceType(float busIndex) {\n return udhBusOffset[32+busIndex];\n}\n\nfloat udhGetDeviceOffset(float busIndex) {\n return 65536+32+MAX_UDH_DEVICES+udhBusOffset[busIndex*2];\n}\n\nchar* udhGetDeviceName(float busIndex) {\n float deviceType = udhGetDeviceType(busIndex);\n if ((deviceType >= 0) && (deviceType <= 20)) {\n return udhDeviceName[deviceType];\n } else {\n return udhDeviceName[1];\n }\n}\n\nvoid udhSetDeviceOffsetSize(float busIndex, char* offst, char* size) {\n udhBusOffset[busIndex*2+0] = offst;\n udhBusOffset[busIndex*2+1] = offst+size-1;\n}\n\nfloat udhGetNumDevices() {\n return MAX_UDH_DEVICES;\n}\n\nfloat udhGetDevices(float type, float maxCount, char* deviceList) {\n float i,devPtr,n;\n\n devPtr = deviceList;\n n = 0;\n for (i = 0; i < MAX_UDH_DEVICES; i++) {\n if ((udhGetDeviceType(i) == type) && (n < maxCount)) {\n n++;\n *devPtr++ = i;\n }\n }\n\n return n;\n}\n"}}}} \ No newline at end of file diff --git a/lua/wire/cpulib.lua b/lua/wire/cpulib.lua new file mode 100644 index 0000000000..0dce233ad5 --- /dev/null +++ b/lua/wire/cpulib.lua @@ -0,0 +1,1159 @@ +-------------------------------------------------------------------------------- +-- ZCPU utility & support code +-------------------------------------------------------------------------------- +local INVALID_BREAKPOINT_IP = 2e7 + +CPULib = CPULib or {} +if CLIENT then + -- Sourcecode available as compiled binary + CPULib.Source = "" + -- Compiled binary + CPULib.Buffer = {} + + -- Sourcecode currently being compiled + CPULib.CurrentSource = "" + -- Buffer currently being written + CPULib.CurrentBuffer = {} + + -- State variables + CPULib.Compiling = false + CPULib.Uploading = false + CPULib.ServerUploading = false + + -- Debugger + CPULib.DebuggerAttached = false + CPULib.Debugger = {} + CPULib.Debugger.Variables = {} + CPULib.Debugger.SourceTab = nil + + -- Reset on recompile + CPULib.Debugger.MemoryVariableByIndex = {} + CPULib.Debugger.MemoryVariableByName = {} + CPULib.Debugger.Labels = {} + CPULib.Debugger.PositionByPointer = {} + CPULib.Debugger.PointersByLine = {} + + CPULib.Debugger.Breakpoint = {} + + -- Convars to control CPULib + local wire_cpu_upload_speed = CreateClientConVar("wire_cpu_upload_speed",1000,false,false) + local wire_cpu_compile_speed = CreateClientConVar("wire_cpu_compile_speed",256,false,false) + local wire_cpu_show_all_registers = CreateClientConVar("wire_cpu_show_all_registers",0,false,false) + + ------------------------------------------------------------------------------ + -- Request compiling specific sourcecode + function CPULib.Compile(source,fileName,successCallback,errorCallback,targetPlatform) + -- Stop any compile/upload process that is running right now + timer.Remove("cpulib_compile") + timer.Remove("cpulib_upload") + CPULib.Uploading = false + + -- See if compiled source is available + --if CPULib.Source == source then + -- successCallback() + -- return + --end + + -- Remember the sourcecode being compiled + CPULib.CurrentSource = source + CPULib.CurrentBuffer = {} + + -- Clear debugging info + CPULib.Debugger.MemoryVariableByIndex = {} + CPULib.Debugger.MemoryVariableByName = {} + CPULib.Debugger.Labels = {} + CPULib.Debugger.PositionByPointer = {} + CPULib.Debugger.PointersByLine = {} + CPULib.CPUName = nil + + -- Start compiling the sourcecode + HCOMP:StartCompile(source,fileName or "source",CPULib.OnWriteByte,nil) + HCOMP.Settings.CurrentPlatform = targetPlatform or "CPU" + print("=== HL-ZASM High Level Assembly Compiler Output ==") + + -- Initialize callbacks + CPULib.SuccessCallback = successCallback + CPULib.ErrorCallback = errorCallback + + -- Run the timer + timer.Create("cpulib_compile",1/60,0,CPULib.OnCompileTimer) + CPULib.Compiling = true + end + + ------------------------------------------------------------------------------ + -- Make sure the file is opened in the tab + function CPULib.SelectTab(editor,fileName) + if not editor then return end + local editorType = string.lower(editor.EditorType) + local fullFileName = editorType.."chip\\"..fileName + + if string.sub(fileName,1,7) == editorType.."chip" then + fullFileName = fileName + end + + local sourceTab + for tab=1,editor:GetNumTabs() do + if editor:GetEditor(tab).chosenfile == fullFileName then + sourceTab = tab + end + end + + if not sourceTab then + editor:LoadFile(fullFileName,true) + sourceTab = editor:GetActiveTabIndex() + else + editor:SetActiveTab(sourceTab) + end + + return editor:GetEditor(sourceTab),sourceTab + end + + ------------------------------------------------------------------------------ + -- Request validating the code + function CPULib.Validate(editor,source,fileName) + CPULib.Compile(source,fileName, + function() + editor.C.Val:Update(nil, nil, " Success, "..(HCOMP.WritePointer or "?").." bytes compiled.", Color(50, 128, 20)) + end, + function(error,errorPos) + local issue = (error or "unknown error") + + local line, char = 0, 0 + + if errorPos then + line = errorPos.Line + char = errorPos.Col + + local textEditor = CPULib.SelectTab(editor,errorPos.File) + if not textEditor then return end + textEditor:SetCaret({errorPos.Line,errorPos.Col}) + end + + editor.C.Val:Update({{message = issue, line = line, char = char}}, nil, issue, Color(128, 20, 50)) + end,editor.EditorType) + end + + ------------------------------------------------------------------------------ + -- Compiler callback + function CPULib.OnWriteByte(caller,address,byte) + CPULib.CurrentBuffer[address] = byte + end + + ------------------------------------------------------------------------------ + -- Compiler timer + function CPULib.OnCompileTimer() + local compile_speed = wire_cpu_compile_speed:GetFloat() + + for _ = 1, compile_speed do + local status,result = pcall(HCOMP.Compile,HCOMP) + if not status then + print("==================================================") + if CPULib.ErrorCallback then CPULib.ErrorCallback(HCOMP.ErrorMessage or ("Internal error: "..result),HCOMP.ErrorPosition) end + timer.Remove("cpulib_compile") + CPULib.Compiling = false + + return + elseif not result then + print("==================================================") + CPULib.Source = CPULib.CurrentSource + CPULib.Buffer = CPULib.CurrentBuffer + + if CPULib.SuccessCallback then + CPULib.Debugger.Labels = HCOMP.DebugInfo.Labels + CPULib.Debugger.PositionByPointer = HCOMP.DebugInfo.PositionByPointer + CPULib.Debugger.PointersByLine = HCOMP.DebugInfo.PointersByLine + + CPULib.SuccessCallback() + end + timer.Remove("cpulib_compile") + CPULib.Compiling = false + + return + end + end + end + + + ------------------------------------------------------------------------------ + -- Uploader timer + function CPULib.OnUploadTimer() + if not CPULib.RemainingData then return end + + local upload_speed = wire_cpu_upload_speed:GetFloat() -- Number of index/value pairs to send (11 bytes each) + if game.SinglePlayer() then upload_speed = 5000 end + + local iters = math.min(upload_speed, CPULib.RemainingUploadData) + net.Start("wire_cpulib_buffer") + net.WriteUInt(iters, 16) + for _ = 1, iters do + local index,value = next(CPULib.RemainingData) + CPULib.RemainingUploadData = CPULib.RemainingUploadData - 1 + net.WriteUInt(index, 24) + net.WriteDouble(value or 0) -- 64bits, in case theres any float literals. Int21 is sufficient for all function calls/memory addresses + CPULib.RemainingData[index] = nil + end + if CPULib.RemainingUploadData <= 0 then + timer.Remove("cpulib_upload") + net.WriteBit(true) -- End + CPULib.Uploading = false + else + net.WriteBit(false) -- Keep going + end + net.SendToServer() + end + + ------------------------------------------------------------------------------ + -- Start upload + function CPULib.Upload(customBuffer) + -- Stop any upload in the progress + timer.Remove("cpulib_upload") + + -- Send the buffer over to server + net.Start("wire_cpulib_bufferstart") net.WriteString(CPULib.CPUName or "") net.SendToServer() + + CPULib.TotalUploadData = 0 + CPULib.RemainingData = {} + if customBuffer then + for k,v in pairs(customBuffer) do + CPULib.RemainingData[k] = v + CPULib.TotalUploadData = CPULib.TotalUploadData + 1 + end + else + for k,v in pairs(CPULib.Buffer) do + CPULib.RemainingData[k] = v + CPULib.TotalUploadData = CPULib.TotalUploadData + 1 + end + end + + CPULib.RemainingUploadData = CPULib.TotalUploadData + timer.Create("cpulib_upload",0.5,0,CPULib.OnUploadTimer) + CPULib.Uploading = true + end + + ------------------------------------------------------------------------------ + -- Get debug text for specific variable/function name + function CPULib.GetDebugPopupText(var) + if not var then return "" end + local csvar = string.upper(var) + + if CPULib.Debugger.Variables[csvar] then + return var.." = "..CPULib.Debugger.Variables[csvar] + else + if CPULib.Debugger.Labels[csvar] then + if CPULib.Debugger.Labels[csvar].Offset then + if not CPULib.Debugger.MemoryVariableByName[csvar] then + CPULib.Debugger.MemoryVariableByName[csvar] = CPULib.Debugger.Labels[csvar].Offset + table.insert(CPULib.Debugger.MemoryVariableByIndex,csvar) + RunConsoleCommand("wire_cpulib_debugvar",#CPULib.Debugger.MemoryVariableByIndex,CPULib.Debugger.Labels[csvar].Offset) + end + return var.." = ..." + elseif CPULib.Debugger.Labels[csvar].Pointer then + return var.." = ptr "..CPULib.Debugger.Labels[csvar].Pointer + else + return var.." = cannot resolve" + end + end + end + end + + ------------------------------------------------------------------------------ + -- Get debug text for specific variable/function name + CPULib.InterruptText = nil + function CPULib.GetDebugWindowText() + local result = { + "EAX = "..(CPULib.Debugger.Variables.EAX or "#####"), + "EBX = "..(CPULib.Debugger.Variables.EBX or "#####"), + "ECX = "..(CPULib.Debugger.Variables.ECX or "#####"), + "EDX = "..(CPULib.Debugger.Variables.EDX or "#####"), + "ESI = "..(CPULib.Debugger.Variables.ESI or "#####"), + "EDI = "..(CPULib.Debugger.Variables.EDI or "#####"), + "EBP = "..(CPULib.Debugger.Variables.EBP or "#####"), + "ESP = "..(CPULib.Debugger.Variables.ESP or "#####"), + } + + table.insert(result,"") + local maxReg = 7 + if wire_cpu_show_all_registers:GetFloat() == 1 then maxReg = 31 end + + for reg=0,maxReg do + table.insert(result,"R"..reg.." = "..(CPULib.Debugger.Variables["R"..reg] or "#####")) + end + + + table.insert(result,"") + if CPULib.Debugger.Variables.IP == INVALID_BREAKPOINT_IP then + table.insert(result,"IP = #####") + else + table.insert(result,"IP = "..(CPULib.Debugger.Variables.IP or "#####")) + end + + if CPULib.InterruptText then + table.insert(result,"") + table.insert(result,CPULib.InterruptText) + end + + return result + end + + ------------------------------------------------------------------------------ + -- Invalidate debugger data + function CPULib.InvalidateDebugger() + CPULib.InterruptText = nil + CPULib.Debugger.MemoryVariableByIndex = {} + CPULib.Debugger.MemoryVariableByName = {} + CPULib.Debugger.Breakpoint = {} + CPULib.Debugger.Variables = {} + CPULib.Debugger.FirstFile = nil + CPULib.DebugUpdateHighlights() + end + + net.Receive("CPULib.InvalidateDebugger", function(netlen) + local state = net.ReadUInt(2) -- 0: No change just invalidate, 1: detach, 2: attach + if state == 1 then + CPULib.DebuggerAttached = false + GAMEMODE:AddNotify("CPU debugger detached!",NOTIFY_GENERIC,7) + elseif state == 2 then + CPULib.DebuggerAttached = true + GAMEMODE:AddNotify("CPU debugger has been attached!",NOTIFY_GENERIC,7) + end + CPULib.InvalidateDebugger() + end) + + -- Get breakpoint at line + function CPULib.GetDebugBreakpoint(fileName,caretPos) + if not fileName or not caretPos then return nil end + return CPULib.Debugger.Breakpoint[caretPos[1]..":"..fileName] + end + + -- Set breakpoint at line + -- FIXME: bug: can only set breakpoints in one file + function CPULib.SetDebugBreakpoint(fileName,caretPos,condition) + if not fileName or not caretPos then return nil end + if not condition then + CPULib.Debugger.Breakpoint[caretPos[1]..":"..fileName] = nil + if CPULib.Debugger.PointersByLine[caretPos[1]..":"..fileName] then + RunConsoleCommand("wire_cpulib_debugbreakpoint",CPULib.Debugger.PointersByLine[caretPos[1]..":"..fileName][1],0) + end + else + if CPULib.Debugger.PointersByLine[caretPos[1]..":"..fileName] then + CPULib.Debugger.Breakpoint[caretPos[1]..":"..fileName] = condition + RunConsoleCommand("wire_cpulib_debugbreakpoint",CPULib.Debugger.PointersByLine[caretPos[1]..":"..fileName][1],condition) + end + end + + CPULib.DebugUpdateHighlights(true) + end + + -- Update highlighted lines + function CPULib.DebugUpdateHighlights(dontForcePosition) + if ZCPU_Editor then + -- Highlight current position + local currentPosition = CPULib.Debugger.PositionByPointer[CPULib.Debugger.Variables.IP] + + if currentPosition then + -- Clear all highlighted lines + for tab=1,ZCPU_Editor:GetNumTabs() do + ZCPU_Editor:GetEditor(tab):ClearHighlightedLines() + end + + local textEditor = CPULib.SelectTab(ZCPU_Editor,currentPosition.File) + if textEditor then + textEditor:HighlightLine(currentPosition.Line,130,0,0,255) + if not dontForcePosition then + textEditor:SetCaret({currentPosition.Line,1}) --currentPosition.Col + end + end + end + + -- Highlight breakpoints + for key,breakpoint in pairs(CPULib.Debugger.Breakpoint) do + local line = tonumber(string.sub(key,1,(string.find(key,":") or 0) - 1)) or 0 + local file = string.sub(key, (string.find(key,":") or 0) + 1) + + local textEditor = CPULib.SelectTab(ZCPU_Editor,file) + if textEditor then + if currentPosition and (currentPosition.Line == line) then + if breakpoint == true then + textEditor:HighlightLine(line,130,70,20,255) + else + textEditor:HighlightLine(line,130,20,70,255) + end + else + if breakpoint == true then + textEditor:HighlightLine(line,0,70,20,255) + else + textEditor:HighlightLine(line,0,20,70,255) + end + end + end + end + end + end + + ------------------------------------------------------------------------------ + -- Debug data arrived from server + function CPULib.OnDebugData_Registers(um) + CPULib.Debugger.Variables.IP = um:ReadFloat() + CPULib.Debugger.Variables.EAX = um:ReadFloat() + CPULib.Debugger.Variables.EBX = um:ReadFloat() + CPULib.Debugger.Variables.ECX = um:ReadFloat() + CPULib.Debugger.Variables.EDX = um:ReadFloat() + CPULib.Debugger.Variables.ESI = um:ReadFloat() + CPULib.Debugger.Variables.EDI = um:ReadFloat() + CPULib.Debugger.Variables.EBP = um:ReadFloat() + CPULib.Debugger.Variables.ESP = um:ReadFloat() + + for reg=0,31 do + CPULib.Debugger.Variables["R"..reg] = um:ReadFloat() + end + + CPULib.DebugUpdateHighlights() + end + usermessage.Hook("cpulib_debugdata_registers", CPULib.OnDebugData_Registers) + + function CPULib.OnDebugData_Variables(um) + local startIndex = um:ReadShort() + for varIdx = startIndex,startIndex+59 do + if CPULib.Debugger.MemoryVariableByIndex[varIdx] then + CPULib.Debugger.Variables[CPULib.Debugger.MemoryVariableByIndex[varIdx]] = um:ReadFloat() + end + end + end + usermessage.Hook("cpulib_debugdata_variables", CPULib.OnDebugData_Variables) + + function CPULib.OnDebugData_Interrupt(um) + local interruptNo,interruptParameter = um:ReadFloat(),um:ReadFloat() + CPULib.InterruptText = "Error #"..interruptNo.. " ["..interruptParameter.."]" + end + usermessage.Hook("cpulib_debugdata_interrupt", CPULib.OnDebugData_Interrupt) + + ------------------------------------------------------------------------------ + -- Show ZCPU/ZGPU documentation + CPULib.HandbookWindow = nil + + function CPULib.ShowDocumentation(platform) + local w = ScrW() * 2/3 + local h = ScrH() * 2/3 + local browserWindow = vgui.Create("DFrame") + browserWindow:SetTitle("Documentation") + browserWindow:SetPos((ScrW() - w)/2, (ScrH() - h)/2) + browserWindow:SetSize(w,h) + browserWindow:MakePopup() + + local browser = vgui.Create("DHTML",browserWindow) + browser:SetPos(10, 25) + browser:SetSize(w - 20, h - 35) + + browser:OpenURL("http://wiki.wiremod.com/wiki/Category:ZCPU_Handbook") + end +end + + + + + + + +if SERVER then + util.AddNetworkString("CPULib.ServerUploading") + ------------------------------------------------------------------------------ + -- Data received from server + CPULib.DataBuffer = {} + + ------------------------------------------------------------------------------ + -- Set this entity as a receiver for networked upload + function CPULib.SetUploadTarget(entity,player) + CPULib.DataBuffer[player:UserID()] = { + Entity = entity, + Player = player, + Data = {}, + } + end + + util.AddNetworkString("wire_cpulib_bufferstart") + net.Receive("wire_cpulib_bufferstart", function(netlen, player) + local Buffer = CPULib.DataBuffer[player:UserID()] + if (not Buffer) or (Buffer.Player ~= player) then return end + if not IsValid(Buffer.Entity) then return end + + net.Start("CPULib.ServerUploading") net.WriteBit(true) net.Send(player) + if Buffer.Entity:GetClass() == "gmod_wire_cpu" then + Buffer.Entity:SetCPUName(net.ReadString()) + end + end) + + -- Concommand to send a single stream of bytes + util.AddNetworkString("wire_cpulib_buffer") + net.Receive("wire_cpulib_buffer", function(netlen, player) + local Buffer = CPULib.DataBuffer[player:UserID()] + if (not Buffer) or (Buffer.Player ~= player) then return end + if not IsValid(Buffer.Entity) then return end + + for _ = 1, net.ReadUInt(16) do + Buffer.Data[net.ReadUInt(24)] = net.ReadDouble() + end + + if net.ReadBit() ~= 0 then -- We're done! + CPULib.DataBuffer[player:UserID()] = nil + net.Start("CPULib.ServerUploading") net.WriteBit(false) net.Send(player) + + if Buffer.Entity:GetClass() == "gmod_wire_cpu" then + Buffer.Entity:FlashData(Buffer.Data) + elseif Buffer.Entity:GetClass() == "gmod_wire_dhdd" then + for k,v in pairs(Buffer.Data) do + Buffer.Entity.Memory[k] = v + end + Buffer.Entity:ShowOutputs() + elseif (Buffer.Entity:GetClass() == "gmod_wire_gpu") or (Buffer.Entity:GetClass() == "gmod_wire_spu") then + Buffer.Entity:WriteCell(65535,0) + if Buffer.Entity.WriteCell then + for k,v in pairs(Buffer.Data) do + Buffer.Entity:WriteCell(k,v) + end + end + Buffer.Entity:WriteCell(65535,Buffer.Entity.Clk) + Buffer.Entity:WriteCell(65534,1) + else + if Buffer.Entity.WriteCell then + for k,v in pairs(Buffer.Data) do + Buffer.Entity:WriteCell(k,v) + end + end + end + end + end) + + ------------------------------------------------------------------------------ + -- Players and corresponding entities (for the debugger) + CPULib.DebuggerData = {} + + ------------------------------------------------------------------------------ + -- Attach a debugger + function CPULib.AttachDebugger(entity,player) + if entity then + entity.BreakpointInstructions = {} + entity.OnBreakpointInstruction = function(IP) + CPULib.SendDebugData(entity.VM,CPULib.DebuggerData[player:UserID()].MemPointers,player) + end + entity.OnVMStep = function() + if CurTime() - CPULib.DebuggerData[player:UserID()].PreviousUpdateTime > 0.2 then + CPULib.DebuggerData[player:UserID()].PreviousUpdateTime = CurTime() + + -- Send a fake update that messes up line pointer, updates registers + local tempIP = entity.VM.IP + entity.VM.IP = INVALID_BREAKPOINT_IP + CPULib.SendDebugData(entity.VM,nil,player) + entity.VM.IP = tempIP + end + end + if not entity.VM.BaseJump then + entity.VM.BaseJump = entity.VM.Jump + entity.VM.Jump = function(VM,IP,CS) + VM:BaseJump(IP,CS) + entity.ForceLastInstruction = true + end + entity.VM.BaseInterrupt = entity.VM.Interrupt + entity.VM.Interrupt = function(VM,interruptNo,interruptParameter,isExternal,cascadeInterrupt) + VM:BaseInterrupt(interruptNo,interruptParameter,isExternal,cascadeInterrupt) + if interruptNo < 27 then + CPULib.DebugLogInterrupt(player,interruptNo,interruptParameter,isExternal,cascadeInterrupt) + CPULib.SendDebugData(entity.VM,CPULib.DebuggerData[player:UserID()].MemPointers,player) + end + end + end + else + if CPULib.DebuggerData[player:UserID()] then + if CPULib.DebuggerData[player:UserID()].Entity and + CPULib.DebuggerData[player:UserID()].Entity.VM and + CPULib.DebuggerData[player:UserID()].Entity.VM.BaseInterrupt then + + CPULib.DebuggerData[player:UserID()].Entity.BreakpointInstructions = nil + if CPULib.DebuggerData[player:UserID()].Entity.VM.BaseJump then + CPULib.DebuggerData[player:UserID()].Entity.VM.Jump = CPULib.DebuggerData[player:UserID()].Entity.VM.BaseJump + CPULib.DebuggerData[player:UserID()].Entity.VM.Interrupt = CPULib.DebuggerData[player:UserID()].Entity.VM.BaseInterrupt + CPULib.DebuggerData[player:UserID()].Entity.VM.BaseJump = nil + CPULib.DebuggerData[player:UserID()].Entity.VM.BaseInterrupt = nil + end + end + end + end + + CPULib.DebuggerData[player:UserID()] = { + Entity = entity, + Player = player, + MemPointers = {}, + PreviousUpdateTime = CurTime(), + } + end + + -- Log debug interrupt + function CPULib.DebugLogInterrupt(player,interruptNo,interruptParameter,isExternal,cascadeInterrupt) + local umsgrp = RecipientFilter() + umsgrp:AddPlayer(player) + + umsg.Start("cpulib_debugdata_interrupt", umsgrp) + umsg.Float(interruptNo) + umsg.Float(interruptParameter) + umsg.End() + end + + -- Send debug log entry to client + function CPULib.SendDebugLogEntry(player,text) +-- + end + + -- Send debugging data to client + function CPULib.SendDebugData(VM,MemPointers,Player,onlyMemPointers) + local umsgrp = RecipientFilter() + umsgrp:AddPlayer(Player) + + if not onlyMemPointers then + umsg.Start("cpulib_debugdata_registers", umsgrp) + umsg.Float(VM.IP) + umsg.Float(VM.EAX) + umsg.Float(VM.EBX) + umsg.Float(VM.ECX) + umsg.Float(VM.EDX) + umsg.Float(VM.ESI) + umsg.Float(VM.EDI) + umsg.Float(VM.EBP) + umsg.Float(VM.ESP) + + for reg = 0,31 do + umsg.Float(VM["R"..reg]) + end + umsg.End() + end + + if MemPointers then + for msgIdx=0,math.floor(#MemPointers/60) do + umsg.Start("cpulib_debugdata_variables", umsgrp) + umsg.Short(msgIdx*60) + for varIdx=msgIdx*60,msgIdx*60+59 do + if MemPointers[varIdx] then + umsg.Float(VM:ReadCell(MemPointers[varIdx])) + end + end + umsg.End() + end + end + end + + -- Concommand to step forward + concommand.Add("wire_cpulib_debugstep", function(player, command, args) + local Data = CPULib.DebuggerData[player:UserID()] + if (not Data) or (Data.Player ~= player) then return end + if not IsValid(Data.Entity) then return end + + if not args[1] then -- Step forward + Data.Entity.VM:Step(1) + Data.Entity.VMStopped = true + else -- Run until instruction + Data.Entity.VMStopped = false + Data.Entity:NextThink(CurTime()) + + Data.Entity.LastInstruction = tonumber(args[1]) or 0 + Data.Entity.OnLastInstruction = function() + Data.Entity.LastInstruction = nil + Data.Entity.OnLastInstruction = nil + CPULib.SendDebugData(Data.Entity.VM,Data.MemPointers,Data.Player) + end + end + CPULib.SendDebugData(Data.Entity.VM,Data.MemPointers,Data.Player) + end) + + -- Concommand to run till breakpoint + concommand.Add("wire_cpulib_debugrun", function(player, command, args) + local Data = CPULib.DebuggerData[player:UserID()] + if (not Data) or (Data.Player ~= player) then return end + if not IsValid(Data.Entity) then return end + + -- Send a fake update that messes up line pointer + local tempIP = Data.Entity.VM.IP + Data.Entity.VM.IP = INVALID_BREAKPOINT_IP + CPULib.SendDebugData(Data.Entity.VM,nil,Data.Player) + Data.Entity.VM.IP = tempIP + + Data.Entity.Clk = true + Data.Entity:NextThink(CurTime()) + end) + + -- Concommand to reset + concommand.Add("wire_cpulib_debugreset", function(player, command, args) + local Data = CPULib.DebuggerData[player:UserID()] + if (not Data) or (Data.Player ~= player) then return end + if not IsValid(Data.Entity) then return end + + Data.Entity.VM:Reset() + CPULib.SendDebugData(Data.Entity.VM,Data.MemPointers,Data.Player) + end) + + -- Concommand to add a variable + concommand.Add("wire_cpulib_debugvar", function(player, command, args) + local Data = CPULib.DebuggerData[player:UserID()] + if (not Data) or (Data.Player ~= player) then return end + if not IsValid(Data.Entity) then return end + + Data.MemPointers[tonumber(args[1]) or 0] = tonumber(args[2]) + CPULib.SendDebugData(Data.Entity.VM,Data.MemPointers,Data.Player,true) + end) + + -- Concommand to set a debug breakpoint + concommand.Add("wire_cpulib_debugbreakpoint", function(player, command, args) + local Data = CPULib.DebuggerData[player:UserID()] + if (not Data) or (Data.Player ~= player) then return end + if not IsValid(Data.Entity) then return end + + if tonumber(args[2]) == 0 then + Data.Entity.BreakpointInstructions[tonumber(args[1]) or 0] = nil + else + Data.Entity.BreakpointInstructions[tonumber(args[1]) or 0] = true + end + end) +end + + +-------------------------------------------------------------------------------- +-- Create a new virtual machine +-------------------------------------------------------------------------------- +function CPULib.VirtualMachine() + -- Create new instance of the VM + include("wire/zvm/zvm_core.lua") + + -- Remove from global scope + local newVM = ZVM + ZVM = nil + return newVM +end + + +-------------------------------------------------------------------------------- +-- Generate a serial number +-------------------------------------------------------------------------------- +local sessionBase, sessionDate +function CPULib.GenerateSN(entityType) + local currentDate = os.date("*t") + + local SNDate = (currentDate.year-2007)*500+(currentDate.yday) + if (not sessionBase) or (SNDate ~= sessionDate) then + sessionBase = math.floor(math.random()*99999) + sessionDate = SNDate + else + sessionBase = sessionBase + 1 + end + + if entityType == "CPU" then + return sessionBase + 100000 + SNDate*1000000 + elseif entityType == "SPU" then + return sessionBase + 200000 + SNDate*1000000 + elseif entityType == "GPU" then + return sessionBase + 300000 + SNDate*1000000 + elseif entityType == "UNK" then + return sessionBase + 700000 + SNDate*1000000 + end +end + +-------------------------------------------------------------------------------- +-- Columns in the instruction set reference table: +-- Opc - Instruction number +-- Mnemonic - Symbolic mnemonic (uppercase). Can be "RESERVED" +-- Ops - Number of operands +-- Version - Minimum CPU version required +-- Flags - Several or none of the following flags: +-- W1: single-operand opcode which writes 1st operand +-- R0: runlevel 0 opcode (privileged opcode) +-- OB: obsolete/should not be used +-- UB: unconditional branching instruction +-- CB: conditional branching instruction +-- TR: trigonometric syntax operand +-- OL: old mnemonic for the instruction +-- BL: instruction supports block prefix +-- Op1 - operand 1 name +-- Op2 - operand 2 name + +-- Possible operand names: +-- X,Y: arbitrary integer or floating-point value +-- PTR: 48-bit pointer into memory +-- CS: 48-bit pointer into memory, new value of CS segment +-- IDX: unsigned integer index into internal processor table +-- PAGE: unsigned integer page number (each page is 128 bytes) +-- PORT: unsigned 47-bit integer port number +-- BIT: integer between 0 and 47 +-- INTR: interrupt nubmer (integer between 0 and 255) +-- SIZE: unsigned 47-bit integer memory block size +-- +-- COLOR: 4-byte color +-- VEC2F: 2-byte vector +-- +-- INT: 48-bit signed integer + +CPULib.InstructionTable = {} +local W1,R0,OB,UB,CB,TR,OL,BL = 1,2,4,8,16,32,64,128 + +local function Bit(x,n) return (math.floor(x / n) % 2) == 1 end +local function Entry(Set,Opc,Mnemonic,Ops,Version,Flags,Op1,Op2,Reference) + table.insert(CPULib.InstructionTable, + { Set = Set, + Opcode = Opc, + Mnemonic = Mnemonic, + OperandCount = Ops, + MinimumVersion = Version, + Operand1 = Op1, + Operand2 = Op2, + Reference = Reference, + + WritesFirstOperand = Bit(Flags,W1), + Privileged = Bit(Flags,R0), + Obsolete = Bit(Flags,OB), + UnconditionalBranching = Bit(Flags,UB), + ConditionalBranching = Bit(Flags,CB), + Trigonometric = Bit(Flags,TR), + Old = Bit(Flags,OL), + BlockPrefix = Bit(Flags,BL), + }) +end +local function CPU(...) Entry("CPU",...) end +local function GPU(...) Entry("GPU",...) end +local function VEX(...) Entry("VEX",...) end +local function SPU(...) Entry("SPU",...) end + + +------------------------------------------------------------------------------------------------------------------------------------------------- +-- Zyelios CPU/GPU/SPU instruction set reference table +------------------------------------------------------------------------------------------------------------------------------------------------- +--- Opc Mnemonic ------- Ops Version Flags ---- Op1 ---- Op2 ---- Reference ------------------------------------------------------------------ +CPU(000, "RESERVED", 0, 0.00, 0, "", "", "Stop processor execution") +CPU(001, "JNE", 1, 1.00, CB, "PTR", "", "Jump to PTR if result is not equal") +CPU(001, "JNZ", 1, 1.00, CB, "PTR", "", "Jump to PTR if result is not zero") +CPU(002, "JMP", 1, 1.00, UB, "PTR", "", "Jump to PTR") +CPU(003, "JG", 1, 1.00, CB, "PTR", "", "Jump to PTR if result is greater") +CPU(003, "JNLE", 1, 1.00, CB, "PTR", "", "Jump to PTR if result is not less or equal") +CPU(004, "JGE", 1, 1.00, CB, "PTR", "", "Jump to PTR if result is greater or equal") +CPU(004, "JNL", 1, 1.00, CB, "PTR", "", "Jump to PTR if result is not less") +CPU(005, "JL", 1, 1.00, CB, "PTR", "", "Jump to PTR if result is less") +CPU(005, "JNGE", 1, 1.00, CB, "PTR", "", "Jump to PTR if result is not greater or equal") +CPU(006, "JLE", 1, 1.00, CB, "PTR", "", "Jump to PTR if result is less or equal") +CPU(006, "JNG", 1, 1.00, CB, "PTR", "", "Jump to PTR if result is not greater") +CPU(007, "JE", 1, 1.00, CB, "PTR", "", "Jump to PTR if result is equal") +CPU(007, "JZ", 1, 1.00, CB, "PTR", "", "Jump to PTR if result is zero") +CPU(008, "CPUID", 1, 1.00, 0, "IDX", "", "Write processor information variable IDX into EAX register") +CPU(009, "PUSH", 1, 1.00, 0, "X", "", "Push X onto processor stack") +---- Dec 1 -------------------------------------------------------------------------------------------------------------------------------------- +CPU(010, "ADD", 2, 1.00, 0, "X", "Y", "X = X + Y") +CPU(011, "SUB", 2, 1.00, 0, "X", "Y", "X = X - Y") +CPU(012, "MUL", 2, 1.00, 0, "X", "Y", "X = X * Y") +CPU(013, "DIV", 2, 1.00, 0, "X", "Y", "X = X / Y") +CPU(014, "MOV", 2, 1.00, 0, "X", "Y", "X = Y") +CPU(015, "CMP", 2, 1.00, 0, "X", "Y", "Compare X and Y. Use with conditional branching instructions") +CPU(016, "RD", 2, 1.00, R0+OB, "X", "PTR", "Read value from memory by pointer PTR") +CPU(017, "WD", 2, 1.00, R0+OB, "PTR", "Y", "Write value to memory by pointer PTR") +CPU(018, "MIN", 2, 1.00, 0, "X", "Y", "Set X to smaller value out of X and Y") +CPU(019, "MAX", 2, 1.00, 0, "X", "Y", "Set X to bigger value out of X and Y") +---- Dec 2 -------------------------------------------------------------------------------------------------------------------------------------- +CPU(020, "INC", 1, 1.00, W1, "X", "", "Increase X by one") +CPU(021, "DEC", 1, 1.00, W1, "X", "", "Decrease X by one") +CPU(022, "NEG", 1, 1.00, W1, "X", "", "Change sign of X") +CPU(023, "RAND", 1, 1.00, W1, "X", "", "Set X to random value") +CPU(024, "LOOP", 1, 2.00, CB, "PTR", "", "Decrease ECX, and if ECX is not set to 0, jump to PTR") +CPU(024, "LOOPC", 1, 10.00, CB, "PTR", "", "Decrease ECX, and if ECX is not set to 0, jump to PTR") +CPU(025, "LOOPA", 1, 2.00, CB, "PTR", "", "Decrease EAX, and if EAX is not set to 0, jump to PTR") +CPU(026, "LOOPB", 1, 2.00, CB, "PTR", "", "Decrease EBX, and if EBX is not set to 0, jump to PTR") +CPU(027, "LOOPD", 1, 2.00, CB, "PTR", "", "Decrease EDX, and if EDX is not set to 0, jump to PTR") +CPU(028, "SPG", 1, 2.00, R0, "PAGE", "", "Make PAGE readonly") +CPU(029, "CPG", 1, 2.00, R0, "PAGE", "", "Make PAGE readable and writeable") +---- Dec 3--------------------------------------------------------------------------------------------------------------------------------------- +CPU(030, "POP", 1, 1.00, 0, "X", "", "Pop value off stack and write it into X") +CPU(031, "CALL", 1, 1.00, UB, "PTR", "", "Call subroutine by address PTR") +CPU(032, "BNOT", 1, 1.00, W1, "INT", "", "Flip all bits in the integer number") +CPU(033, "FINT", 1, 1.00, W1, "X", "", "Force X to be an integer value") +CPU(034, "FRND", 1, 1.00, W1, "X", "", "Round X to the nearest integer value") +CPU(034, "RND", 1, 1.00, W1+OL, "X", "", "FRND") +CPU(035, "FFRAC", 1, 1.00, W1, "X", "", "Remove integer part of the X, leaving only the fractional part") +CPU(036, "FINV", 1, 1.00, W1, "X", "", "X = 1 / X") +CPU(037, "HALT", 1, 1.00, OB, "PORT", "", "Halt processor execution until PORT is written to") +CPU(038, "FSHL", 1, 2.00, W1, "X", "", "Multiply X by 2 (does not floor)") +CPU(039, "FSHR", 1, 2.00, W1, "X", "", "Divide X by 2 (does not floor)") +---- Dec 4 -------------------------------------------------------------------------------------------------------------------------------------- +CPU(040, "RET", 0, 1.00, UB, "", "", "Return from a subroutine") +CPU(041, "IRET", 0, 2.00, UB, "", "", "Return from an interrupt") +CPU(042, "STI", 0, 2.00, R0, "", "", "Enable interrupt handling") +CPU(043, "CLI", 0, 2.00, R0, "", "", "Disable interrupt handling") +CPU(044, "STP", 0, 2.00, R0+OB, "", "", "Enable protected mode") +CPU(045, "CLP", 0, 2.00, R0+OB, "", "", "Disable protected mode") +CPU(046, "RESERVED", 0, 0.00, R0, "", "", "") +CPU(047, "RETF", 0, 1.00, UB, "", "", "Return from a far subroutine call") +CPU(048, "STEF", 0, 4.00, R0, "", "", "Enable extended mode") +CPU(049, "CLEF", 0, 4.00, R0, "", "", "Disable extended mode") +---- Dec 5 -------------------------------------------------------------------------------------------------------------------------------------- +CPU(050, "AND", 2, 1.00, 0, "X", "Y", "Logical AND between X and Y") +CPU(051, "OR", 2, 1.00, 0, "X", "Y", "Logical OR between X and Y") +CPU(052, "XOR", 2, 1.00, 0, "X", "Y", "Logical XOR between X and Y") +CPU(053, "FSIN", 2, 1.00, TR, "X", "Y", "Write sine of X to Y") +CPU(054, "FCOS", 2, 1.00, TR, "X", "Y", "Write cosine of X to Y") +CPU(055, "FTAN", 2, 1.00, TR, "X", "Y", "Write tangent of X to Y") +CPU(056, "FASIN", 2, 1.00, TR, "X", "Y", "Write arcsine of X to Y") +CPU(057, "FACOS", 2, 1.00, TR, "X", "Y", "Write arccosine of X to Y") +CPU(058, "FATAN", 2, 1.00, TR, "X", "Y", "Write arctangent of X to Y") +CPU(059, "MOD", 2, 2.00, 0, "X", "Y", "Write remainder of X/Y to Y") +---- Dec 6 -------------------------------------------------------------------------------------------------------------------------------------- +CPU(060, "BIT", 2, 2.00, 0, "INT", "BIT", "Test whether BIT of X is set. Use with conditional branching instructions") +CPU(061, "SBIT", 2, 2.00, 0, "INT", "BIT", "Set BIT of X") +CPU(062, "CBIT", 2, 2.00, 0, "INT", "BIT", "Clear BIT of X") +CPU(063, "TBIT", 2, 2.00, 0, "INT", "BIT", "Toggle BIT of X") +CPU(064, "BAND", 2, 2.00, 0, "INT", "INT", "Write result of binary AND between operands") +CPU(065, "BOR", 2, 2.00, 0, "INT", "INT", "Write result of binary OR between operands") +CPU(066, "BXOR", 2, 2.00, 0, "INT", "INT", "Write result of binary XOR between operands") +CPU(067, "BSHL", 2, 2.00, 0, "INT", "X", "Shift bits of INT left by X") +CPU(068, "BSHR", 2, 2.00, 0, "INT", "X", "Shift bits of INT right by X") +CPU(069, "JMPF", 2, 2.00, UB, "PTR", "CS", "Jump to PTR in code segment CS") +---- Dec 7 -------------------------------------------------------------------------------------------------------------------------------------- +CPU(070, "NMIINT", 1, 4.00, R0+OL, "INTR", "", "EXTINT") +CPU(070, "EXTINT", 1, 10.00, R0, "INTR", "", "Call interrupt INTR as an external interrupt") +CPU(071, "CNE", 1, 2.00, CB, "PTR", "", "Call subrotine if result is not equal") +CPU(071, "CNZ", 1, 2.00, CB, "PTR", "", "Call subrotine if result is not zero") +CPU(072, "RESERVED", 1, 0.00, 0, "", "", "") +CPU(073, "CG", 1, 2.00, CB, "PTR", "", "Call subrotine if result is greater") +CPU(073, "CNLE", 1, 2.00, CB, "PTR", "", "Call subrotine if result is not less or equal") +CPU(074, "CGE", 1, 2.00, CB, "PTR", "", "Call subrotine if result is greater or equal") +CPU(074, "CNL", 1, 2.00, CB, "PTR", "", "Call subrotine if result is not less") +CPU(075, "CL", 1, 2.00, CB, "PTR", "", "Call subrotine if result is less") +CPU(075, "CNGE", 1, 2.00, CB, "PTR", "", "Call subrotine if result is not greater or equal") +CPU(076, "CLE", 1, 2.00, CB, "PTR", "", "Call subrotine if result is less or equal") +CPU(076, "CNG", 1, 2.00, CB, "PTR", "", "Call subrotine if result is not greater") +CPU(077, "CE", 1, 2.00, CB, "PTR", "", "Call subrotine if result is equal") +CPU(077, "CZ", 1, 2.00, CB, "PTR", "", "Call subrotine if result is zero") +CPU(078, "MCOPY", 1, 2.00, BL, "INT", "", "Copy INT bytes from array pointed by ESI to EDI") +CPU(079, "MXCHG", 1, 2.00, BL, "INT", "", "Swap INT bytes between two arrays pointed by ESI and EDI") +---- Dec 8 -------------------------------------------------------------------------------------------------------------------------------------- +CPU(080, "FPWR", 2, 2.00, 0, "X", "Y", "Raise X to power Y") +CPU(081, "XCHG", 2, 2.00, 0, "X", "Y", "Swap X and Y") +CPU(082, "FLOG", 2, 2.00, OL, "X", "Y", "FLN") +CPU(082, "FLN", 2, 10.00, 0, "X", "Y", "Write logarithm (base e) of Y to X") +CPU(083, "FLOG10", 2, 2.00, 0, "X", "Y", "Write logarithm (base 10) of Y to X") +CPU(084, "IN", 2, 2.00, 0, "X", "PORT", "Input value from PORT to X") +CPU(085, "OUT", 2, 2.00, 0, "PORT", "Y", "Write X to PORT") +CPU(086, "FABS", 2, 2.00, TR, "X", "Y", "Write absolute value of Y to X") +CPU(087, "FSGN", 2, 2.00, TR, "X", "Y", "Write sign of Y to X") +CPU(088, "FEXP", 2, 2.00, TR, "X", "Y", "Write exponent of Y to X") +CPU(089, "CALLF", 2, 2.00, UB, "PTR", "CS", "Call subroutine by offset PTR in code segment CS") +---- Dec 9 -------------------------------------------------------------------------------------------------------------------------------------- +CPU(090, "FPI", 1, 2.00, W1, "X", "", "Set X to precise value of PI (3.1415926..)") +CPU(091, "FE", 1, 2.00, W1, "X", "", "Set X to precise value of E (2.7182818..)") +CPU(092, "INT", 1, 2.00, 0, "INTR", "", "Call interrupt INTR") +CPU(093, "TPG", 1, 2.00, 0, "PAGE", "", "Test PAGE. Use branching instructions to test for zero on failure, non-zero if test passed.") +CPU(094, "FCEIL", 1, 2.00, W1, "X", "", "Rounds X up to the next integer") +CPU(095, "ERPG", 1, 2.00, R0, "PAGE", "", "Erase ROM page") +CPU(096, "WRPG", 1, 2.00, R0, "PAGE", "", "Copy RAM page into ROM page") +CPU(097, "RDPG", 1, 2.00, R0, "PAGE", "", "Read ROM page into RAM") +CPU(098, "TIMER", 1, 2.00, W1, "X", "", "Set X to value of the internal processor timer") +CPU(099, "LIDTR", 1, 2.00, R0, "PTR", "", "Set interrupt table pointer to PTR") +---- Dec 10 ------------------------------------------------------------------------------------------------------------------------------------- +CPU(100, "RESERVED", 1, 0.00, R0, "", "", "") +CPU(101, "JNER", 1, 3.00, CB, "INT", "", "Relative jump INT bytes forward if result is not equal") +CPU(101, "JNZR", 1, 3.00, CB, "INT", "", "Relative jump INT bytes forward if result is not zero") +CPU(102, "JMPR", 1, 3.00, UB, "INT", "", "Relative jump INT bytes forward") +CPU(103, "JGR", 1, 3.00, CB, "INT", "", "Relative jump INT bytes forward if result is greater") +CPU(103, "JNLER", 1, 3.00, CB, "INT", "", "Relative jump INT bytes forward if result is not less or equal") +CPU(104, "JGER", 1, 3.00, CB, "INT", "", "Relative jump INT bytes forward if result is greater or equal") +CPU(104, "JNLR", 1, 3.00, CB, "INT", "", "Relative jump INT bytes forward if result is not less") +CPU(105, "JLR", 1, 3.00, CB, "INT", "", "Relative jump INT bytes forward if result is less") +CPU(105, "JNGER", 1, 3.00, CB, "INT", "", "Relative jump INT bytes forward if result is not greater or equal") +CPU(106, "JLER", 1, 3.00, CB, "INT", "", "Relative jump INT bytes forward if result is less or equal") +CPU(106, "JNGR", 1, 3.00, CB, "INT", "", "Relative jump INT bytes forward if result is not greater") +CPU(107, "JER", 1, 3.00, CB, "INT", "", "Relative jump INT bytes forward if result is equal") +CPU(107, "JZR", 1, 3.00, CB, "INT", "", "Relative jump INT bytes forward if result is zero") +CPU(108, "LNEG", 1, 3.00, W1, "X", "", "Logically negate X") +CPU(109, "RESERVED", 1, 0.00, R0, "", "", "") +---- Dec 11 ------------------------------------------------------------------------------------------------------------------------------------- +CPU(110, "NMIRET", 0, 2.00, R0+OL, "", "", "EXTRET") +CPU(110, "EXTRET", 0, 10.00, R0, "", "", "Return from an external interrupt") +CPU(111, "IDLE", 0, 4.00, R0, "", "", "Skip several processor cycles") +CPU(112, "NOP", 0, 5.00, 0, "", "", "Do nothing") +CPU(113, "RESERVED", 0, 0.00, 0, "", "", "") +CPU(114, "PUSHA", 0, 8.00, 0, "", "", "Push all general purpose registers to stack") +CPU(115, "POPA", 0, 8.00, 0, "", "", "Pop all general purpose registers off stack") +CPU(116, "STD2", 0, 10.00, R0, "", "", "Enable hardware debug mode") +CPU(117, "LEAVE", 0, 10.00, 0, "", "", "Leave subroutine stack frame") +CPU(118, "STM", 0, 10.00, R0, "", "", "Enable extended memory mode") +CPU(119, "CLM", 0, 10.00, R0, "", "", "Disable extended memory mode") +---- Dec 12 ------------------------------------------------------------------------------------------------------------------------------------- +CPU(120, "CPUGET", 2, 5.00, R0, "X", "IDX", "Read internal processor register IDX") +CPU(121, "CPUSET", 2, 5.00, R0, "IDX", "Y", "Write internal processor register IDX") +CPU(122, "SPP", 2, 5.00, R0+BL, "PAGE", "IDX", "Set page flag IDX") +CPU(123, "CPP", 2, 5.00, R0+BL, "PAGE", "IDX", "Clear page flag IDX") +CPU(124, "SRL", 2, 5.00, R0+BL, "PAGE", "INT", "Set page runlevel to INT") +CPU(125, "CRL", 2, 5.00, R0, "X", "PAGE", "Write page runlevel to INT") +CPU(126, "LEA", 2, 5.00, 0, "X", "Y", "Load absolute address fetched by operand Y into X") +CPU(127, "BLOCK", 2, 6.00, 0, "PTR", "SIZE", "Make next instruction run on this block") +CPU(128, "CMPAND", 2, 6.00, 0, "X", "Y", "Compare X and Y, and logically combine with result of previous comparsion using AND") +CPU(129, "CMPOR", 2, 6.00, 0, "X", "Y", "Compare X and Y, and logically combine with result of previous comparsion using OR") +---- Dec 13 ------------------------------------------------------------------------------------------------------------------------------------- +CPU(130, "MSHIFT", 2, 7.00, 0, "COUNT", "OFFSET","Shift (and rotate) data pointed by ESI by OFFSET bytes") +CPU(131, "SMAP", 2, 8.00, R0+BL, "PAGE1", "PAGE2", "Remap PAGE1 to physical page PAGE2") +CPU(132, "GMAP", 2, 8.00, R0, "X", "PAGE", "Read what physical page PAGE is mapped to") +CPU(133, "RSTACK", 2, 9.00, 0, "X", "IDX", "Read value from stack at offset IDX (from address SS+IDX)") +CPU(134, "SSTACK", 2, 9.00, 0, "IDX", "Y", "Write value to stack at offset IDX (to address SS+IDX)") +CPU(135, "ENTER", 1, 10.00, 0, "SIZE", "", "Enter stack frame and allocate SIZE bytes on stack for local variables") +CPU(136, "IRETP", 1, 2.00, R0, "PTBL", "", "Set PTBL, then return from an interrupt") +CPU(137, "EXTRETP", 1, 10.00, R0, "PTBL", "", "Set PTBL, then return from an external interrupt") +---- Dec 14 -- UNDEFINED ------------------------------------------------------------------------------------------------------------------------ +CPU(140, "EXTRETA", 0, 11.00, R0, "", "", "Return from an external interrupt and restore R0-R31 registers") +CPU(141, "EXTRETPA", 1, 11.00, R0, "PTBL", "", "Set PTBL, then return from an external interrupt with restoring R0-R31 registers") +---- Dec 15 -- UNDEFINED ------------------------------------------------------------------------------------------------------------------------ +---- Dec 16 -- UNDEFINED ------------------------------------------------------------------------------------------------------------------------ +---- Dec 17 -- UNDEFINED ------------------------------------------------------------------------------------------------------------------------ +---- Dec 18 -- UNDEFINED ------------------------------------------------------------------------------------------------------------------------ +---- Dec 19 -- UNDEFINED ------------------------------------------------------------------------------------------------------------------------ +---- Dec 20 -- Output buffer control ------------------------------------------------------------------------------------------------------------ +GPU(200, "DTEST", 0, 1.0, 0, "", "", "Output a test pattern to screen") +GPU(200, "DRECT_TEST", 0, 0.5, OL, "", "", "DTEST") +GPU(201, "DEXIT", 0, 0.5, UB, "", "", "End execution of the current frame") +GPU(201, "DVSYNC", 0, 1.0, 0, "", "", "Wait until next frame (only in asynchronous thread)") +GPU(202, "DCLR", 0, 0.5, 0, "", "", "Clear screen color to black") +GPU(203, "DCLRTEX", 0, 0.5, 0, "", "", "Clear background with texture") +GPU(204, "DVXFLUSH", 0, 0.6, 0, "", "", "Flush current vertex buffer to screen") +GPU(205, "DVXCLEAR", 0, 0.6, 0, "", "", "Clear vertex buffer") +GPU(206, "DSETBUF_VX", 0, 1.0, 0, "", "", "Set frame buffer to vertex output") +GPU(207, "DSETBUF_SPR", 0, 1.0, 0, "", "", "Set frame buffer to sprite buffer") +GPU(207, "DBACKBUF", 0, 1.0, 0, "", "", "Set frame buffer to back buffer") +GPU(208, "DSETBUF_FBO", 0, 1.0, 0, "", "", "Set frame buffer to view buffer") +GPU(208, "DFRONTBUF", 0, 1.0, 0, "", "", "Set frame buffer to front buffer") +GPU(209, "DSWAP", 0, 1.0, 0, "", "", "Copy back buffer to front buffer") +---- Dec 21 -- Pipe controls and one-operand opcodes -------------------------------------------------------------------------------------------- +GPU(210, "DVXPIPE", 1, 0.5, 0, "IDX", "", "Set vertex pipe") +GPU(211, "DCVXPIPE", 1, 0.5, OL, "IDX", "", "DCPIPE") +GPU(211, "DCPIPE", 1, 1.0, 0, "IDX", "", "Set coordinate pipe") +GPU(212, "DENABLE", 1, 0.5, 0, "IDX", "", "Enable parameter") +GPU(213, "DDISABLE", 1, 0.5, 0, "IDX", "", "Disable parameter") +GPU(214, "DCLRSCR", 1, 0.5, 0, "COLOR", "", "Clear screen with color") +GPU(215, "DCOLOR", 1, 0.5, 0, "COLOR", "", "Set current color") +GPU(216, "DTEXTURE", 1, 1.0, 0, "IDX", "", "Set current texture") +GPU(217, "DSETFONT", 1, 0.5, 0, "IDX", "", "Set current font") +GPU(218, "DSETSIZE", 1, 0.5, 0, "INT", "", "Set font size") +GPU(219, "DMOVE", 1, 0.5, 0, "VEC2F", "", "Set drawing position offset") +---- Dec 22 -- Rendering opcodes ---------------------------------------------------------------------------------------------------------------- +GPU(220, "DVXDATA_2F", 2, 0.5, 0, "VEC2F", "IDX", "Draw a solid 2D polygon (pointer to 2D data, vertex count)") +GPU(220, "DVXPOLY", 2, 0.5, 0, "VEC2F", "IDX", "Draw a solid 2D polygon (pointer to 2D data, vertex count)") +GPU(221, "DVXDATA_2F_TEX",2, 0.5, 0, "VEC2F", "IDX", "Draw a textured 2D polygon (pointer to 2D data, vertex count)") +GPU(221, "DVXTEXPOLY", 2, 0.5, 0, "VEC2F", "IDX", "Draw a textured 2D polygon (pointer to 2D data, vertex count)") +GPU(222, "DVXDATA_3F", 2, 0.5, 0, "VEC3F", "IDX", "Draw a solid 3D polygon (pointer to 3D data, vertex count)") +GPU(223, "DVXDATA_3F_TEX",2, 0.5, 0, "VEC3FT","IDX", "Draw a textured 3D polygon (pointer to 3D data, vertex count)") +GPU(224, "DVXDATA_3F_WF", 2, 0.5, 0, "VEC3F", "IDX", "Draw a wireframe 3D polygon (pointer to 3D data, vertex count)") +GPU(225, "DRECT", 2, 0.5, 0, "VEC2F", "VEC2F", "Draw a rectangle (by endpoints)") +GPU(226, "DCIRCLE", 2, 0.5, 0, "VEC2F", "Y", "Draw a circle with radius Y") +GPU(227, "DLINE", 2, 0.5, 0, "VEC2F", "VEC2F", "Draw a line") +GPU(228, "DRECTWH", 2, 0.6, 0, "VEC2F", "VEC2F", "Draw a rectangle (by offset, size)") +GPU(229, "DORECT", 2, 0.5, 0, "VEC2F", "VEC2F", "Draw an outlined rectangle") +---- Dec 23 -- Additional rendering opcodes ----------------------------------------------------------------------------------------------------- +GPU(230, "DTRANSFORM2F", 2, 0.5, 0, "VEC2F", "VEC2F", "Transform vector and write it to first operand") +GPU(231, "DTRANSFORM3F", 2, 0.5, 0, "VEC3F", "VEC3F", "Transform vector and write it to first operand") +GPU(232, "DSCRSIZE", 2, 0.5, 0, "X", "Y", "Set screen size") +GPU(233, "DROTATESCALE", 2, 0.5, 0, "X", "Y", "Rotate by X, scale by Y") +GPU(234, "DORECTWH", 2, 0.5, 0, "VEC2F", "VEC2F", "Draw an outlined rectangle by width/height") +GPU(235, "DCULLMODE", 2, 0.7, 0, "IDX", "IDX", "Set cullmode and lighting mode") +--GPU(236, "DARRAY", 2, 1.0, 0, "VEC2F", "STRUCT","Draw an array of pixels") +--GPU(237, "DDTERMINAL", 2, 1.0, 0, "VEC2F", "STRUCT","Draw a console screen/terminal window") +GPU(238, "DPIXEL", 2, 1.0, 0, "VEC2F", "COLOR", "Draw a pixel to screen") +GPU(239, "RESERVED", 2, 0.0, 0, "", "", "") +---- Dec 24 -- Text output and lighting --------------------------------------------------------------------------------------------------------- +GPU(240, "DWRITE", 2, 0.5, 0, "VEC2F", "STRING","Write a string") +GPU(241, "DWRITEI", 2, 0.5, 0, "VEC2F", "INT", "Write an integer value") +GPU(242, "DWRITEF", 2, 0.5, 0, "VEC3F", "Y", "Write a float value") +GPU(243, "DENTRYPOINT", 2, 0.5, 0, "IDX", "PTR", "Set entry point") +GPU(244, "DSETLIGHT", 2, 0.6, 0, "IDX", "STRUCT","Set light") +GPU(245, "DGETLIGHT", 2, 0.6, 0, "STRUCT","IDX", "Get light") +GPU(246, "DWRITEFMT", 2, 0.6, 0, "VEC2F", "STRING","Write a formatted string") +GPU(247, "DWRITEFIX", 2, 0.5, 0, "VEC2F", "Y", "Write a fixed value") +GPU(248, "DTEXTWIDTH", 2, 0.8, 0, "INT", "STRING","Return text width") +GPU(249, "DTEXTHEIGHT", 2, 0.8, 0, "INT", "STRING","Return text height") +---- Dec 25 -- Vector mode extension ------------------------------------------------------------------------------------------------------------ +VEX(250, "VADD", 2, 7.00, 0, "VEC", "VEC", "X = X + Y") +VEX(251, "VSUB", 2, 7.00, 0, "VEC", "VEC", "X = X - Y") +VEX(252, "VMUL", 2, 7.00, 0, "VEC", "X", "X = X * SCALAR Y") +VEX(253, "VDOT", 2, 7.00, 0, "VEC", "VEC", "X = X dot Y") +VEX(254, "VCROSS", 2, 7.00, 0, "VEC", "VEC", "X = X cross Y") +VEX(255, "VMOV", 2, 7.00, 0, "VEC", "VEC", "X = Y") +VEX(256, "VNORM", 2, 7.00, 0, "VEC", "VEC", "X = NORMALIZE(Y)") +VEX(257, "VCOLORNORM", 2, 10.0, 0, "COLOR", "COLOR", "Normalize color (clamp it to RGB range)") +GPU(258, "RESERVED", 2, 0.0, 0, "", "", "") +GPU(259, "DLOOPXY", 2, 0.7, CB, "PTR", "PTR", "2D loop by ECX/EDX registers") +VEX(259, "LOOPXY", 2, 10.0, CB, "PTR", "PTR", "2D loop by ECX/EDX registers") +---- Dec 26 -- Matrix math ---------------------------------------------------------------------------------------------------------------------- +VEX(260, "MADD", 2, 7.00, 0, "MATRIX","MATRIX","X = X + Y") +VEX(261, "MSUB", 2, 7.00, 0, "MATRIX","MATRIX","X = X - Y") +VEX(262, "MMUL", 2, 7.00, 0, "MATRIX","MATRIX","X = X * Y") +VEX(263, "MROTATE", 2, 7.00, 0, "MATRIX","VEC4F", "Rotation matrix based on rotation vector") +VEX(264, "MSCALE", 2, 7.00, 0, "MATRIX","VEC4F", "Scaling matrix based on scaling vector") +VEX(265, "MPERSPECTIVE", 2, 7.00, 0, "MATRIX","VEC4F", "Perspective matrix based on FOV and near/far planes") +VEX(266, "MTRANSLATE", 2, 7.00, 0, "MATRIX","VEC4F", "Translation matrix based on translation vector") +VEX(267, "MLOOKAT", 2, 7.00, 0, "MATRIX","VEC4F", "Lookat matrix based on three vectors") +VEX(268, "MMOV", 2, 7.00, 0, "MATRIX","MATRIX","X = Y") +VEX(269, "VLEN", 2, 7.00, 0, "X", "VEC", "X = Sqrt(Y dot Y)") +---- Dec 27 -- Matrix math ---------------------------------------------------------------------------------------------------------------------- +VEX(270, "MIDENT", 1, 7.00, 0, "MATRIX","", "Load identity matrix") +GPU(271, "MLOADPROJ", 1, 0.6, 0, "MATRIX","", "Load matrix into view matrix") +GPU(272, "MREAD", 1, 0.6, 0, "MATRIX","", "Write view matrix into matrix") +VEX(273, "VMODE", 1, 7.00, 0, "IDX", "", "Set vector math mode") +GPU(274, "DT", 1, 0.6, W1, "X", "", "Set X to frame length time") +GPU(275, "RESERVED", 1, 0.0, 0, "", "", "") +GPU(276, "DSHADE", 1, 0.5, 0, "X", "", "Shade the current color") +GPU(277, "DSETWIDTH", 1, 0.5, 0, "X", "", "Set line width") +GPU(278, "MLOAD", 1, 0.6, 0, "MATRIX","", "Load matrix into model matrix") +GPU(279, "DSHADENORM", 1, 0.6, 0, "X", "", "Shade the current color and normalize it") +GPU(279, "DSHADECOL", 1, 0.6, OL, "X", "", "DSHADENORM") +---- Dec 28 -- Advanced rendering --------------------------------------------------------------------------------------------------------------- +GPU(280, "DDFRAME", 1, 1.0, 0, "STRUCT","", "Draw bordered frame") +GPU(281, "DDBAR", 1, 1.0, 0, "STRUCT","", "Draw a progress bar") +GPU(282, "DDGAUGE", 1, 1.0, 0, "STRUCT","", "Draw gauge needle") +GPU(283, "DRASTER", 1, 0.6, 0, "INT", "", "Set rasterizer quality level") +GPU(284, "DDTERRAIN", 1, 0.8, 0, "STRUCT","", "Draw terrain") +GPU(285, "RESERVED", 1, 0.0, 0, "", "", "") +GPU(286, "RESERVED", 1, 0.0, 0, "", "", "") +GPU(287, "RESERVED", 1, 0.0, 0, "", "", "") +GPU(288, "RESERVED", 1, 0.0, 0, "", "", "") +GPU(289, "RESERVED", 1, 0.0, 0, "", "", "") +---- Dec 29 -- Additional instructions ---------------------------------------------------------------------------------------------------------- +GPU(290, "DLOADBYTES", 2, 1.0, 0, "IDX", "PTR", "Load into texture slot by pointer") +GPU(291, "RESERVED", 2, 0.0, 0, "", "", "") +GPU(292, "RESERVED", 2, 0.0, 0, "", "", "") +GPU(293, "RESERVED", 2, 0.0, 0, "", "", "") +GPU(294, "DMULDT", 2, 0.7, 0, "X", "Y", "X = Y * dT") +VEX(295, "VDIV", 2, 7.00, 0, "VEC", "Y", "VEC = VEC / Y") +VEX(296, "VTRANSFORM", 2, 8.00, 0, "VEC", "MATRIX","X = X * MATRIX") +GPU(297, "DSMOOTH", 2, 1.0, 0, "X", "Y", "Smooth X with smoothness Y") +GPU(298, "DBEGIN", 0, 1.0, 0, "", "", "Begin rendering (from async thread)") +GPU(299, "DEND", 0, 1.0, 0, "", "", "End rendering (from async thread)") +---- Dec 30 -- 3D rendering --------------------------------------------------------------------------------------------------------------------- +GPU(300, "DROTATE", 1, 1.0, 0, "VEC4F", "", "Rotate model by vector") +GPU(301, "DTRANSLATE", 1, 1.0, 0, "VEC4F", "", "Translate model by vector") +GPU(302, "DSCALE", 1, 1.0, 0, "VEC4F", "", "Scale model by vector") +GPU(303, "DXTEXTURE", 1, 1.0, 0, "STR", "", "Bind a specific external texture") +GPU(304, "RESERVED", 2, 0.0, 0, "", "", "") +GPU(305, "RESERVED", 2, 0.0, 0, "", "", "") +GPU(306, "RESERVED", 2, 0.0, 0, "", "", "") +GPU(307, "RESERVED", 2, 0.0, 0, "", "", "") +GPU(308, "RESERVED", 2, 0.0, 0, "", "", "") +GPU(309, "RESERVED", 2, 0.0, 0, "", "", "") +---- Dec 31 -- UNDEFINED ------------------------------------------------------------------------------------------------------------------------ +---- Dec 32 -- SPU output control --------------------------------------------------------------------------------------------------------------- +SPU(320, "CHRESET" , 1, 1.0, 0, "CHAN", "", "Reset channel") +SPU(321, "CHSTART", 1, 1.0, 0, "CHAN", "", "Start sound on channel") +SPU(322, "CHSTOP", 1, 1.0, 0, "CHAN", "", "Stop sound on channel") +SPU(323, "CHTRIGGER", 1, 1.0, 0, "CHAN", "", "Trigger the ADSR envelope on channel") +SPU(324, "CHRELEASE", 1, 1.0, 0, "CHAN", "", "Release the ADSR envelope on channel") +SPU(325, "RESERVED", 1, 0.0, 0, "", "", "") +SPU(326, "RESERVED", 1, 0.0, 0, "", "", "") +SPU(327, "RESERVED", 1, 0.0, 0, "", "", "") +SPU(328, "RESERVED", 1, 0.0, 0, "", "", "") +SPU(329, "RESERVED", 1, 0.0, 0, "", "", "") +---- Dec 33 -- SPU channel control -------------------------------------------------------------------------------------------------------------- +SPU(330, "WSET", 2, 1.0, 0, "WAVE", "STRING","Set lookup name for specific wave") +SPU(331, "CHWAVE", 2, 1.0, 0, "CHAN", "WAVE", "Set waveform") +SPU(332, "CHLOOP", 2, 1.0, 0, "CHAN", "IDX", "Set looping mode") +SPU(333, "CHVOLUME", 2, 1.0, 0, "CHAN", "X", "Set volume") +SPU(334, "CHPITCH", 2, 1.0, 0, "CHAN", "X", "Set pitch (value interpretation depends on register)") +SPU(335, "CHMODT", 2, 1.0, 0, "CHAN", "X", "Set LFO modulation type") +SPU(336, "CHMODA", 2, 1.0, 0, "CHAN", "X", "Set LFO modulation amplitude") +SPU(337, "CHMODF", 2, 1.0, 0, "CHAN", "X", "Set LFO modulation frequency") +SPU(338, "CHADSR", 2, 1.0, 0, "CHAN", "VEC4F", "Set channel ADSR") +SPU(339, "WLEN", 2, 1.0, 0, "X", "WAVE", "Read sound length in seconds") diff --git a/lua/wire/stools/cpu.lua b/lua/wire/stools/cpu.lua new file mode 100644 index 0000000000..2d842611b9 --- /dev/null +++ b/lua/wire/stools/cpu.lua @@ -0,0 +1,400 @@ +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" }, + } +end +WireToolSetup.BaseLang() +WireToolSetup.SetupMax( 7 ) + +TOOL.ClientConVar = { + model = "models/cheeze/wires/cpu.mdl", + filename = "", + memorymodel = "64krom", +} + +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 +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")) + 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: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) + end + end + net.Receive("ZCPU_RequestCode", ZCPU_RequestCode) + + ------------------------------------------------------------------------------ + -- Open ZCPU editor + ------------------------------------------------------------------------------ + function ZCPU_OpenEditor() + if not ZCPU_Editor then + ZCPU_Editor = vgui.Create("Expression2EditorFrame") + ZCPU_Editor:Setup("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") + ZCPU_Editor:Setup("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 = ""}) + + + ---------------------------------------------------------------------------- + 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"}, + } + }) + panel:AddControl("Label", {Text = "Sets the processor memory model (determines interaction with the external devices)"}) + 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 new file mode 100644 index 0000000000..a663b6f140 --- /dev/null +++ b/lua/wire/stools/gpu.lua @@ -0,0 +1,195 @@ +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" }, + } +end +WireToolSetup.BaseLang() +WireToolSetup.SetupMax( 7 ) + +TOOL.ClientConVar = { + model = "models/cheeze/wires/cpu.mdl", + filename = "", + memorymodel = "64k", +} + +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 +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")) + 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") + end + end + net.Receive("ZGPU_RequestCode", ZGPU_RequestCode) + + ------------------------------------------------------------------------------ + -- Open ZGPU editor + ------------------------------------------------------------------------------ + function ZGPU_OpenEditor() + if not ZGPU_Editor then + ZGPU_Editor = vgui.Create("Expression2EditorFrame") + ZGPU_Editor:Setup("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") + ZGPU_Editor:Setup("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"}) + + + ---------------------------------------------------------------------------- +-- panel:AddControl("Button", { +-- Text = "ZGPU documentation (online)" +-- }) +-- panel:AddControl("Label", { +-- Text = "Loads online GPU documentation and tutorials" +-- }) + 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 new file mode 100644 index 0000000000..6fc663cdf3 --- /dev/null +++ b/lua/wire/stools/spu.lua @@ -0,0 +1,168 @@ +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" }, + } +end +WireToolSetup.BaseLang() +WireToolSetup.SetupMax( 7 ) + +TOOL.ClientConVar = { + model = "models/cheeze/wires/cpu.mdl", + filename = "", +} + +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 +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")) + 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) + end + end + net.Receive("ZSPU_RequestCode", ZSPU_RequestCode) + + ------------------------------------------------------------------------------ + -- Open ZSPU editor + ------------------------------------------------------------------------------ + function ZSPU_OpenEditor() + if not ZSPU_Editor then + ZSPU_Editor = vgui.Create("Expression2EditorFrame") + ZSPU_Editor:Setup("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") + ZSPU_Editor:Setup("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 = ""}) + end + + ------------------------------------------------------------------------------ + -- Tool screen + ------------------------------------------------------------------------------ + function TOOL:DrawToolScreen(width, height) + CPULib.RenderCPUTool(1,"ZSPU") + end +end diff --git a/lua/wire/zvm/zvm_core.lua b/lua/wire/zvm/zvm_core.lua new file mode 100644 index 0000000000..fc7a575a17 --- /dev/null +++ b/lua/wire/zvm/zvm_core.lua @@ -0,0 +1,662 @@ +-------------------------------------------------------------------------------- +-- Zyelios VM (Zyelios CPU/GPU virtual machine) +-- +-- Virtual machine implementation core +-------------------------------------------------------------------------------- +ZVM = {} +if not SERVER and not CLIENT then + ZVM.MicrocodeDebug = true +end + + + + +-------------------------------------------------------------------------------- +-- Include extra files +include("wire/zvm/zvm_opcodes.lua") +include("wire/zvm/zvm_features.lua") +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) + 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(text) + self.EmitBlock = self.EmitBlock..text.."\n" + end +end + + + + +-------------------------------------------------------------------------------- +-- Start new dynamic precompile block +function ZVM:Dyn_StartBlock() + 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 = {} + + -- 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 + + + + +-------------------------------------------------------------------------------- +-- 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 + + -- 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 + + if self.NeedInterruptCheck[RM] then self.EmitNeedInterruptCheck = true end + end + + self.EmitOperandRM[OP] = RM +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 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")) + + self:Emit(preEmit) + end + end +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") +end + + + + +-------------------------------------------------------------------------------- +-- Emit preprocessed text +function ZVM:Dyn_Emit(text) + self:Emit(self:Dyn_PreprocessEmit(text)) +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 +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 +end + + + + +-------------------------------------------------------------------------------- +-- Emit forced block return +function ZVM:Dyn_EmitBreak(emitIP) + self:Emit("VM.TMR = VM.TMR + "..self.PrecompileInstruction) + self:Emit("VM.CODEBYTES = VM.CODEBYTES + "..self.PrecompileBytes) + if emitIP then + self:Emit("VM.IP = "..(self.PrecompileIP or 0)) + self:Emit("VM.XEIP = "..(self.PrecompileTrueXEIP or 0)) + end + if self.ExtraEmitFunction then self.ExtraEmitFunction(self) end + self:Emit("if true then return end") +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 +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 +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 +end + + + + +-------------------------------------------------------------------------------- +-- Emit specific opcode +function ZVM:Dyn_EmitOpcode(opcode) + self.EmitExpression = {} + if self.OpcodeTable[opcode] then + self.OpcodeTable[opcode](self) + end +end + + + + +-------------------------------------------------------------------------------- +-- Emit interrupt call +function ZVM:Dyn_EmitInterrupt(intNo,intParam) + self:Dyn_EmitState() + self:Emit("VM.IP = "..(self.PrecompileIP or 0)) + self:Emit("VM.XEIP = "..(self.PrecompileTrueXEIP or 0)) + self:Dyn_Emit("VM:Interrupt("..intNo..","..intParam..")") + self:Dyn_EmitBreak() +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 = "..self.PrecompileStartIP) + self:Emit("VM.XEIP = "..(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 = "..self.PrecompileStartIP) + self:Emit("VM.XEIP = "..(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 = "..self.PrecompileStartIP) + self:Emit("VM.XEIP = "..(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 + + + + +-------------------------------------------------------------------------------- +-- End precompile block +function ZVM:Dyn_EndBlock() + if not self.PrecompileBreak then + self:Dyn_EmitState() + self:Dyn_EmitBreak(true) + end + + 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 + + + + +-------------------------------------------------------------------------------- +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.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 + for address = self.PrecompileStartXEIP, self.PrecompileXEIP-1 do + if not self.IsAddressPrecompiled[address] then + self.IsAddressPrecompiled[address] = { } + end + table.insert(self.IsAddressPrecompiled[address],self.PrecompileStartXEIP) + end + self.PrecompiledData[self.PrecompileStartXEIP] = 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.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 +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 + + -- 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("..math.floor(self.PrecompileXEIP/128)..")") + self:Emit("if (VM.PCAP == 1) and (VM.CurrentPage.Execute == 0) and") + self:Emit(" (VM.PreviousPage.RunLevel ~= 0) then") + self:Dyn_EmitInterrupt("14",self.PrecompileIP) + self:Emit("end") + self:Emit("VM:SetPreviousPage("..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 + + -- 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 (self.OperandCount[Opcode % 1000] and (self.OperandCount[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:Emit("if (VM.PCAP == 1) and (VM.CurrentPage.RunLevel > "..self.OpcodeRunLevel[Opcode]..") then") + self:Dyn_EmitInterrupt("13",Opcode) + self:Emit("end") + 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 self.OperandCount[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 self.OperandCount[Opcode] > 0 then + self:Dyn_LoadOperand(1,dRM1) + if self.OperandCount[Opcode] > 1 then + self:Dyn_LoadOperand(2,dRM2) + end + end + else + if self.OperandCount[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 self.OperandCount[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 = "..(self.PrecompileIP or 0)) + self:Emit("VM.XEIP = "..(self.PrecompileTrueXEIP or 0)) + end + + -- Emit opcode + self:Dyn_EmitOpcode(Opcode) + + -- Write back the values + if self.OperandCount[Opcode] and (self.OperandCount[Opcode] > 0) then + self:Dyn_WriteOperand(1,dRM1) + if self.OperandCount[Opcode] > 1 then + self:Dyn_WriteOperand(2,dRM2) + end + end + + -- Emit interrupt check + if self.EmitNeedInterruptCheck then + self:Dyn_EmitInterruptCheck() + end + end + + -- Do not repeat if opcode breaks the stream + return not self.PrecompileBreak +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 + + -- Check if current instruction is precompiled + local instructionXEIP = self.XEIP + if self.PrecompiledData[instructionXEIP] 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 = "..(self.PrecompileIP or 0)) + self:Emit("VM.XEIP = "..(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 + local previousVM = VM + VM = self + if CLIENT then -- FIXME: hack around crash on PCALL + self.PrecompiledData[self.XEIP]() + else + local status,message = pcall(self.PrecompiledData[self.XEIP]) + if not status then + print("[ZVM ERROR]: "..message) + self:Interrupt(5,1) + end + end + VM = previousVM + 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 + + + + +-------------------------------------------------------------------------------- +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) +end diff --git a/lua/wire/zvm/zvm_data.lua b/lua/wire/zvm/zvm_data.lua new file mode 100644 index 0000000000..1a0edac7ea --- /dev/null +++ b/lua/wire/zvm/zvm_data.lua @@ -0,0 +1,489 @@ +-------------------------------------------------------------------------------- +-- Zyelios VM (Zyelios CPU/GPU virtual machine) +-- +-- Virtual machine lookup tables +-------------------------------------------------------------------------------- + + + + +-------------------------------------------------------------------------------- +-- Internal registers mapped to names +ZVM.InternalRegister = {} +ZVM.InternalLimits = {IPREC = {1, 128}} +ZVM.ReadOnlyRegister = {} + +ZVM.InternalRegister[00] = "IP" +ZVM.InternalRegister[01] = "EAX" +ZVM.InternalRegister[02] = "EBX" +ZVM.InternalRegister[03] = "ECX" +ZVM.InternalRegister[04] = "EDX" +ZVM.InternalRegister[05] = "ESI" +ZVM.InternalRegister[06] = "EDI" +ZVM.InternalRegister[07] = "ESP" +ZVM.InternalRegister[08] = "EBP" +ZVM.InternalRegister[09] = "ESZ" +---------------------------------- +ZVM.InternalRegister[16] = "CS" +ZVM.InternalRegister[17] = "SS" +ZVM.InternalRegister[18] = "DS" +ZVM.InternalRegister[19] = "ES" +ZVM.InternalRegister[20] = "GS" +ZVM.InternalRegister[21] = "FS" +ZVM.InternalRegister[22] = "KS" +ZVM.InternalRegister[23] = "LS" +---------------------------------- +ZVM.InternalRegister[24] = "IDTR" +ZVM.InternalRegister[25] = "CMPR" +ZVM.InternalRegister[26] = "XEIP" ZVM.ReadOnlyRegister[26] = true +ZVM.InternalRegister[27] = "LADD" +ZVM.InternalRegister[28] = "LINT" +ZVM.InternalRegister[29] = "TMR" +ZVM.InternalRegister[30] = "TIMER" +ZVM.InternalRegister[31] = "CPAGE" ZVM.ReadOnlyRegister[31] = true +ZVM.InternalRegister[32] = "IF" +ZVM.InternalRegister[33] = "PF" +ZVM.InternalRegister[34] = "EF" +ZVM.InternalRegister[35] = "NIF" +ZVM.InternalRegister[36] = "MF" +ZVM.InternalRegister[37] = "PTBL" +ZVM.InternalRegister[38] = "PTBE" +ZVM.InternalRegister[39] = "PCAP" +ZVM.InternalRegister[40] = "RQCAP" +---------------------------------- +ZVM.InternalRegister[41] = "PPAGE" ZVM.ReadOnlyRegister[41] = true +ZVM.InternalRegister[42] = "MEMRQ" +---------------------------------- +ZVM.InternalRegister[43] = "RAMSize" ZVM.ReadOnlyRegister[43] = true +ZVM.InternalRegister[44] = "External" +ZVM.InternalRegister[45] = "BusLock" +ZVM.InternalRegister[46] = "Idle" +ZVM.InternalRegister[47] = "INTR" +---------------------------------- +ZVM.InternalRegister[48] = "SerialNo" ZVM.ReadOnlyRegister[48] = true +ZVM.InternalRegister[49] = "CODEBYTES" ZVM.ReadOnlyRegister[49] = true +ZVM.InternalRegister[50] = "BPREC" +ZVM.InternalRegister[51] = "IPREC" +ZVM.InternalRegister[52] = "NIDT" +ZVM.InternalRegister[53] = "BlockStart" +ZVM.InternalRegister[54] = "BlockSize" +ZVM.InternalRegister[55] = "VMODE" +ZVM.InternalRegister[56] = "XTRL" +ZVM.InternalRegister[57] = "HaltPort" +ZVM.InternalRegister[58] = "HWDEBUG" +ZVM.InternalRegister[59] = "DBGSTATE" +ZVM.InternalRegister[60] = "DBGADDR" +ZVM.InternalRegister[61] = "CRL" +ZVM.InternalRegister[62] = "TimerDT" ZVM.ReadOnlyRegister[62] = true +ZVM.InternalRegister[63] = "MEMADDR" +---------------------------------- +ZVM.InternalRegister[64] = "TimerMode" +ZVM.InternalRegister[65] = "TimerRate" +ZVM.InternalRegister[66] = "TimerPrevTime" +ZVM.InternalRegister[67] = "TimerAddress" +ZVM.InternalRegister[68] = "TimerPrevMode" +---------------------------------- +for reg=0,31 do ZVM.InternalRegister[96+reg] = "R"..reg end + + + + +-------------------------------------------------------------------------------- +-- Segment register index mapped to segment register +ZVM.SegmentLookup = {} + +-- Old ZCPU format +ZVM.SegmentLookup[-02] = "CS" +ZVM.SegmentLookup[-03] = "SS" +ZVM.SegmentLookup[-04] = "DS" +ZVM.SegmentLookup[-05] = "ES" +ZVM.SegmentLookup[-06] = "GS" +ZVM.SegmentLookup[-07] = "FS" +ZVM.SegmentLookup[-08] = "KS" +ZVM.SegmentLookup[-09] = "LS" +ZVM.SegmentLookup[-10] = "EAX" +ZVM.SegmentLookup[-11] = "EBX" +ZVM.SegmentLookup[-12] = "ECX" +ZVM.SegmentLookup[-13] = "EDX" +ZVM.SegmentLookup[-14] = "ESI" +ZVM.SegmentLookup[-15] = "EDI" +ZVM.SegmentLookup[-16] = "ESP" +ZVM.SegmentLookup[-17] = "EBP" + +-- New ZCPU format +ZVM.SegmentLookup[01] = "CS" +ZVM.SegmentLookup[02] = "SS" +ZVM.SegmentLookup[03] = "DS" +ZVM.SegmentLookup[04] = "ES" +ZVM.SegmentLookup[05] = "GS" +ZVM.SegmentLookup[06] = "FS" +ZVM.SegmentLookup[07] = "KS" +ZVM.SegmentLookup[08] = "LS" +ZVM.SegmentLookup[09] = "EAX" +ZVM.SegmentLookup[10] = "EBX" +ZVM.SegmentLookup[11] = "ECX" +ZVM.SegmentLookup[12] = "EDX" +ZVM.SegmentLookup[13] = "ESI" +ZVM.SegmentLookup[14] = "EDI" +ZVM.SegmentLookup[15] = "ESP" +ZVM.SegmentLookup[16] = "EBP" +for reg=0,31 do ZVM.SegmentLookup[17+reg] = "R"..reg end + + + + + +-------------------------------------------------------------------------------- +-- Functions to decode RM bytes (READ) +ZVM.OperandReadFunctions = {} + +ZVM.OperandReadFunctions[00] = "$BYTE" +-- GP registers +ZVM.OperandReadFunctions[01] = "VM.EAX" +ZVM.OperandReadFunctions[02] = "VM.EBX" +ZVM.OperandReadFunctions[03] = "VM.ECX" +ZVM.OperandReadFunctions[04] = "VM.EDX" +ZVM.OperandReadFunctions[05] = "VM.ESI" +ZVM.OperandReadFunctions[06] = "VM.EDI" +ZVM.OperandReadFunctions[07] = "VM.ESP" +ZVM.OperandReadFunctions[08] = "VM.EBP" +-- Segment registers +ZVM.OperandReadFunctions[09] = "VM.CS" +ZVM.OperandReadFunctions[10] = "VM.SS" +ZVM.OperandReadFunctions[11] = "VM.DS" +ZVM.OperandReadFunctions[12] = "VM.ES" +ZVM.OperandReadFunctions[13] = "VM.GS" +ZVM.OperandReadFunctions[14] = "VM.FS" +ZVM.OperandReadFunctions[15] = "VM.KS" +ZVM.OperandReadFunctions[16] = "VM.LS" +-- Read from memory by GP +ZVM.OperandReadFunctions[17] = "(VM:ReadCell(VM.EAX+$SEG) or 0)" +ZVM.OperandReadFunctions[18] = "(VM:ReadCell(VM.EBX+$SEG) or 0)" +ZVM.OperandReadFunctions[19] = "(VM:ReadCell(VM.ECX+$SEG) or 0)" +ZVM.OperandReadFunctions[20] = "(VM:ReadCell(VM.EDX+$SEG) or 0)" +ZVM.OperandReadFunctions[21] = "(VM:ReadCell(VM.ESI+$SEG) or 0)" +ZVM.OperandReadFunctions[22] = "(VM:ReadCell(VM.EDI+$SEG) or 0)" +ZVM.OperandReadFunctions[23] = "(VM:ReadCell(VM.ESP+$SEG) or 0)" +ZVM.OperandReadFunctions[24] = "(VM:ReadCell(VM.EBP+$SEG) or 0)" +-- Read from memory by displacement +ZVM.OperandReadFunctions[25] = "(VM:ReadCell($BYTE+$SEG) or 0)" +-- Register plus segment +ZVM.OperandReadFunctions[26] = "(VM.EAX+$SEG)" +ZVM.OperandReadFunctions[27] = "(VM.EBX+$SEG)" +ZVM.OperandReadFunctions[28] = "(VM.ECX+$SEG)" +ZVM.OperandReadFunctions[29] = "(VM.EDX+$SEG)" +ZVM.OperandReadFunctions[30] = "(VM.ESI+$SEG)" +ZVM.OperandReadFunctions[31] = "(VM.EDI+$SEG)" +ZVM.OperandReadFunctions[32] = "(VM.ESP+$SEG)" +ZVM.OperandReadFunctions[33] = "(VM.EBP+$SEG)" +-- Read by register plus immediate +ZVM.OperandReadFunctions[34] = "(VM:ReadCell(VM.EAX+$BYTE) or 0)" +ZVM.OperandReadFunctions[35] = "(VM:ReadCell(VM.EBX+$BYTE) or 0)" +ZVM.OperandReadFunctions[36] = "(VM:ReadCell(VM.ECX+$BYTE) or 0)" +ZVM.OperandReadFunctions[37] = "(VM:ReadCell(VM.EDX+$BYTE) or 0)" +ZVM.OperandReadFunctions[38] = "(VM:ReadCell(VM.ESI+$BYTE) or 0)" +ZVM.OperandReadFunctions[39] = "(VM:ReadCell(VM.EDI+$BYTE) or 0)" +ZVM.OperandReadFunctions[40] = "(VM:ReadCell(VM.ESP+$BYTE) or 0)" +ZVM.OperandReadFunctions[41] = "(VM:ReadCell(VM.EBP+$BYTE) or 0)" +-- Register plus immediate +ZVM.OperandReadFunctions[42] = "(VM.EAX+$BYTE)" +ZVM.OperandReadFunctions[43] = "(VM.EBX+$BYTE)" +ZVM.OperandReadFunctions[44] = "(VM.ECX+$BYTE)" +ZVM.OperandReadFunctions[45] = "(VM.EDX+$BYTE)" +ZVM.OperandReadFunctions[46] = "(VM.ESI+$BYTE)" +ZVM.OperandReadFunctions[47] = "(VM.EDI+$BYTE)" +ZVM.OperandReadFunctions[48] = "(VM.ESP+$BYTE)" +ZVM.OperandReadFunctions[49] = "(VM.EBP+$BYTE)" +-- Constant plus segment +ZVM.OperandReadFunctions[50] = "($BYTE+$SEG)" +-- Ports +for i=1000,2023 do ZVM.OperandReadFunctions[i] = "(VM:ReadPort("..(i-1000)..") or 0)" end +-- Extended registers +for reg=0,31 do ZVM.OperandReadFunctions[2048+reg] = "VM.R"..reg end +for reg=0,31 do ZVM.OperandReadFunctions[2080+reg] = "(VM:ReadCell(VM.R"..reg.."+$SEG) or 0)" end +for reg=0,31 do ZVM.OperandReadFunctions[2112+reg] = "(VM.R"..reg.."+$SEG)" end +for reg=0,31 do ZVM.OperandReadFunctions[2144+reg] = "(VM:ReadCell(VM.R"..reg.."+$BYTE) or 0)" end +for reg=0,31 do ZVM.OperandReadFunctions[2176+reg] = "(VM.R"..reg.."+$BYTE)" end + + + + +-------------------------------------------------------------------------------- +-- Registers required by read operation +ZVM.ReadInvolvedRegisterLookup = {} +for i= 1, 8 do ZVM.ReadInvolvedRegisterLookup[i] = i- 1+ 1 end +--for i= 9,16 do ZVM.ReadInvolvedRegisterLookup[i] = i- 9+16 end +for i=17,24 do ZVM.ReadInvolvedRegisterLookup[i] = i-17+ 1 end +for i=26,33 do ZVM.ReadInvolvedRegisterLookup[i] = i-26+ 1 end +for i=34,41 do ZVM.ReadInvolvedRegisterLookup[i] = i-34+ 1 end +for i=42,49 do ZVM.ReadInvolvedRegisterLookup[i] = i-42+ 1 end +for i=2048,2079 do ZVM.ReadInvolvedRegisterLookup[i] = i-2048+96 end +for i=2080,2111 do ZVM.ReadInvolvedRegisterLookup[i] = i-2080+96 end +for i=2112,2143 do ZVM.ReadInvolvedRegisterLookup[i] = i-2112+96 end +for i=2144,2175 do ZVM.ReadInvolvedRegisterLookup[i] = i-2144+96 end +for i=2176,2207 do ZVM.ReadInvolvedRegisterLookup[i] = i-2176+96 end + + + + + +-------------------------------------------------------------------------------- +-- Functions to decode RM bytes (WRITE) +ZVM.OperandWriteFunctions = {} +ZVM.OperandWriteFunctions[00] = "" +-- GP registers +ZVM.OperandWriteFunctions[01] = "VM.EAX = $EXPR" +ZVM.OperandWriteFunctions[02] = "VM.EBX = $EXPR" +ZVM.OperandWriteFunctions[03] = "VM.ECX = $EXPR" +ZVM.OperandWriteFunctions[04] = "VM.EDX = $EXPR" +ZVM.OperandWriteFunctions[05] = "VM.ESI = $EXPR" +ZVM.OperandWriteFunctions[06] = "VM.EDI = $EXPR" +ZVM.OperandWriteFunctions[07] = "VM.ESP = $EXPR" +ZVM.OperandWriteFunctions[08] = "VM.EBP = $EXPR" +-- Segment registers +ZVM.OperandWriteFunctions[10] = "VM.SS = $EXPR" +ZVM.OperandWriteFunctions[11] = "VM.DS = $EXPR" +ZVM.OperandWriteFunctions[12] = "VM.ES = $EXPR" +ZVM.OperandWriteFunctions[13] = "VM.GS = $EXPR" +ZVM.OperandWriteFunctions[14] = "VM.FS = $EXPR" +ZVM.OperandWriteFunctions[15] = "VM.KS = $EXPR" +ZVM.OperandWriteFunctions[16] = "VM.LS = $EXPR" +-- Write from memory by GP +ZVM.OperandWriteFunctions[17] = "VM:WriteCell(VM.EAX+$SEG,$EXPR)" +ZVM.OperandWriteFunctions[18] = "VM:WriteCell(VM.EBX+$SEG,$EXPR)" +ZVM.OperandWriteFunctions[19] = "VM:WriteCell(VM.ECX+$SEG,$EXPR)" +ZVM.OperandWriteFunctions[20] = "VM:WriteCell(VM.EDX+$SEG,$EXPR)" +ZVM.OperandWriteFunctions[21] = "VM:WriteCell(VM.ESI+$SEG,$EXPR)" +ZVM.OperandWriteFunctions[22] = "VM:WriteCell(VM.EDI+$SEG,$EXPR)" +ZVM.OperandWriteFunctions[23] = "VM:WriteCell(VM.ESP+$SEG,$EXPR)" +ZVM.OperandWriteFunctions[24] = "VM:WriteCell(VM.EBP+$SEG,$EXPR)" +-- Write from memory by displacement +ZVM.OperandWriteFunctions[25] = "VM:WriteCell($BYTE+$SEG,$EXPR)" +-- Write by register plus immediate +ZVM.OperandWriteFunctions[34] = "VM:WriteCell(VM.EAX+$BYTE,$EXPR)" +ZVM.OperandWriteFunctions[35] = "VM:WriteCell(VM.EBX+$BYTE,$EXPR)" +ZVM.OperandWriteFunctions[36] = "VM:WriteCell(VM.ECX+$BYTE,$EXPR)" +ZVM.OperandWriteFunctions[37] = "VM:WriteCell(VM.EDX+$BYTE,$EXPR)" +ZVM.OperandWriteFunctions[38] = "VM:WriteCell(VM.ESI+$BYTE,$EXPR)" +ZVM.OperandWriteFunctions[39] = "VM:WriteCell(VM.EDI+$BYTE,$EXPR)" +ZVM.OperandWriteFunctions[40] = "VM:WriteCell(VM.ESP+$BYTE,$EXPR)" +ZVM.OperandWriteFunctions[41] = "VM:WriteCell(VM.EBP+$BYTE,$EXPR)" +-- Ports +for i=1000,2023 do ZVM.OperandWriteFunctions[i] = "VM:WritePort("..(i-1000)..",$EXPR)" end +-- Extended registers +for reg=0,31 do ZVM.OperandWriteFunctions[2048+reg] = "VM.R"..reg.." = $EXPR" end +for reg=0,31 do ZVM.OperandWriteFunctions[2080+reg] = "VM:WriteCell(VM.R"..reg.."+$SEG,$EXPR)" end +for reg=0,31 do ZVM.OperandWriteFunctions[2144+reg] = "VM:WriteCell(VM.R"..reg.."+$BYTE,$EXPR)" end + + + + + +-------------------------------------------------------------------------------- +-- Registers changed by writeback +ZVM.WriteInvolvedRegisterLookup = {} +for i= 1, 8 do ZVM.WriteInvolvedRegisterLookup[i] = i end +for i=2048,2079 do ZVM.WriteInvolvedRegisterLookup[i] = i-2048+96 end + + + + +-------------------------------------------------------------------------------- +-- Registers required by write operation +ZVM.WriteRequiredRegisterLookup = {} +for i=17,24 do ZVM.WriteRequiredRegisterLookup[i] = i-17+ 1 end +for i=34,41 do ZVM.WriteRequiredRegisterLookup[i] = i-34+ 1 end +for i=2080,2111 do ZVM.WriteRequiredRegisterLookup[i] = i-2080+96 end +for i=2144,2175 do ZVM.WriteRequiredRegisterLookup[i] = i-2144+96 end + + + + +-------------------------------------------------------------------------------- +-- Functions to decode RM bytes (psuedo-WRITE) +ZVM.OperandFastWriteFunctions = {} +-- GP registers +ZVM.OperandFastWriteFunctions[01] = "EAX = $EXPR" +ZVM.OperandFastWriteFunctions[02] = "EBX = $EXPR" +ZVM.OperandFastWriteFunctions[03] = "ECX = $EXPR" +ZVM.OperandFastWriteFunctions[04] = "EDX = $EXPR" +ZVM.OperandFastWriteFunctions[05] = "ESI = $EXPR" +ZVM.OperandFastWriteFunctions[06] = "EDI = $EXPR" +ZVM.OperandFastWriteFunctions[07] = "ESP = $EXPR" +ZVM.OperandFastWriteFunctions[08] = "EBP = $EXPR" +-- Write from memory by GP +ZVM.OperandFastWriteFunctions[17] = "VM:WriteCell(EAX+$SEG,$EXPR)" +ZVM.OperandFastWriteFunctions[18] = "VM:WriteCell(EBX+$SEG,$EXPR)" +ZVM.OperandFastWriteFunctions[19] = "VM:WriteCell(ECX+$SEG,$EXPR)" +ZVM.OperandFastWriteFunctions[20] = "VM:WriteCell(EDX+$SEG,$EXPR)" +ZVM.OperandFastWriteFunctions[21] = "VM:WriteCell(ESI+$SEG,$EXPR)" +ZVM.OperandFastWriteFunctions[22] = "VM:WriteCell(EDI+$SEG,$EXPR)" +ZVM.OperandFastWriteFunctions[23] = "VM:WriteCell(ESP+$SEG,$EXPR)" +ZVM.OperandFastWriteFunctions[24] = "VM:WriteCell(EBP+$SEG,$EXPR)" +-- Write from memory by displacement +ZVM.OperandFastWriteFunctions[25] = "VM:WriteCell($BYTE+$SEG,$EXPR)" +-- Write by register plus immediate +ZVM.OperandFastWriteFunctions[34] = "VM:WriteCell(EAX+$BYTE,$EXPR)" +ZVM.OperandFastWriteFunctions[35] = "VM:WriteCell(EBX+$BYTE,$EXPR)" +ZVM.OperandFastWriteFunctions[36] = "VM:WriteCell(ECX+$BYTE,$EXPR)" +ZVM.OperandFastWriteFunctions[37] = "VM:WriteCell(EDX+$BYTE,$EXPR)" +ZVM.OperandFastWriteFunctions[38] = "VM:WriteCell(ESI+$BYTE,$EXPR)" +ZVM.OperandFastWriteFunctions[39] = "VM:WriteCell(EDI+$BYTE,$EXPR)" +ZVM.OperandFastWriteFunctions[40] = "VM:WriteCell(ESP+$BYTE,$EXPR)" +ZVM.OperandFastWriteFunctions[41] = "VM:WriteCell(EBP+$BYTE,$EXPR)" +-- Extended registers +for reg=0,31 do ZVM.OperandFastWriteFunctions[2048+reg] = "R"..reg.." = $EXPR" end +for reg=0,31 do ZVM.OperandFastWriteFunctions[2080+reg] = "VM:WriteCell(R"..reg.."+$SEG,$EXPR)" end +for reg=0,31 do ZVM.OperandFastWriteFunctions[2144+reg] = "VM:WriteCell(R"..reg.."+$BYTE,$EXPR)" end + + + + +-------------------------------------------------------------------------------- +-- Functions to decode RM bytes (psuedo-READ) +ZVM.OperandFastReadFunctions = {} +-- GP registers +ZVM.OperandFastReadFunctions[01] = "EAX" +ZVM.OperandFastReadFunctions[02] = "EBX" +ZVM.OperandFastReadFunctions[03] = "ECX" +ZVM.OperandFastReadFunctions[04] = "EDX" +ZVM.OperandFastReadFunctions[05] = "ESI" +ZVM.OperandFastReadFunctions[06] = "EDI" +ZVM.OperandFastReadFunctions[07] = "ESP" +ZVM.OperandFastReadFunctions[08] = "EBP" +-- Read from memory by GP +ZVM.OperandFastReadFunctions[17] = "(VM:ReadCell(EAX+$SEG) or 0)" +ZVM.OperandFastReadFunctions[18] = "(VM:ReadCell(EBX+$SEG) or 0)" +ZVM.OperandFastReadFunctions[19] = "(VM:ReadCell(ECX+$SEG) or 0)" +ZVM.OperandFastReadFunctions[20] = "(VM:ReadCell(EDX+$SEG) or 0)" +ZVM.OperandFastReadFunctions[21] = "(VM:ReadCell(ESI+$SEG) or 0)" +ZVM.OperandFastReadFunctions[22] = "(VM:ReadCell(EDI+$SEG) or 0)" +ZVM.OperandFastReadFunctions[23] = "(VM:ReadCell(ESP+$SEG) or 0)" +ZVM.OperandFastReadFunctions[24] = "(VM:ReadCell(EBP+$SEG) or 0)" +-- Read from memory by displacement +ZVM.OperandFastReadFunctions[25] = "(VM:ReadCell($BYTE+$SEG) or 0)" +-- Register plus segment +ZVM.OperandFastReadFunctions[26] = "(EAX+$SEG)" +ZVM.OperandFastReadFunctions[27] = "(EBX+$SEG)" +ZVM.OperandFastReadFunctions[28] = "(ECX+$SEG)" +ZVM.OperandFastReadFunctions[29] = "(EDX+$SEG)" +ZVM.OperandFastReadFunctions[30] = "(ESI+$SEG)" +ZVM.OperandFastReadFunctions[31] = "(EDI+$SEG)" +ZVM.OperandFastReadFunctions[32] = "(ESP+$SEG)" +ZVM.OperandFastReadFunctions[33] = "(EBP+$SEG)" +-- Read by register plus immediate +ZVM.OperandFastReadFunctions[34] = "(VM:ReadCell(EAX+$BYTE) or 0)" +ZVM.OperandFastReadFunctions[35] = "(VM:ReadCell(EBX+$BYTE) or 0)" +ZVM.OperandFastReadFunctions[36] = "(VM:ReadCell(ECX+$BYTE) or 0)" +ZVM.OperandFastReadFunctions[37] = "(VM:ReadCell(EDX+$BYTE) or 0)" +ZVM.OperandFastReadFunctions[38] = "(VM:ReadCell(ESI+$BYTE) or 0)" +ZVM.OperandFastReadFunctions[39] = "(VM:ReadCell(EDI+$BYTE) or 0)" +ZVM.OperandFastReadFunctions[40] = "(VM:ReadCell(ESP+$BYTE) or 0)" +ZVM.OperandFastReadFunctions[41] = "(VM:ReadCell(EBP+$BYTE) or 0)" +-- Register plus immediate +ZVM.OperandFastReadFunctions[42] = "(EAX+$BYTE)" +ZVM.OperandFastReadFunctions[43] = "(EBX+$BYTE)" +ZVM.OperandFastReadFunctions[44] = "(ECX+$BYTE)" +ZVM.OperandFastReadFunctions[45] = "(EDX+$BYTE)" +ZVM.OperandFastReadFunctions[46] = "(ESI+$BYTE)" +ZVM.OperandFastReadFunctions[47] = "(EDI+$BYTE)" +ZVM.OperandFastReadFunctions[48] = "(ESP+$BYTE)" +ZVM.OperandFastReadFunctions[49] = "(EBP+$BYTE)" +-- Extended registers +for reg=0,31 do ZVM.OperandFastReadFunctions[2048+reg] = "R"..reg end +for reg=0,31 do ZVM.OperandFastReadFunctions[2080+reg] = "(VM:ReadCell(R"..reg.."+$SEG) or 0)" end +for reg=0,31 do ZVM.OperandFastReadFunctions[2112+reg] = "(R"..reg.."+$SEG)" end +for reg=0,31 do ZVM.OperandFastReadFunctions[2144+reg] = "(VM:ReadCell(R"..reg.."+$BYTE) or 0)" end +for reg=0,31 do ZVM.OperandFastReadFunctions[2176+reg] = "(R"..reg.."+$BYTE)" end + + + + +-------------------------------------------------------------------------------- +-- Is byte fetch required for the RM +ZVM.NeedFetchByteLookup = {} +ZVM.NeedFetchByteLookup[0] = true +ZVM.NeedFetchByteLookup[25] = true +ZVM.NeedFetchByteLookup[34] = true +ZVM.NeedFetchByteLookup[35] = true +ZVM.NeedFetchByteLookup[36] = true +ZVM.NeedFetchByteLookup[37] = true +ZVM.NeedFetchByteLookup[38] = true +ZVM.NeedFetchByteLookup[39] = true +ZVM.NeedFetchByteLookup[40] = true +ZVM.NeedFetchByteLookup[41] = true +ZVM.NeedFetchByteLookup[42] = true +ZVM.NeedFetchByteLookup[43] = true +ZVM.NeedFetchByteLookup[44] = true +ZVM.NeedFetchByteLookup[45] = true +ZVM.NeedFetchByteLookup[46] = true +ZVM.NeedFetchByteLookup[47] = true +ZVM.NeedFetchByteLookup[48] = true +ZVM.NeedFetchByteLookup[49] = true +ZVM.NeedFetchByteLookup[50] = true + +-- Is interrupt check required for the RM +ZVM.NeedInterruptCheck = {} +ZVM.NeedInterruptCheck[17] = true +ZVM.NeedInterruptCheck[18] = true +ZVM.NeedInterruptCheck[19] = true +ZVM.NeedInterruptCheck[20] = true +ZVM.NeedInterruptCheck[21] = true +ZVM.NeedInterruptCheck[22] = true +ZVM.NeedInterruptCheck[23] = true +ZVM.NeedInterruptCheck[24] = true +ZVM.NeedInterruptCheck[25] = true +ZVM.NeedInterruptCheck[34] = true +ZVM.NeedInterruptCheck[35] = true +ZVM.NeedInterruptCheck[36] = true +ZVM.NeedInterruptCheck[37] = true +ZVM.NeedInterruptCheck[38] = true +ZVM.NeedInterruptCheck[39] = true +ZVM.NeedInterruptCheck[40] = true +ZVM.NeedInterruptCheck[41] = true +for i=1000,2023 do ZVM.NeedInterruptCheck[i] = true end +for i=2048,2207 do ZVM.NeedInterruptCheck[i] = true end + +-- Register lookup table FIXME: add segments +ZVM.NeedRegisterLookup = {} +ZVM.NeedRegisterLookup["EAX"] = 1 +ZVM.NeedRegisterLookup["EBX"] = 2 +ZVM.NeedRegisterLookup["ECX"] = 3 +ZVM.NeedRegisterLookup["EDX"] = 4 +ZVM.NeedRegisterLookup["ESI"] = 5 +ZVM.NeedRegisterLookup["EDI"] = 6 +ZVM.NeedRegisterLookup["ESP"] = 7 +ZVM.NeedRegisterLookup["EBP"] = 8 +for reg=0,31 do ZVM.NeedRegisterLookup["R"..reg] = reg+96 end + + + + +-------------------------------------------------------------------------------- +-- Lookup for LEA instruction +ZVM.OperandEffectiveAddress = {} +-- Read from memory by GP +ZVM.OperandEffectiveAddress[17] = "(VM.EAX+$SEG)" +ZVM.OperandEffectiveAddress[18] = "(VM.EBX+$SEG)" +ZVM.OperandEffectiveAddress[19] = "(VM.ECX+$SEG)" +ZVM.OperandEffectiveAddress[20] = "(VM.EDX+$SEG)" +ZVM.OperandEffectiveAddress[21] = "(VM.ESI+$SEG)" +ZVM.OperandEffectiveAddress[22] = "(VM.EDI+$SEG)" +ZVM.OperandEffectiveAddress[23] = "(VM.ESP+$SEG)" +ZVM.OperandEffectiveAddress[24] = "(VM.EBP+$SEG)" +-- Read from memory by displacement +ZVM.OperandEffectiveAddress[25] = "($BYTE+$SEG)" +-- Read by register plus immediate +ZVM.OperandEffectiveAddress[34] = "(VM.EAX+$BYTE)" +ZVM.OperandEffectiveAddress[35] = "(VM.EBX+$BYTE)" +ZVM.OperandEffectiveAddress[36] = "(VM.ECX+$BYTE)" +ZVM.OperandEffectiveAddress[37] = "(VM.EDX+$BYTE)" +ZVM.OperandEffectiveAddress[38] = "(VM.ESI+$BYTE)" +ZVM.OperandEffectiveAddress[39] = "(VM.EDI+$BYTE)" +ZVM.OperandEffectiveAddress[40] = "(VM.ESP+$BYTE)" +ZVM.OperandEffectiveAddress[41] = "(VM.EBP+$BYTE)" +-- Ports +for i=1000,2024 do ZVM.OperandEffectiveAddress[i] = -i+1000-1 end +-- Extended registers +for reg=0,31 do ZVM.OperandEffectiveAddress[2080+reg] = "(VM.R"..reg.."+$SEG)" end +for reg=0,31 do ZVM.OperandEffectiveAddress[2144+reg] = "(VM.R"..reg.."+$BYTE)" end diff --git a/lua/wire/zvm/zvm_features.lua b/lua/wire/zvm/zvm_features.lua new file mode 100644 index 0000000000..8efa2529d1 --- /dev/null +++ b/lua/wire/zvm/zvm_features.lua @@ -0,0 +1,1122 @@ +-------------------------------------------------------------------------------- +-- Zyelios VM (Zyelios CPU/GPU virtual machine) +-- +-- Implementation for most default ZCPU features (including the data bus) +-------------------------------------------------------------------------------- + + + + +-------------------------------------------------------------------------------- +-- Default configuration +ZVM.RAMSize = 65536 -- Internal RAM size +ZVM.ROMSize = 65536 -- Internal ROM size +ZVM.PCAP = 1 -- Paging capability +ZVM.RQCAP = 1 -- Memory request delaying capability +ZVM.CPUVER = 1000 -- Version reported by CPUID +ZVM.CPUTYPE = 0 -- Processor type +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 +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 + + -- 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 + + + + +-------------------------------------------------------------------------------- +-- Checks if address is valid +local function IsValidAddress(n) + return n and (math.floor(n) == n) and (n >= -140737488355327) and (n <= 140737488355328) +end + + + + +-------------------------------------------------------------------------------- +function ZVM:SignalError(errorCode) + +end + +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 +end + +function ZVM:ExternalRead(Address,Value) + if Address >= 0 + then self:Interrupt(7,Address) return -- MemBus + else return 0 -- IOBus + end +end + + + + +-------------------------------------------------------------------------------- +-- Default WritePort handler +function ZVM:WritePort(Port,Value) + self:WriteCell(-Port-1,Value) +end + + + + +-------------------------------------------------------------------------------- +-- Default ReadPort handler +function ZVM:ReadPort(Port) + return self:ReadCell(-Port-1) +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 + 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 + 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 + 15 + return self:ExternalRead(Address) + end +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 + 5 + + -- Check if address is valid + if not IsValidAddress(Address) then + self:Interrupt(15,Address) + return false + end + + -- Invalidate precompiled data + 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 + + -- 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 interrupt handled + -- Check if page is overriden + if Page.Override == 1 then + 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 + + -- 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 + 15 + return self:ExternalWrite(Address,Value) + end +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 +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 +end + + + + +-------------------------------------------------------------------------------- +-- Write value to stack (SSTACK implementation) +function ZVM:WriteToStack(Index,Value) + -- 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) +end + + + + +-------------------------------------------------------------------------------- +-- Read a value from stack (RSTACK implementation) +function ZVM:ReadFromStack(Index) + -- 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 +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, +} + +local errorPage = { + 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 +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 +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 +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 +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 +end + + + + +-------------------------------------------------------------------------------- +function ZVM:SetCurrentPage(index) + if self.PCAP == 1 then + self.CurrentPage = self:GetPageByIndex(index) + else + self.CurrentPage = defaultPage + end +end + + + + +-------------------------------------------------------------------------------- +function ZVM:SetPreviousPage(index) + if self.PCAP == 1 then + self.PreviousPage = self:GetPageByIndex(index) + else + self.PreviousPage = defaultPage + end +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 +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 +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 + local fractionalParameter = self.LADD * (10^math.floor(-math.log10(math.abs(self.LADD)+1e-12)-1)) + self:SignalError(fractionalParameter+interruptNo) + + -- 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 = 1 + 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 + + + + +-------------------------------------------------------------------------------- +-- 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 +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 +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 +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 +end + +function ZVM:ReadMatrix(address) + 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) +end + +function ZVM:WriteVector3f(address,vector) + 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) +end + +function ZVM:WriteMatrix(address,matrix) + for i=0,15 do self:WriteCell(address+i,matrix[i]) end +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 +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 +end + + + + +-------------------------------------------------------------------------------- +-- Binary OR +function ZVM:BinaryOr(m,n) + 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 + + return self:BinaryToInteger(bits) +end + + + + +-------------------------------------------------------------------------------- +-- Binary AND +function ZVM:BinaryAnd(m,n) + 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 + + return self:BinaryToInteger(bits) +end + + + + +-------------------------------------------------------------------------------- +-- Binary NOT +function ZVM:BinaryNot(n) + 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) +end + + + + +-------------------------------------------------------------------------------- +-- Binary XOR +function ZVM:BinaryXor(m,n) + 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 + + return self:BinaryToInteger(bits) +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) +end + + + + +-------------------------------------------------------------------------------- +-- Binary shift left +function ZVM:BinarySHL(n,cnt) + 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 + + return self:BinaryToInteger(bits) +end + + + + +-------------------------------------------------------------------------------- +-- Reset to initial state +ZVM:Reset() diff --git a/lua/wire/zvm/zvm_opcodes.lua b/lua/wire/zvm/zvm_opcodes.lua new file mode 100644 index 0000000000..28a44ce008 --- /dev/null +++ b/lua/wire/zvm/zvm_opcodes.lua @@ -0,0 +1,1657 @@ +-------------------------------------------------------------------------------- +-- Zyelios VM (Zyelios CPU/GPU virtual machine) +-- +-- Primary opcode set +-------------------------------------------------------------------------------- + + + + +-- Initialize opcode count lookup table +ZVM.OperandCount = {} +for _,instruction in pairs(CPULib.InstructionTable) do + ZVM.OperandCount[instruction.Opcode] = instruction.OperandCount +end + + + + +-- Initialize runlevel lookup table +ZVM.OpcodeRunLevel = {} +for _,instruction in pairs(CPULib.InstructionTable) do + if instruction.Privileged then + ZVM.OpcodeRunLevel[instruction.Opcode] = 0 + end +end + + + + +-------------------------------------------------------------------------------- +-- Hand-leg guide to writing ZCPU microcode +-- self:Dyn_Emit(code) +-- Emits microcode to output stream +-- +-- self:Dyn_EmitOperand(OP,code,emitNow) +-- self:Dyn_EmitOperand(code) +-- Emits write to specific operand (if no operant specified - then operand #1) +-- Passing emitNow as true will emit operand at the current spot in microcode, +-- and set it later (must be done if operand is set inside block). +-- +-- self:Dyn_EmitState() +-- Pushes state update - all global registers are set to their local values +-- +-- self:Dyn_EmitBreak(emitIP) +-- Emits a return (does not push state). Emits new IP if required +-- +-- self:Dyn_EmitForceRegisterGlobal(register) +-- Forces a specific register to be global (so VM.EAX has valid value) +-- +-- self:Dyn_EmitForceRegisterLocal(register) +-- Forces a specific register to be local (so EAX has valid value) +-- Marks register as changed +-- +-- self:Dyn_EmitRegisterValueChanged(register) +-- Marks that there now exists local state for the register +-- +-- self:Dyn_EmitInterrupt(intNo,intParam) +-- Emits interrupt call +-- +-- self:Dyn_EmitInterruptCheck() +-- Emits interrupt check + + + +-------------------------------------------------------------------------------- +-- Load all opcodes +ZVM.OpcodeTable = {} + +ZVM.OpcodeTable[0] = function(self) --END (STOP) + 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") +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 +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") +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") +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.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") +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") +end +ZVM.OpcodeTable[8] = function(self) --CPUID + 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() +end +-------------------------------------------------------------------------------- +ZVM.OpcodeTable[10] = function(self) --ADD + self:Dyn_EmitOperand("$1 + $2") +end +ZVM.OpcodeTable[11] = function(self) --SUB + self:Dyn_EmitOperand("$1 - $2") +end +ZVM.OpcodeTable[12] = function(self) --MUL + 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") +end +ZVM.OpcodeTable[14] = function(self) --MOV + self:Dyn_EmitOperand("$2") +end +ZVM.OpcodeTable[15] = function(self) --CMP + 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_Emit("if VM.Memory[OP] then") + self:Dyn_Emit("ANS = VM.Memory[OP]") + self:Dyn_Emit("end") +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") +end +ZVM.OpcodeTable[18] = function(self) --MIN + self:Dyn_EmitOperand("math.min($1,$2)") +end +ZVM.OpcodeTable[19] = function(self) --MAX + self:Dyn_EmitOperand("math.max($1,$2)") +end +-------------------------------------------------------------------------------- +ZVM.OpcodeTable[20] = function(self) --INC + self:Dyn_EmitOperand("$1 + 1") +end +ZVM.OpcodeTable[21] = function(self) --DEC + self:Dyn_EmitOperand("$1 - 1") +end +ZVM.OpcodeTable[22] = function(self) --NEG + self:Dyn_EmitOperand("-$1") +end +ZVM.OpcodeTable[23] = function(self) --RAND + 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") +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") +end +ZVM.OpcodeTable[26] = function(self) --LOOPB + self:Dyn_EmitForceRegisterLocal("EBX") + self:Dyn_Emit("EBX = EBX - 1") + self:Dyn_Emit("if VM.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") +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") +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 <= VM.Page[idx].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() +end +ZVM.OpcodeTable[31] = function(self) --CALL + self:Dyn_EmitForceRegisterGlobal("ESP") + self:Dyn_Emit("if VM:Push("..self.PrecompileIP..") then") + self:Dyn_Emit("VM:Jump($1)") + self:Dyn_EmitState() + self:Dyn_EmitBreak() + self:Dyn_Emit("end") + self:Dyn_EmitInterruptCheck() + + self.PrecompileBreak = true +end +ZVM.OpcodeTable[32] = function(self) --BNOT + self:Dyn_EmitOperand("VM:BinaryNot($1)") +end +ZVM.OpcodeTable[33] = function(self) --FINT + self:Dyn_EmitOperand("math.floor($1)") +end +ZVM.OpcodeTable[34] = function(self) --RND + 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)") +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") +end +ZVM.OpcodeTable[37] = function(self) --HALT + self:Dyn_Emit("VM.HaltPort = math.floor($1)") +end +ZVM.OpcodeTable[38] = function(self) --FSHL + self:Dyn_EmitOperand("$1 * 2") +end +ZVM.OpcodeTable[39] = function(self) --FSHR + 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.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 +end +ZVM.OpcodeTable[42] = function(self) --STI + self:Dyn_Emit("VM.NIF = 1") +end +ZVM.OpcodeTable[43] = function(self) --CLI + self:Dyn_Emit("VM.IF = 0") +end +ZVM.OpcodeTable[44] = function(self) --STP + self:Dyn_Emit("VM.PF = 1") +end +ZVM.OpcodeTable[45] = function(self) --CLP + self:Dyn_Emit("VM.PF = 0") +end +ZVM.OpcodeTable[46] = function(self) --STD + 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.PrecompileBreak = true +end +ZVM.OpcodeTable[48] = function(self) --STEF + self:Dyn_Emit("VM.EF = 1") +end +ZVM.OpcodeTable[49] = function(self) --CLEF + 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") +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") +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") +end +ZVM.OpcodeTable[53] = function(self) --FSIN + self:Dyn_EmitOperand("math.sin($2)") +end +ZVM.OpcodeTable[54] = function(self) --FCOS + self:Dyn_EmitOperand("math.cos($2)") +end +ZVM.OpcodeTable[55] = function(self) --FTAN + self:Dyn_EmitOperand("math.tan($2)") +end +ZVM.OpcodeTable[56] = function(self) --FASIN + self:Dyn_EmitOperand("math.asin($2)") +end +ZVM.OpcodeTable[57] = function(self) --FACOS + self:Dyn_EmitOperand("math.acos($2)") +end +ZVM.OpcodeTable[58] = function(self) --FATAN + self:Dyn_EmitOperand("math.atan($2)") +end +ZVM.OpcodeTable[59] = function(self) --MOD + self:Dyn_EmitOperand("math.fmod($1,$2)") +end +-------------------------------------------------------------------------------- +ZVM.OpcodeTable[60] = function(self) --BIT + 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") +end +ZVM.OpcodeTable[61] = function(self) --SBIT + 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") +end +ZVM.OpcodeTable[62] = function(self) --CBIT + 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") +end +ZVM.OpcodeTable[63] = function(self) --TBIT + 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") +end +ZVM.OpcodeTable[64] = function(self) --BAND + self:Dyn_EmitOperand("VM:BinaryAnd($1,$2)") + self:Dyn_Emit("VM.TMR = VM.TMR + 30") +end +ZVM.OpcodeTable[65] = function(self) --BOR + self:Dyn_EmitOperand("VM:BinaryOr($1,$2)") + self:Dyn_Emit("VM.TMR = VM.TMR + 30") +end +ZVM.OpcodeTable[66] = function(self) --BXOR + self:Dyn_EmitOperand("VM:BinaryXor($1,$2)") + self:Dyn_Emit("VM.TMR = VM.TMR + 30") +end +ZVM.OpcodeTable[67] = function(self) --BSHL + self:Dyn_EmitOperand("VM:BinarySHL($1,$2)") + self:Dyn_Emit("VM.TMR = VM.TMR + 30") +end +ZVM.OpcodeTable[68] = function(self) --BSHR + self:Dyn_EmitOperand("VM:BinarySHR($1,$2)") + self:Dyn_Emit("VM.TMR = VM.TMR + 30") +end +ZVM.OpcodeTable[69] = function(self) --JMPF + 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 = "..(self.PrecompileIP or 0)) + self:Emit("VM.XEIP = "..(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("..self.PrecompileIP..") then") + 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("..self.PrecompileIP..") then") + 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("..self.PrecompileIP..") then") + 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("..self.PrecompileIP..") then") + 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("..self.PrecompileIP..") then") + 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("..self.PrecompileIP..") then") + 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("..self.PrecompileIP..") then") + 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("for i = 1,math.Clamp($1,0,8192) do") + self:Dyn_Emit("$L VAL") + 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_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") +end +-------------------------------------------------------------------------------- +ZVM.OpcodeTable[80] = function(self) --FPWR + 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") +end +ZVM.OpcodeTable[82] = function(self) --FLOG + self:Dyn_EmitOperand("math.log($2)") +end +ZVM.OpcodeTable[83] = function(self) --FLOG10 + self:Dyn_EmitOperand("math.log10($2)") +end +ZVM.OpcodeTable[84] = function(self) --IN + 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() +end +ZVM.OpcodeTable[86] = function(self) --FABS + 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") +end +ZVM.OpcodeTable[88] = function(self) --FEXP + 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("..self.PrecompileIP..") then") + self:Dyn_Emit("VM:Jump($1,$2)") + self:Dyn_EmitState() + self:Dyn_EmitBreak() + self:Dyn_Emit("end") + self:Dyn_EmitInterruptCheck() + + self.PrecompileBreak = true +end +-------------------------------------------------------------------------------- +ZVM.OpcodeTable[90] = function(self) --FPI + self:Dyn_EmitOperand("3.141592653589793") +end +ZVM.OpcodeTable[91] = function(self) --FE + self:Dyn_EmitOperand("2.718281828459045") +end +ZVM.OpcodeTable[92] = function(self) --INT + 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") +end +ZVM.OpcodeTable[94] = function(self) --FCEIL + 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") +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") +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") +end +ZVM.OpcodeTable[98] = function(self) --TIMER + self:Dyn_EmitOperand("(VM.TIMER+"..(self.PrecompileInstruction or 0).."*VM.TimerDT)") +end +ZVM.OpcodeTable[99] = function(self) --LIDTR + self:Dyn_Emit("VM.IDTR = $1") +end +-------------------------------------------------------------------------------- +ZVM.OpcodeTable[100] = function(self) --STATESTORE + self:Dyn_EmitState() + self:Dyn_Emit("VM:WriteCell($1 + 00,"..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("..self.PrecompileIP.." + $1)") + self:Dyn_EmitState() + self:Dyn_EmitBreak() + self:Dyn_Emit("end") +end +ZVM.OpcodeTable[102] = function(self) --JMPR + self:Dyn_Emit("VM:Jump("..self.PrecompileIP.." + $1)") + self:Dyn_EmitState() + self:Dyn_EmitBreak() + + self.PrecompileBreak = true +end +ZVM.OpcodeTable[103] = function(self) --JGR + self:Dyn_Emit("if VM.CMPR > 0 then") + self:Dyn_Emit("VM:Jump("..self.PrecompileIP.." + $1)") + 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("..self.PrecompileIP.." + $1)") + 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("..self.PrecompileIP.." + $1)") + 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("..self.PrecompileIP.." + $1)") + 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("..self.PrecompileIP.." + $1)") + 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)") +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") +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 +end +ZVM.OpcodeTable[111] = function(self) --IDLE + 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") +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() +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") +end +ZVM.OpcodeTable[116] = function(self) --STD2 + 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_Emit("EBP = VM:Pop()") + self:Dyn_EmitInterruptCheck() + self:Dyn_EmitRegisterValueChanged("EBP") +end +ZVM.OpcodeTable[118] = function(self) --STM + self:Dyn_Emit("VM.MF = 1") +end +ZVM.OpcodeTable[119] = function(self) --CLM + 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_Emit("if VM.InternalRegister[REG] then") + self:Dyn_Emit("OP = VM[VM.InternalRegister[REG]]") + self:Dyn_Emit("end") +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("VM[VM.InternalRegister[REG]] = limit and math.Clamp(OP, limit[1], limit[2]) or OP") + self:Dyn_Emit("if (REG == 0) or (REG == 16) then") + self:Dyn_Emit("VM:Jump("..self.PrecompileIP..",VM.CS)") + 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") +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") +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") +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") +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.gsub(emitText,"$SEG","VM."..(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") +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") +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") +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") +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("if VM.IsAddressPrecompiled[address] then") + self:Dyn_Emit("for k,v in ipairs(VM.IsAddressPrecompiled[address]) do") + self:Dyn_Emit("VM.PrecompiledData[v] = nil") + self:Dyn_Emit("VM.IsAddressPrecompiled[address][k] = nil") + self:Dyn_Emit("end") + self:Dyn_Emit("end") + 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_Emit("$L IDX = math.floor(ADDR / 128)") + self:Dyn_Emit("$L PAGE = VM:GetPageByIndex(IDX)") + self:Dyn_EmitInterruptCheck() + self:Dyn_EmitOperand("PAGE.MappedIndex") +end +ZVM.OpcodeTable[133] = function(self) --RSTACK + 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() +end +ZVM.OpcodeTable[135] = function(self) --ENTER + 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") +end +ZVM.OpcodeTable[136] = function(self) --IRETP + 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 +end +ZVM.OpcodeTable[139] = function(self) --CLD + 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"..i.." = 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 +end + + +-------------------------------------------------------------------------------- +ZVM.OpcodeTable[250] = function(self) --VADD + self:Dyn_Emit("if VM.VMODE == 2 then") + self:Dyn_Emit("$L V1 = VM:ReadVector2f($1 + VM."..(self.EmitOperandSegment[1] or "DS")..")") + self:Dyn_Emit("$L V2 = VM:ReadVector2f($2 + VM."..(self.EmitOperandSegment[2] or "DS")..")") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:WriteVector2f($1 + VM."..(self.EmitOperandSegment[1] or "DS")..",") + 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 + VM."..(self.EmitOperandSegment[1] or "DS")..")") + self:Dyn_Emit("$L V2 = VM:ReadVector3f($2 + VM."..(self.EmitOperandSegment[2] or "DS")..")") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:WriteVector3f($1 + VM."..(self.EmitOperandSegment[1] or "DS")..",") + 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 + self:Dyn_Emit("if VM.VMODE == 2 then") + self:Dyn_Emit("$L V1 = VM:ReadVector2f($1 + VM."..(self.EmitOperandSegment[1] or "DS")..")") + self:Dyn_Emit("$L V2 = VM:ReadVector2f($2 + VM."..(self.EmitOperandSegment[2] or "DS")..")") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:WriteVector2f($1 + VM."..(self.EmitOperandSegment[1] or "DS")..",") + 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 + VM."..(self.EmitOperandSegment[1] or "DS")..")") + self:Dyn_Emit("$L V2 = VM:ReadVector3f($2 + VM."..(self.EmitOperandSegment[2] or "DS")..")") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:WriteVector3f($1 + VM."..(self.EmitOperandSegment[1] or "DS")..",") + 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 + self:Dyn_Emit("if VM.VMODE == 2 then") + self:Dyn_Emit("$L V1 = VM:ReadVector2f($1 + VM."..(self.EmitOperandSegment[1] or "DS")..")") + self:Dyn_Emit("$L V2 = $2") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:WriteVector2f($1 + VM."..(self.EmitOperandSegment[1] or "DS")..",") + 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 + VM."..(self.EmitOperandSegment[1] or "DS")..")") + self:Dyn_Emit("$L V2 = $2") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:WriteVector3f($1 + VM."..(self.EmitOperandSegment[1] or "DS")..",") + 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 + self:Dyn_Emit("if VM.VMODE == 2 then") + self:Dyn_Emit("$L V1 = VM:ReadVector2f($1 + VM."..(self.EmitOperandSegment[1] or "DS")..")") + self:Dyn_Emit("$L V2 = VM:ReadVector2f($2 + VM."..(self.EmitOperandSegment[2] or "DS")..")") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:WriteCell($1 + VM."..self.EmitOperandSegment[1]..",") + 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 + VM."..(self.EmitOperandSegment[1] or "DS")..")") + self:Dyn_Emit("$L V2 = VM:ReadVector3f($2 + VM."..(self.EmitOperandSegment[2] or "DS")..")") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:WriteCell($1 + VM."..(self.EmitOperandSegment[1] or "DS")..",") + 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 + self:Dyn_Emit("if VM.VMODE == 2 then") + self:Dyn_Emit("$L V1 = VM:ReadVector2f($1 + VM."..(self.EmitOperandSegment[1] or "DS")..")") + self:Dyn_Emit("$L V2 = VM:ReadVector2f($2 + VM."..(self.EmitOperandSegment[2] or "DS")..")") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:WriteCell($1 + VM."..(self.EmitOperandSegment[1] or "DS")..",") + 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 + VM."..(self.EmitOperandSegment[1] or "DS")..")") + self:Dyn_Emit("$L V2 = VM:ReadVector3f($2 + VM."..(self.EmitOperandSegment[2] or "DS")..")") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:WriteVector3f($1 + VM."..(self.EmitOperandSegment[1] or "DS")..",") + 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 + self:Dyn_Emit("if VM.VMODE == 2 then") + self:Dyn_Emit("$L V = VM:ReadVector2f($2 + VM."..(self.EmitOperandSegment[2] or "DS")..")") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:WriteVector2f($1 + VM."..(self.EmitOperandSegment[1] or "DS")..",V)") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("elseif VM.VMODE == 3 then") + self:Dyn_Emit("$L V = VM:ReadVector3f($2 + VM."..(self.EmitOperandSegment[2] or "DS")..")") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:WriteVector3f($1 + VM."..(self.EmitOperandSegment[1] or "DS")..",V)") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("end") +end +ZVM.OpcodeTable[256] = function(self) --VNORM + self:Dyn_Emit("if VM.VMODE == 2 then") + self:Dyn_Emit("$L V = VM:ReadVector2f($2 + VM."..(self.EmitOperandSegment[2] or "DS")..")") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("$L D = (V.x^2+V.y^2)^(1/2)+1e-8") + self:Dyn_Emit("VM:WriteVector2f($1 + VM."..(self.EmitOperandSegment[1] or "DS")..",") + 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 + VM."..(self.EmitOperandSegment[2] or "DS")..")") + 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 + VM."..(self.EmitOperandSegment[1] or "DS")..",") + 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 + self:Dyn_Emit("$L V = VM:ReadVector4f($2 + VM."..(self.EmitOperandSegment[2] or "DS")..")") + 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 + VM."..(self.EmitOperandSegment[1] or "DS")..",V)") + self:Dyn_EmitInterruptCheck() +end +ZVM.OpcodeTable[259] = function(self) --LOOPXY + self:Dyn_EmitForceRegisterLocal("ECX") + self:Dyn_EmitForceRegisterLocal("EDX") + +-- self:Dyn_Emit(" +end +-------------------------------------------------------------------------------- +ZVM.OpcodeTable[260] = function(self) --MADD + self:Dyn_Emit("$L M1 = VM:ReadMatrix($1 + VM."..(self.EmitOperandSegment[1] or "DS")..")") + self:Dyn_Emit("$L M2 = VM:ReadMatrix($2 + VM."..(self.EmitOperandSegment[2] or "DS")..")") + 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("VM:WriteMatrix($1 + VM."..(self.EmitOperandSegment[1] or "DS")..",RM)") + self:Dyn_EmitInterruptCheck() +end +ZVM.OpcodeTable[261] = function(self) --MSUB + self:Dyn_Emit("$L M1 = VM:ReadMatrix($1 + VM."..(self.EmitOperandSegment[1] or "DS")..")") + self:Dyn_Emit("$L M2 = VM:ReadMatrix($2 + VM."..(self.EmitOperandSegment[2] or "DS")..")") + 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("VM:WriteMatrix($1 + VM."..(self.EmitOperandSegment[1] or "DS")..",RM)") + self:Dyn_EmitInterruptCheck() +end +ZVM.OpcodeTable[262] = function(self) --MMUL + self:Dyn_Emit("$L M1 = VM:ReadMatrix($1 + VM."..(self.EmitOperandSegment[1] or "DS")..")") + self:Dyn_Emit("$L M2 = VM:ReadMatrix($2 + VM."..(self.EmitOperandSegment[2] or "DS")..")") + 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 + VM."..(self.EmitOperandSegment[1] or "DS")..",RM)") + self:Dyn_EmitInterruptCheck() +end +ZVM.OpcodeTable[263] = function(self) --MROTATE + self:Dyn_Emit("$L VEC = VM:ReadVector4f($2 + VM."..(self.EmitOperandSegment[2] or "DS")..")") + 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 + VM."..(self.EmitOperandSegment[1] or "DS")..",RM)") + self:Dyn_EmitInterruptCheck() +end +ZVM.OpcodeTable[264] = function(self) --MSCALE + self:Dyn_Emit("$L VEC = VM:ReadVector3f($2 + VM."..(self.EmitOperandSegment[2] or "DS")..")") + 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 + VM."..(self.EmitOperandSegment[1] or "DS")..",RM)") + self:Dyn_EmitInterruptCheck() +end +ZVM.OpcodeTable[265] = function(self) --MPERSPECTIVE + self:Dyn_Emit("$L VEC = VM:ReadVector4f($2 + VM."..(self.EmitOperandSegment[2] or "DS")..")") + 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 + VM."..(self.EmitOperandSegment[1] or "DS")..",RM)") + self:Dyn_EmitInterruptCheck() +end +ZVM.OpcodeTable[266] = function(self) --MTRANSLATE + self:Dyn_Emit("$L VEC = VM:ReadVector3f($2 + VM."..(self.EmitOperandSegment[2] or "DS")..")") + 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 + VM."..(self.EmitOperandSegment[1] or "DS")..",RM)") + self:Dyn_EmitInterruptCheck() +end +ZVM.OpcodeTable[267] = function(self) --MLOOKAT + self:Dyn_Emit("$L EYE = VM:ReadVector3f($2 + VM."..(self.EmitOperandSegment[2] or "DS").."+0)") + self:Dyn_Emit("$L CENTER = VM:ReadVector3f($2 + VM."..(self.EmitOperandSegment[2] or "DS").."+3)") + self:Dyn_Emit("$L UP = VM:ReadVector3f($2 + VM."..(self.EmitOperandSegment[2] or "DS").."+6)") + 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 + VM."..(self.EmitOperandSegment[1] or "DS")..",RM)") + self:Dyn_EmitInterruptCheck() +end +ZVM.OpcodeTable[268] = function(self) --MMOV + self:Dyn_Emit("$L M = VM:ReadMatrix($2 + VM."..(self.EmitOperandSegment[2] or "DS")..")") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("VM:WriteMatrix($1 + VM."..(self.EmitOperandSegment[1] or "DS")..",M)") + self:Dyn_EmitInterruptCheck() +end +ZVM.OpcodeTable[269] = function(self) --VLEN + self:Dyn_Emit("if VM.VMODE == 2 then") + self:Dyn_Emit("$L V = VM:ReadVector2f($2 + VM."..(self.EmitOperandSegment[2] or "DS")..")") + 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 + VM."..(self.EmitOperandSegment[2] or "DS")..")") + 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 + 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 + VM."..(self.EmitOperandSegment[1] or "DS")..",M)") + self:Dyn_EmitInterruptCheck() +end +ZVM.OpcodeTable[273] = function(self) --VMODE + self:Dyn_Emit("VM.VMODE = $1") +end +-------------------------------------------------------------------------------- +ZVM.OpcodeTable[295] = function(self) --VDIV + self:Dyn_Emit("$L SCALAR = $2") + self:Dyn_Emit("if VM.VMODE == 2 then") + self:Dyn_Emit("$L V = VM:ReadVector2f($1 + VM."..(self.EmitOperandSegment[1] or "DS")..")") + 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 + VM."..(self.EmitOperandSegment[1] or "DS")..",V)") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("elseif VM.VMODE == 3 then") + self:Dyn_Emit("$L V = VM:ReadVector3f($1 + VM."..(self.EmitOperandSegment[1] or "DS")..")") + 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 + VM."..(self.EmitOperandSegment[1] or "DS")..",V)") + self:Dyn_EmitInterruptCheck() + self:Dyn_Emit("end") +end +ZVM.OpcodeTable[296] = function(self) --VTRANSFORM + self:Dyn_Emit("local address_1 = $1 + VM."..(self.EmitOperandSegment[1] or "DS")) + self:Dyn_Emit("local address_2 = $2 + VM."..(self.EmitOperandSegment[2] or "DS")) + 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 From 8b159225e4ccad04708731aa60c8dad57663de22 Mon Sep 17 00:00:00 2001 From: bjcscat Date: Tue, 31 Oct 2023 12:12:59 -0500 Subject: [PATCH 2/3] add some stuff to flashing --- lua/entities/gmod_wire_cpu.lua | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lua/entities/gmod_wire_cpu.lua b/lua/entities/gmod_wire_cpu.lua index 5ab5a54f81..c043609c24 100644 --- a/lua/entities/gmod_wire_cpu.lua +++ b/lua/entities/gmod_wire_cpu.lua @@ -205,10 +205,12 @@ end -- Write data to RAM and then flash ROM if required function ENT:FlashData(data) self.VM:Reset() - for k,v in pairs(data) do - self.VM:WriteCell(k,tonumber(v) or 0) - if (k >= 0) and (k < self.VM.ROMSize) then - self.VM.ROM[k] = tonumber(v) or 0 + for addr, v in pairs(data) do + addr = math.floor(addr) + + self.VM:WriteCell(addr,tonumber(v) or 0) + if (addr >= 0) and (addr < self.VM.ROMSize) then + self.VM.ROM[addr] = tonumber(v) or 0 end end end From 427e89eacd4fc66b26d46d78380683bd8a06ceda Mon Sep 17 00:00:00 2001 From: bjcscat Date: Tue, 31 Oct 2023 14:03:47 -0500 Subject: [PATCH 3/3] Fix linter warnings --- lua/wire/cpulib.lua | 4 ++-- lua/wire/stools/cpu.lua | 10 ++++------ lua/wire/stools/gpu.lua | 3 +-- lua/wire/stools/spu.lua | 3 +-- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/lua/wire/cpulib.lua b/lua/wire/cpulib.lua index 0dce233ad5..e4786e98f0 100644 --- a/lua/wire/cpulib.lua +++ b/lua/wire/cpulib.lua @@ -120,7 +120,7 @@ if CLIENT then local line, char = 0, 0 - if errorPos then + if errorPos then line = errorPos.Line char = errorPos.Col @@ -734,7 +734,7 @@ local sessionBase, sessionDate function CPULib.GenerateSN(entityType) local currentDate = os.date("*t") - local SNDate = (currentDate.year-2007)*500+(currentDate.yday) + local SNDate = (currentDate.year-2007)*500+currentDate.yday if (not sessionBase) or (SNDate ~= sessionDate) then sessionBase = math.floor(math.random()*99999) sessionDate = SNDate diff --git a/lua/wire/stools/cpu.lua b/lua/wire/stools/cpu.lua index 2d842611b9..0aa20f0524 100644 --- a/lua/wire/stools/cpu.lua +++ b/lua/wire/stools/cpu.lua @@ -136,7 +136,7 @@ if CLIENT then ---------------------------------------------------------------------------- - local currentDirectory + local FileBrowser = vgui.Create("wire_expression2_browser" , panel) panel:AddPanel(FileBrowser) FileBrowser:Setup("cpuchip") @@ -174,7 +174,7 @@ if CLIENT then ---------------------------------------------------------------------------- - local modelPanel = WireDermaExts.ModelSelect(panel, "wire_cpu_model", list.Get("Wire_gate_Models"), 2) + WireDermaExts.ModelSelect(panel, "wire_cpu_model", list.Get("Wire_gate_Models"), 2) panel:AddControl("Label", {Text = ""}) @@ -291,12 +291,12 @@ if CLIENT then 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) + 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) + 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)) @@ -387,8 +387,6 @@ if CLIENT 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 diff --git a/lua/wire/stools/gpu.lua b/lua/wire/stools/gpu.lua index a663b6f140..cf3a8906bd 100644 --- a/lua/wire/stools/gpu.lua +++ b/lua/wire/stools/gpu.lua @@ -38,7 +38,6 @@ if SERVER then 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")) @@ -117,7 +116,7 @@ if CLIENT then ---------------------------------------------------------------------------- - local currentDirectory + local FileBrowser = vgui.Create("wire_expression2_browser" , panel) panel:AddPanel(FileBrowser) FileBrowser:Setup("GPUChip") diff --git a/lua/wire/stools/spu.lua b/lua/wire/stools/spu.lua index 6fc663cdf3..c56c1b6751 100644 --- a/lua/wire/stools/spu.lua +++ b/lua/wire/stools/spu.lua @@ -37,7 +37,6 @@ if SERVER then 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")) @@ -122,7 +121,7 @@ if CLIENT then ---------------------------------------------------------------------------- - local currentDirectory + local FileBrowser = vgui.Create("wire_expression2_browser" , panel) panel:AddPanel(FileBrowser) FileBrowser:Setup("spuchip")