Created
October 3, 2015 20:33
-
-
Save katlogic/c0df66ba2e1e16f44ab6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #if 0 //To compile as a stand-alone test program, type "nmake eval.c" | |
| !ifndef COMP | |
| COMP=1 | |
| !endif | |
| eval.exe: eval.c; cl eval.c /Ox /G6Fy /Gs /MD /nologo /DEVALTEST /DCOMPILE=$(COMP) /link /opt:nowin98 /nologo | |
| del eval.obj | |
| !if 0 | |
| #endif | |
| #if 0 | |
| ============================================================================== | |
| Eval general todo: | |
| ! make params passed as pointers not require declaration&init | |
| ! return start&end index in original text buffer of errors | |
| * support: func(&buf[var]) | |
| * optimization bug: eval "static j;(){j=1;i=func(j);j=0;i}func(){j}" | |
| * Add relocation function for user.. or: call [rel0]; pop edx; add edx, ?; | |
| * Unusual behavior: rounding functions return ~9.2e18 when infinity is input: | |
| printf("%f %f %f",ceil(rnd/0),floor(rnd/0),int(rnd/0)); | |
| * ability to define arrays on local stack using 'auto' keyword (for multithread) | |
| * implement C functions: rand(), abort(), log10(), cosh(), sinh(), tanh() | |
| * implement switch statement&associated syntax (case, default) | |
| * type declarations: double, float, (int alias for long), long, short, char | |
| Precedence: double, float, __uint64, __int64, ulong, long, ushort, short, uchar, char | |
| * function domain problems: | |
| /,%,FMOD slow for 0 but correct | |
| ACOS/ASIN (x < -1) or (x > 1) slow but correct | |
| EXP (very high/low numbers slow but correct) | |
| SQRT/LOG (negative numbers slow but correct) | |
| * Multithread problems (due to writing globals): RND,NRND | |
| * USERFUNC save/restore only necessary FPU registers (fix fldz/ffree stuff) | |
| * Write machine code primitives for PPC ... very low priority :P | |
| * Functions not ideally implemented on Linux: cpuid/testflag, kpow/krand/fact | |
| * KASM87C problem with inst6.kc: exp(-1/x) = 0 in KASM87 | |
| exp(-1/x) = -inf in KASM87C | |
| ============================================================================== | |
| 04/17/2003. Ken Silverman`s x87 expression compiler. This code takes a math | |
| expression in the form of a string, compiles it to (somewhat) optimized x87 | |
| code, and returns a function pointer so you can call it freely from your C | |
| code. When no longer needed, call `kasm87_free` to free the function's | |
| memory. All parameters are passed as double-precision floating point. The | |
| return value is also double. | |
| Currently supported operators, functions, and statements: | |
| Parenthesis: (), Arrays: [], Blocks: {}, Const strings: "", Literal '"': \" | |
| Assignment: = *= /= %= += -= ++ -- (only 1 allowed per statement) | |
| 1-Param Operators: + - . 0 1 2 3 4 5 6 7 8 9 E PI NRND RND (variable names) | |
| 2-Param Operators: ^ * / % + - < <= > >= == != && || | |
| 1-Param Functions: ABS ACOS ASIN ATAN ATN CEIL COS EXP FABS FACT FLOOR INT | |
| LOG SGN SIN SQRT TAN UNIT | |
| 2-Param Functions: ATAN2 FADD FMOD LOG MIN MAX POW | |
| Statements: IF(expr){codetrue} | |
| IF(expr){codetrue}ELSE{codefalse} | |
| DO{code}WHILE(expr); | |
| WHILE(expr){code} | |
| FOR(precode;expr;postcode){code} | |
| GOTO label; | |
| RETURN expr; | |
| BREAK; | |
| CONTINUE; | |
| ENUM{name(=expr),name(=expr),...}; | |
| STATIC name[expr]["...],name["],...; | |
| label: | |
| Comments: // text (CR), /* text */ | |
| Requirements: | |
| CPU: Pentium or above | |
| OS: Microsoft Windows 98/ME/2K/XP | |
| Compiler: Microsoft Visual C/C++ 6.0 or above. | |
| Compiling: | |
| At the command prompt, type "nmake eval.c". Or if you prefer the VC | |
| windows environment, select "Win32 Console application" and make sure | |
| "EVALTEST" is defined in the code. You can either do this in the makefile | |
| with the /D option or as a #define at the top of the program. | |
| It is easy to make this an externally callable library. Just copy the | |
| following function declarations into your code and make sure EVALTEST is | |
| NOT defined: | |
| //function: | |
| // This is your function. The formatting is similar to C syntax, except | |
| // for these differences: | |
| // | |
| // * Function name should be left blank | |
| // * Function parameters support the following types: | |
| // | |
| // kasm87 syntax: Equivalent C syntax: Description: | |
| // a double a pass-by-value variable | |
| // &a double &a pointer to double | |
| // a[10] double a[10] pointer to array of doubles | |
| // a() double (*a)(double) function pointer, 1 param | |
| // a(,) double (*a)(double,double) function pointer, 2 params... | |
| // a(,,) etc... | |
| // | |
| // * Array indices must be constants or enum names. | |
| // * Function pointers must only have pass-by-value double parameters | |
| // * Type declarations are not allowed in the function body. Any new | |
| // variables are assumed to be `double` | |
| // * Use int() function to round towards 0. | |
| // * No {} needed around function body. Code begins after the first () | |
| // * The last expression is the return value and it doesn`t need a ; | |
| // * Switch statement and associated syntax (case, default) not yet supported. | |
| // | |
| // You can pass any number of variables to your function. With this, you | |
| // specify the names and the order of your variables which are found | |
| // inside mathexpression. Here`s an example: | |
| // | |
| // "(x,y,z)sqrt(x*x+y*y+z*z)" | |
| // | |
| // The (x,y,z) tells kasm87 that the function will have 3 parameters, | |
| // with the first parameter called "x", etc... | |
| // | |
| //Returns either: | |
| // 1. Pointer to the newly generated C function (__cdecl format) | |
| // 2. NULL pointer if there was an error in parsing. | |
| extern void *kasm87 (char *function); | |
| //Finds index to '(' of 1st function. -1 if simple form (no function blocks) | |
| extern long kasm87_findfirstfuncparen (char *function); | |
| //If kasm87 returns a NULL pointer, an error string is stored in kasm87err. | |
| extern char kasm87err[256]; | |
| //The number of bytes allocated by the malloc in kasm87; error text markers | |
| extern long kasm87leng, kasm87err0, kasm87err1; | |
| //mode=0: overwrite backward jumps to 0's | |
| //mode=1: restore backward jumps | |
| extern void kasm87jumpback (void *, long mode); | |
| //Free memory of compiled function (does nothing if using kasm87c) | |
| extern void kasm87free (void *); | |
| extern void kasm87freeall (); | |
| //Returns string of compiled code based on showflags (call after kasm87) | |
| //Showflags: | |
| // 1: pseudo-asm | |
| // 2: machine code bytes | |
| // (4: Intel asm) | |
| extern void kasm87_showdebug (long showflags, char *debuf, long debuflng); | |
| //Specify list of external functions&variables to be recognized by future kasm87() calls. | |
| //With this, you no longer need to simulate global functions/variables by passing them as pointers. | |
| typedef struct { char *nam; long *ptr; } evalextyp; | |
| extern void kasm87addext (evalextyp *daeet, long n); | |
| //Note to self: How to relocate kasm87-generated code: | |
| myfunc2 = (double (*)(double,...))malloc(FUNCBYTEOFFS+kasm87leng); | |
| memcpy(myfunc2,myfunc,FUNCBYTEOFFS+kasm87leng); | |
| //kasm87-generated code is fully re-locatable except for this 1 necessary hack: | |
| //If 1st line is "mov edx, imm32", adjust offset for new code (actually data) offset | |
| if (((char *)myfunc2)[FUNCBYTEOFFS] == 0xba) | |
| *(long *)(((long)myfunc2)+FUNCBYTEOFFS+1) += ((long)myfunc2)-((long)myfunc); | |
| Speed analysis 02/22/2004: | |
| CHS ABS 1 :) | |
| + - * 4 :) | |
| MIN MAX 7 :) | |
| SGN UNIT 9 :) | |
| < etc.. 10 :) | |
| RND 11 :) | |
| SQRT 15 :) | |
| / 18 :) | |
| && || 21 - | |
| FMOD 48 :( | |
| % 59 :( | |
| LOG 111 :( | |
| SIN 164 :( | |
| ATAN2 183 :( | |
| TAN 184 :( | |
| EXP 192 :( | |
| ATAN 192 :( | |
| NRND 203 :( | |
| COS 209 :( | |
| FLOOR 216 :( | |
| CEIL 226 :( | |
| LOG 232 :( | |
| ACOS 242 :( | |
| ASIN 250 :( | |
| POW ^ 342 :( | |
| FACT 467 - | |
| ÚÄÄÄÄÄÄÄÄÄÄÄÒÄÄÄÄÂÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÂÄÄÄÄÄÄÄÄÄ¿ | |
| ³Round mode:ºQB: ³CLIB:³C TYPECAST: ³FPU:³SSE: ³ | |
| ÆÍÍÍÍÍÍÍÍÍÍÍÎÍÍÍÍØÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍØÍÍÍÍÍÍÍÍ͵ | |
| ³ 0 ºFIX ³ ³(int) ³ ³cvttss2si³ <- generate array index | |
| ³ near/even ºCINT³ ³(int):QIFIST³ftol³cvtss2si ³ <- calculation precision | |
| ³ -inf ºINT ³floor³ ³ ³ ³ | |
| ³ +inf º ³ceil ³ ³ ³ ³ | |
| ÀÄÄÄÄÄÄÄÄÄÄÄÐÄÄÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÁÄÄÄÄÄÄÄÄÄÙ | |
| 06/24/2004: Compile time analysis: | |
| ÚÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ | |
| ³ MCC ³ P4-2.8 comp/s ³ | |
| ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ | |
| ³ perspmom.kc ³ 60.90 ³ 45.7 ³ | |
| ³ groufst2.kc ³ 24.70 ³ 112.7 ³ | |
| ³ poster.kc ³ 20.50 ³ 135.8 ³ | |
| ³ moire.kc ³ 18.60 ³ 149.7 ³ | |
| ³ normarea.kc ³ 10.80 ³ 257.8 ³ | |
| ³ goldball.kc ³ 9.68 ³ 287.6 ³ | |
| ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ | |
| ------------------------------------------------------------------------------ | |
| Ken`s official website: http://advsys.net/ken | |
| ============================================================================== | |
| #endif | |
| #include <string.h> | |
| #ifdef _MSC_VER | |
| #include <conio.h> | |
| #endif | |
| #include <stdlib.h> | |
| #include <stdio.h> | |
| #include <math.h> | |
| #include "eval.h" | |
| //#include <float.h> | |
| //#include "kdisasm.c" | |
| #if !defined(max) | |
| #define max(a,b) (((a) > (b)) ? (a) : (b)) | |
| #endif | |
| #if !defined(min) | |
| #define min(a,b) (((a) < (b)) ? (a) : (b)) | |
| #endif | |
| //----------------------------------------- KASM87 BEGINS ----------------------------------------- | |
| #ifndef COMPILE | |
| //if `COMPILE` is not specified in the makefile, choose the fastest supported option | |
| #if defined(_M_IX86) || defined(__i386__) | |
| #define COMPILE 1 //True compile (Windows/Linux) | |
| #else | |
| #define COMPILE 0 //Virtual machine (PowerPC) | |
| #endif | |
| #endif | |
| enum | |
| { | |
| PARAM0=0,NUL=PARAM0,GOTO,RETURN, RND,NRND, | |
| PARAM1, NOP=PARAM1,MOV,NEGMOV,NEQU0, IF0,IF1, | |
| FABS,SGN,UNIT,FLOOR,CEIL,ROUND0,ROUND0_32,SIN,COS,TAN,ASIN,ACOS,ATAN,SQRT,EXP,FACT,LOG, | |
| PARAM2, TIMES=PARAM2,SLASH,PERC,PLUS,MINUS,LES,LESEQ,MOR,MOREQ,EQU,NEQU,LAND,LOR, | |
| POW,MIN,MAX,FADD,FMOD,ATAN2,LOGB,PEEK, | |
| PARAM3, POKE,POKETIMES,POKESLASH,POKEPERC,POKEPLUS,POKEMINUS, | |
| USERFUNC, | |
| PARAMEND | |
| }; | |
| static unsigned char oprio[PARAMEND] = {0}; | |
| #define KEAX 0x00000000 // - | |
| #define KECX 0x10000000 //Local variable (moved to KFST/KESP for compiled) | |
| #define KEDX 0x20000000 //Constants (doubles/strings/arrays) | |
| #define KEBX 0x30000000 // - | |
| #define KESP 0x40000000 //Function parameter | |
| #define KEBP 0x50000000 // - | |
| #define KESI 0x60000000 // - | |
| #define KEDI 0x70000000 // - | |
| #define KEIP 0x80000000 //Jump location for GOTO/USERFUNC/IF* | |
| #define KFST 0x90000000 //Floating point stack (lowest 4 local variables) | |
| #define KPTR 0xa0000000 //Pointer to function parameter (addressed by ESP) | |
| #define KIMM 0xb0000000 //Immediate address from evalextyp[?].ptr | |
| #define KSTR 0xc0000000 //String table (moved to end of KEDX for compiled) | |
| #define KARR 0xd0000000 //Array table (moved to end of KEDX for compiled) | |
| #define KGLB 0xe0000000 //Global static (behaves similar to KARR&KIMM, but separate list) | |
| #define KUNUSED (KEDX+1) //Make parameter act like constant (best for optimization) and not match anything | |
| //min/max values for exp: -745.13321910194116528 (-log(2)*(1024+51)) and 709.78271289338396 (log(2)*1024) | |
| #define PI 3.14159265358979323 | |
| #ifdef _MSC_VER | |
| __declspec(align(16)) static long kexptval[4] = {0,0x80000000,0,0}; | |
| #define LL(l) l##i64 | |
| #define PRINTF64 "I64d" | |
| #else | |
| //#define __cdecl __attribute__((cdecl)) | |
| #define __cdecl | |
| #define _inline __inline__ | |
| #define LL(l) l##ll | |
| #define PRINTF64 "lld" | |
| typedef long long __int64; | |
| #define _snprintf snprintf | |
| #define lnglng(x) x ## ll | |
| #define stricmp strcasecmp | |
| #endif | |
| static const long pinf = 0x7f800000, ninf = 0xff800000, pind = 0x7fc00000, nind = 0xffc00000; | |
| static const float posone = 1.f, negone = -1.f, pointfive = .5f, oneover2_31 = 1.f/2147483648.f; | |
| //static const float threeup51 = 6755399441055744; | |
| //-------------------------------------------------- | |
| static long *funcst = 0; //for initial parsing of functions/global sections | |
| static long maxfuncst = 0; | |
| static long gstatmem = 0; //pointer to global static buffer | |
| static evalextyp *gevalext = 0; | |
| static long gevalextnum = 0; | |
| //kasm87 parsing temp variables | |
| static long maxops = 0, arrnum; | |
| static long *gop, *gnext, globi, memnum; //maxops | |
| static double *globval; //maxops | |
| static long gccnt; | |
| static char *gstring; //maxst | |
| static long gstnum, maxst = 0; | |
| typedef struct { long i; double v; } initval_t; | |
| static initval_t *ginitval; | |
| static long ginitvalnum, maxinitval = 0; | |
| static long gecnt, gnumarg = 0, gnumglob = 0; | |
| static double *gvl; //regnum*(recursion depth), stack space used by kasm87c only | |
| static double *gvlp; | |
| static long maxvars = 0, maxvarchars = 0; | |
| static char *newvarnam; //maxvarchars (variable name buffer; strings separated by NULL terminator) | |
| static long newvarhash[256], newvarhash_glob[256]; | |
| typedef struct | |
| { | |
| long r; //pointer to register family & offset | |
| long maxind; //Maximum index for arrays (0 if not an array) | |
| long parnum; //>=0: # parameters for user functions. <0: not a function; # = 1's complement of # dimensions | |
| long proti; //For funcs/arrays: newvarnam index. FuncProto:{d=double,D=double*}, ArrayDims:{(~parnum)*4} | |
| long nami; //index to start of variable/function`s name string in newvarnam | |
| long hashn; //hash index for variable/function name (for faster string finding & function overloading) | |
| } newvartyp; | |
| static newvartyp *newvar; | |
| static long newvarnum, newvarplc; //maxvars | |
| static char *enumnam; static long maxenumchars = 0, enumcharplc; //Enum name list (NULL terminator separators) | |
| static double *enumval; static long maxenum = 0, enumnum; //Enum value list | |
| static long maxlabs = 0, maxlabchars = 0; | |
| static char *newlabnam; //maxlabchars | |
| static long *newlabind, newlabnum, newlabplc; //maxlabs | |
| static long *labpat, *jumpat, *lablinum, numlabels; //maxlabs | |
| typedef struct { long addr, val; } jumpback_t; | |
| static jumpback_t *jumpback = 0; | |
| static long jumpbacknum = 0, maxjumpbacks = 0; | |
| #define MAXPARMS (1+2) //Output + #Inputs (for > 2 inputs, use rxi) | |
| typedef struct | |
| { | |
| long r; //register family (EAX,ECX,EDX,ESP,etc...) in highest 4 bits, and offset in lower 28 bits | |
| long q; //additional info (array index, which user function) | |
| long nv; //newvar index | |
| } rtyp; | |
| typedef struct | |
| { | |
| long f; //function enum index | |
| long g; //additional info for function | |
| long n; //Number of inputs | |
| rtyp r[MAXPARMS]; //register description | |
| long rxi; //Register eXtra Index | |
| } gasmtyp; | |
| static gasmtyp *gasm; //maxops | |
| static rtyp *rxi; | |
| static long numrxi, maxrxi = 0; | |
| #if (COMPILE != 0) | |
| #define FUNCBYTEOFFS 16 //Should be multiple of 16 for alignment speed. Pointer to jumpback table. | |
| #define CODEDATADIST 1024 //Number of bytes separate code and data blocks | |
| //if (?.ind >= 0) ?.ptr = gevalext[?.ind].ptr (must look up later for user function pointers) | |
| typedef struct { long *lptr; long ind; } patch_t; | |
| static patch_t *patch = 0; | |
| static long patchnum = 0, maxpatch = 0; | |
| #else | |
| #define FUNCBYTEOFFS 0 | |
| #endif | |
| static long round0msk[2048][2]; | |
| //-------------------------------------------------- | |
| static long cputype = 0, cpuinited = 0; | |
| #ifdef _MSC_VER | |
| static _inline long testflag (long c) | |
| { | |
| _asm | |
| { | |
| mov ecx, c | |
| pushfd | |
| pop eax | |
| mov edx, eax | |
| xor eax, ecx | |
| push eax | |
| popfd | |
| pushfd | |
| pop eax | |
| xor eax, edx | |
| mov eax, 1 | |
| jne menostinx | |
| xor eax, eax | |
| menostinx: | |
| } | |
| } | |
| static _inline void cpuid (long a, long *s) | |
| { | |
| _asm | |
| { | |
| push ebx | |
| push esi | |
| mov eax, a | |
| cpuid | |
| mov esi, s | |
| mov dword ptr [esi+0], eax | |
| mov dword ptr [esi+4], ebx | |
| mov dword ptr [esi+8], ecx | |
| mov dword ptr [esi+12], edx | |
| pop esi | |
| pop ebx | |
| } | |
| } | |
| #else | |
| static _inline long testflag (long c) { return(0); } | |
| static _inline void cpuid (long a, long *s) { return; } | |
| #endif | |
| //Bit numbers of return value: | |
| //0:FPU, 4:RDTSC, 15:CMOV, 22:MMX+, 23:MMX, 25:SSE, 26:SSE2, 27:SSE3, 30:3DNow!+, 31:3DNow! | |
| static long getcputype () | |
| { | |
| long i, cpb[4], cpid[4]; | |
| if (!testflag(0x200000)) return(0); | |
| cpuid(0,cpid); if (!cpid[0]) return(0); | |
| cpuid(1,cpb); i = (cpb[3]&~((1<<22)|(1<<27)|(1<<30)|(1<<31))); | |
| if (cpb[2]&(1<<0)) i |= (1<<27); //I hijack bit 27 for SSE3 detection | |
| cpuid(0x80000000,cpb); | |
| if (((unsigned long)cpb[0]) > 0x80000000) | |
| { | |
| cpuid(0x80000001,cpb); | |
| i |= (cpb[3]&(1<<31)); | |
| if (!((cpid[1]^0x68747541)|(cpid[3]^0x69746e65)|(cpid[2]^0x444d4163))) //AuthenticAMD | |
| i |= (cpb[3]&((1<<22)|(1<<30))); | |
| } | |
| if (i&(1<<25)) i |= (1<<22); //SSE implies MMX+ support | |
| return(i); | |
| } | |
| //-------------------------------------------------- | |
| static unsigned char *compcode = 0; | |
| long kasm87leng, kasm87err0, kasm87err1, kasm87optimize = 1; | |
| char kasm87err[256] = ""; | |
| static long *texttrans = 0, texttransn, texttransmal = 0; //keep track of removed whitespace for adjusting kasm87err0 & kasm87err1 | |
| static long kholdrand = 1, snormstat = 0; | |
| void ksrand (long val) { kholdrand = val; snormstat = 0; } | |
| #ifndef _MSC_VER | |
| static long krand () { kholdrand = (unsigned long)((kholdrand*(214013*2)+2531011*2)>>1); return(kholdrand); } | |
| #else | |
| __declspec(naked) static long krand () | |
| { | |
| _asm | |
| { | |
| mov eax, kholdrand | |
| imul eax, 214013*2 | |
| add eax, 2531011*2 | |
| shr eax, 1 | |
| mov kholdrand, eax | |
| ret | |
| } | |
| } | |
| #endif | |
| static double nrnd () | |
| { | |
| static double srand2; | |
| double x, y, r; | |
| //Box-Muller method (Good & fast) | |
| if (snormstat) { snormstat = 0; return(srand2); } | |
| do | |
| { | |
| x = ((double)(krand()-1073741824))*(oneover2_31*2.0); //-1 to 1 | |
| y = ((double)(krand()-1073741824))*(oneover2_31*2.0); //-1 to 1 | |
| r = x*x + y*y; | |
| } while (r >= 1); | |
| snormstat = 1; r = sqrt(-2.0*log(r)/r); srand2 = x*r; | |
| return(y*r); | |
| } | |
| #ifndef _MSC_VER | |
| static double fact (double num) | |
| { | |
| if ((num <= -.99999999999999996) || (num >= 170.6243769562767)) return(*(float *)&pinf); | |
| num++; //2^, 14*, 1/, 15+ (Ken optimized out most divides - wasn`t easy!) | |
| return(pow(num+5.5,num+0.5)*exp(-5.5-num)* | |
| (((((((num*2.506628275107298 + 83.8676043423952)*num + 1168.926494792211)*num + | |
| 8687.245297053594)*num + 36308.29514770109)*num + 80916.62789524846)*num + 75122.63315304522) / | |
| (((((((num + 21)*num + 175)*num + 735)*num + 1624)*num + 1764)*num + 720)*num))); | |
| } | |
| #else | |
| __declspec(naked) static double __cdecl fact (double num) | |
| { | |
| static const double maxval = 170.6243769562767; | |
| static const double factconsts[15] = {2.506628275107298,83.8676043423952,1168.926494792211,8687.245297053594, | |
| 36308.29514770109,80916.62789524846,75122.63315304522,21,175,735,1624,1764,720,5.5,0.5}; | |
| //st(0) > src: if !(ah&0x45) | |
| //st(0) >= src: if (!(ah&0x45)) || (ah&0x40) | |
| //st(0) == src: if (ah&0x40) | |
| //st(0) <= src: if (ah&0x41) | |
| //st(0) < src: if (ah&0x01) | |
| //unordered if (ah&0x04) | |
| _asm //WARNING: CAN MODIFY ONLY EAX! | |
| { | |
| fld qword ptr [esp+4] | |
| fcom dword ptr [negone] | |
| fnstsw ax | |
| and ah, 0x41 | |
| jnz short factinf ;<= -1 | |
| ;fld dword ptr [negone] | |
| ;fcomip st, st(1) ;Requires >=PPRO | |
| ;jae short factinf | |
| fcom qword ptr [maxval] | |
| fnstsw ax | |
| and ah, 0x45 | |
| jz short factinf ;> maxval | |
| ;fld qword ptr [maxval] | |
| ;fcomip st, st(1) ;Requires >=PPRO | |
| ;jb short factinf | |
| fadd dword ptr [posone] | |
| fld st(0) | |
| fld st(0) | |
| fld st(0) | |
| mov eax, offset factconsts | |
| fmul qword ptr [eax] ;a *= konst[0] | |
| fadd qword ptr [eax+8] ;a += konst[8] | |
| fmul st, st(2) ;a *= num | |
| fadd qword ptr [eax+16] ;a += konst[16] | |
| fmul st, st(2) ;a *= num | |
| fadd qword ptr [eax+24] ;a += konst[24] | |
| fmul st, st(2) ;a *= num | |
| fadd qword ptr [eax+32] ;a += konst[32] | |
| fmul st, st(2) ;a *= num | |
| fadd qword ptr [eax+40] ;a += konst[40] | |
| fmul st, st(2) ;a *= num | |
| fadd qword ptr [eax+48] ;a += konst[48] | |
| fxch st(1) | |
| fadd qword ptr [eax+56] ;b += konst[56] | |
| fmul st, st(2) ;b *= num | |
| fadd qword ptr [eax+64] ;b += konst[64] | |
| fmul st, st(2) ;b *= num | |
| fadd qword ptr [eax+72] ;b += konst[72] | |
| fmul st, st(2) ;b *= num | |
| fadd qword ptr [eax+80] ;b += konst[80] | |
| fmul st, st(2) ;b *= num | |
| fadd qword ptr [eax+88] ;b += konst[88] | |
| fmul st, st(2) ;b *= num | |
| fadd qword ptr [eax+96] ;b += konst[96] | |
| fmul st, st(2) ;b *= num | |
| fdivp st(1), st ;a /= b | |
| fxch st(2) | |
| fadd qword ptr [eax+104] ;c = num+konst[104] ;c ? | |
| fxch st(1) ;? c | |
| fadd qword ptr [eax+112] ;d = num+konst[112] ;d c | |
| fld st(1) ;c d c | |
| ;c = 2^(log2(c)*d - c*l2e) | |
| fyl2x ;(st1 *= log2(st0), pop st) ;log2(c)*d c | |
| fxch st(1) ;c log2(c)*d | |
| fldl2e | |
| fmulp st(1), st | |
| fsubp st(1), st | |
| #if 0 | |
| ;Multi-thread unsafe exp (faster than safe) | |
| mov eax, offset kexptval[8] | |
| fist dword ptr [eax] | |
| fisub dword ptr [eax] | |
| add dword ptr [eax], 0x3fff | |
| f2xm1 | |
| fadd dword ptr [posone] | |
| fld tbyte ptr [eax-8] | |
| fmulp st(1), st(0) | |
| #else | |
| ;Multi-thread safe exp: (~80cc slower than unsafe) | |
| sub esp, 8 | |
| fist dword ptr [esp] | |
| fisub dword ptr [esp] | |
| add dword ptr [esp], 0x3fff | |
| f2xm1 | |
| fadd dword ptr [posone] | |
| push 0x80000000 | |
| push 0 | |
| fld tbyte ptr [esp] | |
| fmulp st(1), st(0) | |
| add esp, 16 | |
| #endif | |
| fmulp st(1), st ;a *= c | |
| ret | |
| factinf: | |
| fstp st(0) | |
| fld dword ptr [pinf] | |
| ret | |
| } | |
| } | |
| #endif | |
| //Ken`s replacement for pow... | |
| #ifndef _MSC_VER | |
| static double kpow(double x, double y) { return pow(x,y); } | |
| #else | |
| __declspec(naked) static double __cdecl kpow (double x, double y) | |
| { | |
| _asm //WARNING: CAN MODIFY ONLY EAX! | |
| { | |
| cmp dword ptr [esp+8], 0 ;if (x == 0) (1st half of test) | |
| je short dozer | |
| backz:fld qword ptr [esp+12] | |
| fld qword ptr [esp+4] | |
| fabs | |
| fyl2x ;(st1 *= log2(st0), pop st) | |
| fist dword ptr [esp+4] | |
| fisub dword ptr [esp+4] | |
| mov eax, [esp+4] | |
| lea eax, [eax+16383] | |
| #if 0 | |
| ;Multi-thread unsafe exp (faster than safe) | |
| mov dword ptr kexptval[8], eax | |
| f2xm1 | |
| fadd dword ptr [posone] | |
| fld tbyte ptr kexptval[0] | |
| #else | |
| ;Multi-thread safe exp: (~80cc slower than unsafe) | |
| lea esp, [esp-8] | |
| mov dword ptr [esp], eax | |
| f2xm1 | |
| fadd dword ptr [posone] | |
| push 0x80000000 | |
| push 0 | |
| fld tbyte ptr [esp] | |
| lea esp, [esp+16] | |
| #endif | |
| fmulp st(1), st(0) | |
| jl short doneg ;if (x < 0) | |
| ret | |
| doneg:fld qword ptr [esp+12] ;handle pow(-,*) cases | |
| fistp qword ptr [esp+4] | |
| fild qword ptr [esp+4] | |
| fcomp qword ptr [esp+12] | |
| fnstsw ax | |
| and ah, 0x40 | |
| jz short bad1 ;power is not an integer | |
| test dword ptr [esp+4], 1 | |
| jz short endit | |
| fchs | |
| endit:ret | |
| bad1: fstp st(0) | |
| fld dword ptr [nind] | |
| ret | |
| dozer:cmp dword ptr [esp+4], 0 ;if (x == 0) (2nd half of test) | |
| jne short backz ;oops! x wasn`t actually 0! | |
| fldz ;handle pow(0,*) cases | |
| fcomp qword ptr [esp+12] | |
| fnstsw ax | |
| test ax, 0x0100 | |
| jnz short bad2 | |
| test ax, 0x4000 | |
| jz short skp2 | |
| fld1 | |
| ret | |
| skp2: fld dword ptr [pinf] | |
| ret | |
| bad2: fldz | |
| ret | |
| } | |
| } | |
| #endif | |
| static void getfuncnam (gasmtyp *g, char *st) | |
| { | |
| switch(g->f) | |
| { | |
| case NUL: strcpy(st,"NUL"); break; | |
| case GOTO: strcpy(st,"GOTO"); break; | |
| case RETURN:strcpy(st,"RETURN"); break; | |
| case RND: strcpy(st,"RND"); break; | |
| case NRND: strcpy(st,"NRND"); break; | |
| case NOP: strcpy(st,"NOP"); break; | |
| case MOV: strcpy(st,"MOV"); break; | |
| case NEGMOV:strcpy(st,"NEGMOV"); break; | |
| case NEQU0: strcpy(st,"NEQU0"); break; | |
| case IF0: strcpy(st,"IF0"); break; | |
| case IF1: strcpy(st,"IF1"); break; | |
| case FABS: strcpy(st,"FABS"); break; | |
| case SGN: strcpy(st,"SGN"); break; | |
| case UNIT: strcpy(st,"UNIT"); break; | |
| case FLOOR: strcpy(st,"FLOOR"); break; | |
| case ROUND0: case ROUND0_32: strcpy(st,"ROUND0"); break; | |
| case CEIL: strcpy(st,"CEIL"); break; | |
| case SIN: strcpy(st,"SIN"); break; | |
| case COS: strcpy(st,"COS"); break; | |
| case TAN: strcpy(st,"TAN"); break; | |
| case ASIN: strcpy(st,"ASIN"); break; | |
| case ACOS: strcpy(st,"ACOS"); break; | |
| case ATAN: strcpy(st,"ATAN"); break; | |
| case SQRT: strcpy(st,"SQRT"); break; | |
| case EXP: strcpy(st,"EXP"); break; | |
| case FACT: strcpy(st,"FACT"); break; | |
| case LOG: strcpy(st,"LOG"); break; | |
| case TIMES: strcpy(st,"*"); break; | |
| case SLASH: strcpy(st,"/"); break; | |
| case PERC: strcpy(st,"%%"); break; | |
| case PLUS: strcpy(st,"+"); break; | |
| case MINUS: strcpy(st,"-"); break; | |
| case LES: strcpy(st,"<"); break; | |
| case LESEQ: strcpy(st,"<="); break; | |
| case MOR: strcpy(st,">"); break; | |
| case MOREQ: strcpy(st,">="); break; | |
| case EQU: strcpy(st,"=="); break; | |
| case NEQU: strcpy(st,"!="); break; | |
| case LAND: strcpy(st,"&&"); break; | |
| case LOR: strcpy(st,"||"); break; | |
| case POW: strcpy(st,"POW"); break; | |
| case MIN: strcpy(st,"MIN"); break; | |
| case MAX: strcpy(st,"MAX"); break; | |
| case FADD: strcpy(st,"FADD"); break; | |
| case FMOD: strcpy(st,"FMOD"); break; | |
| case ATAN2: strcpy(st,"ATAN2"); break; | |
| case LOGB: strcpy(st,"LOGB"); break; | |
| case PEEK: strcpy(st,"PEEK"); break; | |
| case POKE: strcpy(st,"POKE"); break; | |
| case POKETIMES:strcpy(st,"POKETIMES"); break; | |
| case POKESLASH:strcpy(st,"POKESLASH"); break; | |
| case POKEPERC: strcpy(st,"POKEPERC"); break; | |
| case POKEPLUS: strcpy(st,"POKEPLUS"); break; | |
| case POKEMINUS:strcpy(st,"POKEMINUS"); break; | |
| case USERFUNC: if (g->g >= 0) strcpy(st,&newvarnam[newvar[g->g].nami]); else strcpy(st,"USERFUNC"); break; | |
| default: st[0] = 0; break; | |
| } | |
| } | |
| static void getvarnam (rtyp reg, char *st) | |
| { | |
| long l, m; | |
| if (reg.r == KUNUSED) { strcpy(st,"?"); return; } | |
| switch(((unsigned long)reg.r)>>28) | |
| { | |
| case (KECX>>28): sprintf(st,"m%d",(reg.r&0x0fffffff)>>3); break; | |
| case (KSTR>>28): reg.r = (reg.r&0x0fffffff)+gccnt*8+KEDX; goto getvarnam_casekedx; | |
| case (KARR>>28): reg.r = (reg.r&0x0fffffff)+gccnt*8+gstnum+KEDX; goto getvarnam_casekedx; | |
| case (KEDX>>28): | |
| getvarnam_casekedx: | |
| l = (reg.r&0x0fffffff); | |
| if (l < (gccnt<<3) ) { sprintf(st,"%g",globval[l>>3]); } | |
| else if (l < (gccnt<<3)+gstnum) { sprintf(st,"\"%s\"",&gstring[l-(gccnt<<3)]); } | |
| else | |
| { | |
| for(m=newvarnum-1;m>=0;m--) | |
| if (newvar[m].r == l-(gccnt<<3)-gstnum+(signed)KARR) | |
| { | |
| strcpy(st,&newvarnam[newvar[m].nami]); | |
| sprintf(&st[strlen(st)],"[%d]",reg.q); | |
| break; | |
| } | |
| if (m < 0) sprintf(st,"??0x%08x??",reg.r); | |
| } | |
| break; | |
| case (KESP>>28): case (KPTR>>28): | |
| if (((reg.r&0x0fffffff)>>3) < memnum) | |
| { | |
| sprintf(st,"m%d",(reg.r&0x0fffffff)>>3); | |
| break; | |
| } //No break intentional | |
| case (KIMM>>28): case (KGLB>>28): | |
| for(m=0;m<gnumglob;m++) | |
| if (newvar[m].r == reg.r) | |
| { | |
| strcpy(st,&newvarnam[newvar[m].nami]); | |
| if ((newvar[m].maxind) || ((((unsigned long)reg.r)>>28) == (KPTR>>28))) | |
| sprintf(&st[strlen(st)],"[%d]",reg.q); | |
| break; | |
| } | |
| if (m >= gnumglob) sprintf(st,"??0x%08x??",reg.r); | |
| break; | |
| case (KEIP>>28): sprintf(st,"l%d",reg.r&0x0fffffff); break; | |
| case (KFST>>28): sprintf(st,"r%d",(reg.r&0x0fffffff)>>3); break; | |
| default: sprintf(st,"?!0x%08x!?",reg.r); break; | |
| } | |
| } | |
| //showflags=1: pseudoasm | |
| //showflags=2: machine code bytes | |
| void kasm87_showdebug (long showflags, char *debuf, long debuflng) | |
| { | |
| rtyp *rp; | |
| long i, j, k, didlab = 0; | |
| char st[4][64], st2[260], *cptr, *cptr2, *cp0, *cp1; | |
| if (debuflng <= 0) return; | |
| debuf[0] = 0; | |
| cp0 = debuf; cp1 = &debuf[debuflng-1]; cp1[0] = 0; | |
| if (showflags&1) | |
| { | |
| for(i=0;i<gecnt;i++) | |
| { | |
| for(j=2;j>=0;j--) getvarnam(gasm[i].r[j],st[j]); | |
| if (gasm[i].n >= 3) getvarnam(rxi[gasm[i].rxi],st[3]); else st[3][0] = 0; | |
| if (gasm[i].f == NUL) { k = _snprintf(cp0,cp1-cp0,"%s:",st[0]); if (k >= 0) cp0 += k; didlab = 1; continue; } | |
| if (!didlab) { k = _snprintf(cp0,cp1-cp0," "); if (k >= 0) cp0 += k; } else didlab = 0; | |
| if (gasm[i].f == IF0) { k = _snprintf(cp0,cp1-cp0,"IF !(%s) GOTO %s\n",st[2],st[1]); if (k >= 0) cp0 += k; continue; } | |
| if (gasm[i].f == IF1) { k = _snprintf(cp0,cp1-cp0,"IF (%s) GOTO %s\n",st[2],st[1]); if (k >= 0) cp0 += k; continue; } | |
| if (gasm[i].f == GOTO) { k = _snprintf(cp0,cp1-cp0,"GOTO %s\n",st[1]); if (k >= 0) cp0 += k; continue; } | |
| if (gasm[i].f == RETURN) { k = _snprintf(cp0,cp1-cp0,"RETURN %s\n",st[1]); if (k >= 0) cp0 += k; continue; } | |
| if (gasm[i].f == USERFUNC) | |
| { | |
| getfuncnam(&gasm[i],st2); | |
| k = _snprintf(cp0,cp1-cp0,"%s = %s(",st[0],st2); if (k >= 0) cp0 += k; | |
| for(j=0;j<gasm[i].n;j++) | |
| { | |
| if (j) { k = _snprintf(cp0,cp1-cp0,","); if (k >= 0) cp0 += k; } | |
| if (newvarnam[newvar[gasm[i].g].proti+j] == 'D') | |
| { k = _snprintf(cp0,cp1-cp0,"&"); if (k >= 0) cp0 += k; } | |
| if (newvarnam[newvar[gasm[i].g].proti+j] == 'C') | |
| { k = _snprintf(cp0,cp1-cp0,"$"); if (k >= 0) cp0 += k; } | |
| if (j < 2) rp = &gasm[i].r[j+1]; else rp = &rxi[gasm[i].rxi+j-2]; | |
| getvarnam(*rp,st[2]); | |
| k = _snprintf(cp0,cp1-cp0,"%s",st[2]); if (k >= 0) cp0 += k; | |
| } | |
| k = _snprintf(cp0,cp1-cp0,")\n"); if (k >= 0) cp0 += k; | |
| continue; | |
| } | |
| switch(gasm[i].f) | |
| { | |
| case RND: case NRND: | |
| getfuncnam(&gasm[i],st2); strcat(st2,"()"); break; | |
| case MOV: strcpy(st2,"%s"); break; | |
| case NEGMOV:strcpy(st2,"-%s"); break; | |
| case NEQU0: strcpy(st2,"%s != 0"); break; | |
| case FABS: case SGN: case UNIT: case FLOOR: case CEIL: case ROUND0: case ROUND0_32: case SIN: case COS: case TAN: | |
| case ASIN: case ACOS: case ATAN: case SQRT: case EXP: case FACT: case LOG: | |
| getfuncnam(&gasm[i],st2); strcat(st2,"(%s)"); break; | |
| case TIMES: case SLASH: case PERC: case PLUS: case MINUS: | |
| case LES: case LESEQ: case MOR: case MOREQ: case EQU: case NEQU: case LAND: case LOR: | |
| strcpy(st2,"%s "); getfuncnam(&gasm[i],&st2[strlen(st2)]); strcat(st2," %s"); break; | |
| case POW: case MIN: case MAX: case FADD: case FMOD: case ATAN2: case LOGB: case PEEK: | |
| getfuncnam(&gasm[i],st2); strcat(st2,"(%s,%s)"); break; | |
| case POKE: case POKETIMES: case POKESLASH: case POKEPERC: case POKEPLUS: case POKEMINUS: | |
| getfuncnam(&gasm[i],st2); strcat(st2,"(%s,%s,%s)"); break; | |
| } | |
| k = _snprintf(cp0,cp1-cp0,"%s = ",st[0]); if (k >= 0) cp0 += k; | |
| k = _snprintf(cp0,cp1-cp0,st2,st[1],st[2],st[3]); if (k >= 0) cp0 += k; | |
| k = _snprintf(cp0,cp1-cp0,"\n"); if (k >= 0) cp0 += k; | |
| } | |
| if (didlab) { k = _snprintf(cp0,cp1-cp0,"\n"); if (k >= 0) cp0 += k; } //last label has no code | |
| } | |
| if ((showflags&2) && (compcode)) | |
| { | |
| for(i=0;i<kasm87leng;i++) | |
| { | |
| if (!(i&15)) | |
| { | |
| if (i) | |
| { | |
| k = _snprintf(cp0,cp1-cp0,"\n",i); if (k >= 0) cp0 += k; | |
| for(j=i+1;j<kasm87leng;j++) if (compcode[i] != compcode[j]) break; | |
| if (j-i >= 16) | |
| { | |
| j = ((j-i)&~15); | |
| k = _snprintf(cp0,cp1-cp0,"%06x %02x * 0x%x\n",i,compcode[i],j); if (k >= 0) cp0 += k; | |
| if (j > 16) { k = _snprintf(cp0,cp1-cp0,"..\n"); if (k >= 0) cp0 += k; } | |
| i += j; if (i >= kasm87leng) break; | |
| } | |
| } | |
| k = _snprintf(cp0,cp1-cp0,"%06x ",i); if (k >= 0) cp0 += k; | |
| } | |
| k = _snprintf(cp0,cp1-cp0,"%02x ",compcode[i]); if (k >= 0) cp0 += k; | |
| } | |
| k = _snprintf(cp0,cp1-cp0," (%d bytes)\n",kasm87leng); if (k >= 0) cp0 += k; | |
| } | |
| //else { k = _snprintf(cp0,cp1-cp0,"\n"); if (k >= 0) cp0 += k; } | |
| //if ((showflags&4) && (compcode)) { i = 0; cp0 = kdisasm((char *)compcode,kasm87leng,cp0,cp1-cp0,&i); } | |
| } | |
| static long isvarchar (unsigned char ch) | |
| { | |
| static const long isvarcharbuf[8] = {0,0x03ff0000,0x87fffffe,0x07fffffe,0,0,0,0}; | |
| #if defined(_M_IX86) || defined(__i386__) | |
| return(isvarcharbuf[ch>>5]&(1<<ch)); //WARNING: Shift auto-modulo`d by 32 trick only works on Intel CPUs! | |
| #elif 1 | |
| return(isvarcharbuf[ch>>5]&(1<<(ch&31))); | |
| #endif | |
| //return(((ch >= '0') && (ch <= '9')) || ((ch >= 'A') && (ch <= 'Z')) || (ch == '_') || ((ch >= 'a') && (ch <= 'z'))); | |
| } | |
| static long getnewvarhash (const char *st) | |
| { | |
| long i, hashind; | |
| char ch; | |
| for(i=0,hashind=0;st[i];i++) | |
| { | |
| ch = st[i]; if ((ch >= 'a') && (ch <= 'z')) ch -= 32; | |
| hashind = ch - hashind*3; | |
| } | |
| return(hashind&(sizeof(newvarhash)/sizeof(newvarhash[0])-1)); | |
| } | |
| static void checkfuncst (long i) //note: i is per long, not function | |
| { | |
| if (i < maxfuncst) return; | |
| if (!i) { maxfuncst = 256; } else { while (i >= maxfuncst) maxfuncst += (maxfuncst>>2); } | |
| //printf("maxfuncst=%d\n",maxfuncst); | |
| if (!(funcst = (long *)realloc(funcst,sizeof(funcst[0])*maxfuncst))) { globi = -1; strcpy(kasm87err,"ERROR: malloc failed"); return; } | |
| } | |
| static void checkops (long i) | |
| { | |
| if (i < maxops) return; | |
| if (!i) { maxops = 1024; } else { while (i >= maxops) maxops += (maxops>>2); } | |
| //printf("maxops=%d\n",maxops); | |
| if (!( gop = ( long *)realloc( gop,sizeof(long) *maxops))) { globi = -1; strcpy(kasm87err,"ERROR: malloc failed"); return; } | |
| if (!( gnext = ( long *)realloc( gnext,sizeof(long) *maxops))) { globi = -1; strcpy(kasm87err,"ERROR: malloc failed"); return; } | |
| if (!( globval = ( double *)realloc( globval,sizeof(double) *maxops))) { globi = -1; strcpy(kasm87err,"ERROR: malloc failed"); return; } | |
| if (!( gasm = (gasmtyp *)realloc( gasm,sizeof(gasmtyp)*maxops))) { globi = -1; strcpy(kasm87err,"ERROR: malloc failed"); return; } | |
| } | |
| static void checkstrings (long i) | |
| { | |
| if (i < maxst) return; | |
| if (!i) { maxst = 1024; } else { while (i >= maxst) maxst += (maxst>>2); } | |
| //printf("maxst=%d\n",maxst); | |
| if (!( gstring = ( char *)realloc( gstring,sizeof(char) *maxst))) { globi = -1; strcpy(kasm87err,"ERROR: malloc failed"); return; } | |
| } | |
| static void checkinitvals (long i) | |
| { | |
| if (i < maxinitval) return; | |
| if (!i) { maxinitval = 256; } else { while (i >= maxinitval) maxinitval += (maxinitval>>2); } | |
| //printf("maxinitval=%d\n",maxinitval); | |
| if (!(ginitval = (initval_t *)realloc(ginitval,sizeof(initval_t)*maxinitval))) { globi = -1; strcpy(kasm87err,"ERROR: malloc failed"); return; } | |
| } | |
| static void checkrxi (long i) | |
| { | |
| if (i < maxrxi) return; | |
| if (!i) { maxrxi = 256; } else { while (i >= maxrxi) maxrxi += (maxrxi>>2); } | |
| //printf("maxrxi=%d\n",maxrxi); | |
| if (!(rxi = (rtyp *)realloc(rxi,sizeof(rtyp)*maxrxi))) { globi = -1; strcpy(kasm87err,"ERROR: malloc failed"); return; } | |
| } | |
| static void checkvarchars (long i) | |
| { | |
| if (i < maxvarchars) return; | |
| if (!i) { maxvarchars = 1024; } else { while (i >= maxvarchars) maxvarchars += (maxvarchars>>2); } | |
| //printf("maxvarchars=%d\n",maxvarchars); | |
| if (!(newvarnam = (char *)realloc(newvarnam,sizeof(char)*maxvarchars))) { globi = -1; strcpy(kasm87err,"ERROR: malloc failed"); return; } | |
| } | |
| static void checkvars (long i) | |
| { | |
| if (i < maxvars) return; | |
| if (!i) { maxvars = 256; } else { while (i >= maxvars) maxvars += (maxvars>>2); } | |
| //printf("maxvars=%d\n",maxvars); | |
| if (!(newvar = (newvartyp *)realloc(newvar,sizeof(newvartyp)*maxvars))) { globi = -1; strcpy(kasm87err,"ERROR: malloc failed"); return; } | |
| } | |
| static void checkenumchars (long i) | |
| { | |
| if (i < maxenumchars) return; | |
| if (!i) { maxenumchars = 1024; } else { while (i >= maxenumchars) maxenumchars += (maxenumchars>>2); } | |
| //printf("maxenumchars=%d\n",maxenumchars); | |
| if (!(enumnam = (char *)realloc(enumnam,sizeof(char)*maxenumchars))) { globi = -1; strcpy(kasm87err,"ERROR: malloc failed"); return; } | |
| } | |
| static void checkenum (long i) | |
| { | |
| if (i < maxenum) return; | |
| if (!i) { maxenum = 256; } else { while (i >= maxenum) maxenum += (maxenum>>2); } | |
| //printf("maxenum=%d\n",maxenum); | |
| if (!(enumval = (double *)realloc(enumval,sizeof(double)*maxenum))) { globi = -1; strcpy(kasm87err,"ERROR: malloc failed"); return; } | |
| } | |
| static void checklabchars (long i) | |
| { | |
| if (i < maxlabchars) return; | |
| if (!i) { maxlabchars = 1024; } else { while (i >= maxlabchars) maxlabchars += (maxlabchars>>2); } | |
| //printf("maxlabchars=%d\n",maxlabchars); | |
| if (!(newlabnam = ( char *)realloc(newlabnam,sizeof(char) *maxlabchars))) { globi = -1; strcpy(kasm87err,"ERROR: malloc failed"); return; } | |
| } | |
| static void checklabs (long i) | |
| { | |
| if (i < maxlabs) return; | |
| if (!i) { maxlabs = 256; } else { while (i >= maxlabs) maxlabs += (maxlabs>>2); } | |
| //printf("maxlabs=%d\n",maxlabs); | |
| if (!(newlabind = ( long *)realloc(newlabind,sizeof(long) *maxlabs))) { globi = -1; strcpy(kasm87err,"ERROR: malloc failed"); return; } | |
| if (!( labpat = ( long *)realloc(labpat ,sizeof(long) *maxlabs))) { globi = -1; strcpy(kasm87err,"ERROR: malloc failed"); return; } | |
| if (!( jumpat = ( long *)realloc(jumpat ,sizeof(long) *maxlabs))) { globi = -1; strcpy(kasm87err,"ERROR: malloc failed"); return; } | |
| if (!( lablinum = ( long *)realloc(lablinum ,sizeof(long) *maxlabs))) { globi = -1; strcpy(kasm87err,"ERROR: malloc failed"); return; } | |
| } | |
| static void checkjumpbacks (long i) | |
| { | |
| if (i < maxjumpbacks) return; | |
| if (!i) { maxjumpbacks = 256; } else { while (i >= maxjumpbacks) maxjumpbacks += (maxjumpbacks>>2); } | |
| //printf("maxjumpbacks=%d\n",maxjumpbacks); | |
| if (!(jumpback = (jumpback_t *)realloc(jumpback,sizeof(jumpback_t)*maxjumpbacks))) { globi = -1; strcpy(kasm87err,"ERROR: malloc failed"); return; } | |
| } | |
| #if (COMPILE != 0) | |
| static void checkpatch (long i) | |
| { | |
| if (i < maxpatch) return; | |
| if (!i) { maxpatch = 256; } else { while (i >= maxpatch) maxpatch += (maxpatch>>2); } | |
| //printf("maxpatch=%d\n",maxpatch); | |
| if (!(patch = (patch_t *)realloc(patch,sizeof(patch_t)*maxpatch))) { globi = -1; strcpy(kasm87err,"ERROR: malloc failed"); return; } | |
| } | |
| #endif | |
| //Helper function to compare 2 parameters on gasm | |
| static long gasmeq (rtyp g0, rtyp g1) | |
| { | |
| return((g0.r == g1.r) && (g0.q == g1.q)); | |
| } | |
| static long skipparen (char *st, long z) | |
| { | |
| long p = 0, inquotes = 0; | |
| for(;1;z++) | |
| { | |
| if (!st[z]) return(-1); | |
| if ((st[z] == '\"') && ((!z) || (st[z-1] != '\\'))) inquotes ^= 1; | |
| if (inquotes) continue; | |
| if (st[z] == '(') { p++; continue; } | |
| if (st[z] == ')') { p--; if (!p) break; } | |
| } | |
| return(z+1); //Skip ')' | |
| } | |
| //findscope(): parses dangling else`s and other weird syntax missing {} properly | |
| // st: string pointer base | |
| // z: string start index | |
| // z0: index to scope start | |
| // z1: index to scope end (where to write NULL terminator) | |
| //returns:index to 1st char of next statement (skips `}` when applicable) | |
| static long findscope (char *st, long z, long *z0, long *z1) | |
| { | |
| long p, ifcnt, zx0, zx1; | |
| if (st[z] == '{') | |
| { | |
| z++; (*z0) = z; p = 1; | |
| for(;1;z++) | |
| { | |
| if (!st[z]) return(-1); | |
| if (st[z] == '{') { p++; continue; } | |
| if (st[z] == '}') { p--; if (!p) break; } | |
| } | |
| (*z1) = z; | |
| z++; //Skip '}' | |
| } | |
| else | |
| { | |
| (*z0) = z; | |
| if ((!strncmp(&st[z],"IF",2)) && (!isvarchar(st[z+2]))) | |
| { | |
| if ((z = skipparen(st,z+2)) < 0) return(-1); | |
| if ((z = findscope(st,z,&zx0,&zx1)) < 0) return(-1); | |
| if ((!strncmp(&st[z],"ELSE",4)) && (!isvarchar(st[z+4]))) | |
| { | |
| z += 4; if (st[z] == ' ') z++; | |
| if ((z = findscope(st,z,&zx0,&zx1)) < 0) return(-1); | |
| } | |
| } | |
| else if ((!strncmp(&st[z],"DO",2)) && (!isvarchar(st[z+2]))) | |
| { | |
| if ((z = findscope(st,z+2,&zx0,&zx1)) < 0) return(-1); | |
| if ((!strncmp(&st[z],"WHILE",5)) && (!isvarchar(st[z+5]))) | |
| { | |
| if ((z = skipparen(st,z+5)) < 0) return(-1); | |
| if (st[z] == ';') z++; | |
| } else return(-1); | |
| } | |
| else if ((!strncmp(&st[z],"WHILE",5)) && (!isvarchar(st[z+5]))) | |
| { | |
| if ((z = skipparen(st,z+5)) < 0) return(-1); | |
| if ((z = findscope(st,z,&zx0,&zx1)) < 0) return(-1); | |
| } | |
| else if ((!strncmp(&st[z],"FOR",3)) && (!isvarchar(st[z+3]))) | |
| { | |
| if ((z = skipparen(st,z+3)) < 0) return(-1); | |
| if ((z = findscope(st,z,&zx0,&zx1)) < 0) return(-1); | |
| } | |
| else | |
| { | |
| for(;1;z++) | |
| { | |
| if (!st[z]) return(-1); | |
| if (st[z] == ';') { z++; break; } | |
| } | |
| } | |
| (*z1) = z; | |
| } | |
| return(z); | |
| } | |
| void kasm87addext (evalextyp *daeet, long n) { gevalext = daeet; gevalextnum = n; } | |
| //NOTE: newvar must be written and 0-terminated first before calling this! | |
| //Writes each dimension to newvar[newvarnum]; returns total array size (error=0), and new z in daz | |
| static void parsefunc (char *, long, long); | |
| static long kasmoptimizations (long, long); | |
| static long parse_dimensions (char *st, long *daz, long writenewvar) | |
| { | |
| long i, j, k, p, z, arrind = 1; | |
| char *cptr; | |
| z = j = (*daz); | |
| if (st[j] == ' ') j++; | |
| while (st[j] == '[') | |
| { | |
| j++; | |
| for(z=j;st[z] != ']';z++) | |
| if (!st[z]) { strcpy(kasm87err,"ERROR: missing ]"); return(0); } | |
| #if 0 | |
| i = -1; //First see if array index is an "enum" style name... | |
| for(k=0,cptr=enumnam;k<enumnum;k++,cptr=&cptr[p+1]) | |
| { | |
| for(p=1;cptr[p];p++); | |
| if ((z-j == p) && (!strncmp(&st[j],cptr,p))) | |
| { i = (long)enumval[k]; break; } | |
| } | |
| if (i < 0) { i = strtol(&st[j],(char **)&z,10); z -= (long)st; } //If no, get number | |
| #else | |
| //This version handles expressions.. left old case for nostalgia? | |
| { | |
| long oglobi, ogecnt; | |
| char *nst; | |
| oglobi = globi; ogecnt = gecnt; | |
| nst = (char *)malloc(z-j+1); if (!nst) { strcpy(kasm87err,"ERROR: malloc failed"); return(0); } | |
| memcpy(nst,&st[j],z-j); nst[z-j] = 0; parsefunc(nst,-1,-1); free(nst); | |
| if (globi == -1) return(0); | |
| kasmoptimizations(ogecnt,1); | |
| if ((gecnt-ogecnt != 1) || (gasm[ogecnt].f != MOV) || ((gasm[ogecnt].r[1].r&0xf0000000) != KEDX)) | |
| { st[z] = 0; sprintf(kasm87err,"ERROR: could not simplify dimension (%s) to constant",&st[j]); st[z] = ']'; return(0); } | |
| i = ((long)ceil(globval[(gasm[ogecnt].r[1].r&0x0fffffff)>>3])); | |
| globi = oglobi; gecnt = ogecnt; gnext[oglobi] = 0x7fffffff; | |
| } | |
| #endif | |
| if (i <= 0) { strcpy(kasm87err,"ERROR: invalid dimensions"); return(0); } | |
| z++; //skip ']' | |
| if (writenewvar) | |
| { | |
| checkvarchars(newvarplc+4); | |
| *(long *)&newvarnam[newvarplc] = i; newvarplc += 4; //i is size of array dimension | |
| newvar[newvarnum].parnum++; | |
| } | |
| arrind *= i; | |
| if (st[z] == ' ') z++; | |
| j = z; | |
| } | |
| (*daz) = z; | |
| return(arrind); | |
| } | |
| static long parse_enum (char *st, long z) | |
| { | |
| double d; | |
| long k, p; | |
| z += 4; | |
| if (st[z] == ' ') z++; | |
| if (st[z] == '{') | |
| { | |
| d = 0.0; k = 0; z++; p = 1; | |
| for(;st[z];z++) | |
| { | |
| if (st[z] == ' ') z++; | |
| if (st[z] == '=') | |
| { | |
| z++; | |
| #if 0 | |
| d = strtod(&st[z],(char **)&z); z -= (long)st; | |
| #else | |
| //This version handles expressions.. left old case for nostalgia? | |
| { | |
| long oglobi, ogecnt, np; | |
| char *nst; | |
| oglobi = globi; ogecnt = gecnt; | |
| k = z; | |
| np = 0; | |
| do | |
| { | |
| if (!st[z]) { strcpy(kasm87err,"ERROR: static init missing , or ;"); return(-1); } | |
| if (st[z] == '{') { np++; } | |
| if (st[z] == '}') { np--; if (np < 0) break; } | |
| if (((st[z] == ';') || (st[z] == ',')) && (np <= 0)) break; | |
| z++; | |
| } while (1); | |
| nst = (char *)malloc(z-k+1); if (!nst) { strcpy(kasm87err,"ERROR: malloc failed"); return(-1); } | |
| memcpy(nst,&st[k],z-k); nst[z-k] = 0; parsefunc(nst,-1,-1); free(nst); | |
| if (globi == -1) return(-1); | |
| kasmoptimizations(ogecnt,1); | |
| if ((gecnt-ogecnt != 1) || (gasm[ogecnt].f != MOV) || ((gasm[ogecnt].r[1].r&0xf0000000) != KEDX)) | |
| { st[z] = 0; sprintf(kasm87err,"ERROR: could not simplify enum init (%s) to constant",&st[k]); st[z] = ']'; return(-1); } | |
| d = globval[(gasm[ogecnt].r[1].r&0x0fffffff)>>3]; | |
| globi = oglobi; gecnt = ogecnt; gnext[oglobi] = 0x7fffffff; | |
| } | |
| #endif | |
| k = 1; | |
| } | |
| if (st[z] == ' ') z++; | |
| if ((st[z] == ',') || (st[z] == '}')) | |
| { | |
| checkenumchars(enumcharplc+1); enumnam[enumcharplc++] = 0; | |
| checkenum(enumnum+1); enumval[enumnum++] = d; d++; | |
| k = 0; | |
| } | |
| else | |
| { | |
| if (k) { strcpy(kasm87err,"ERROR: ENUM syntax incorrect"); globi = -1; return(-1); } | |
| checkenumchars(enumcharplc+1); enumnam[enumcharplc++] = st[z]; | |
| } | |
| if (st[z] == '{') { p++; continue; } | |
| if (st[z] == '}') { p--; if (!p) break; } | |
| } | |
| if (st[z] != '}') { strcpy(kasm87err,"ERROR: missing }"); globi = -1; return(-1); } | |
| z++; //Skip '}' | |
| } | |
| return(z); | |
| } | |
| //writes: newvar,newvarhash,newvarnam,newvarnum,newvarplc | |
| //writes: ginitval,ginitvalnum | |
| static long parse_static (char *st, long z) | |
| { | |
| long i, j, k, p, arrind; | |
| char ch, *cptr; | |
| z += 7; | |
| do | |
| { | |
| if (st[z] == ' ') z++; | |
| if ((st[z] >= '0') && (st[z] <= '9')) | |
| { strcpy(kasm87err,"ERROR: bad static init syntax"); globi = -1; return(-1); } | |
| cptr = &st[z]; | |
| for(j=z;isvarchar(st[j]);j++); | |
| if (j == z) { strcpy(kasm87err,"ERROR: static missing variable"); globi = -1; return(-1); } | |
| ch = st[j]; st[j] = 0; | |
| for(i=newvarhash[getnewvarhash(cptr)];i>=0;i=newvar[i].hashn) | |
| if (!strcmp(cptr,&newvarnam[newvar[i].nami])) | |
| { sprintf(kasm87err,"ERROR: %s already defined",cptr); st[j] = ch; globi = -1; return(-1); } | |
| st[j] = ch; | |
| checkvarchars(newvarplc+j-z+2); | |
| checkvars(newvarnum+1); | |
| newvar[newvarnum].nami = newvarplc; | |
| //Save new variable name&index to list | |
| //if (st[z] == '\"') { memcpy(&newvarnam[newvarplc],&st[z+1],j-z-2); newvarplc += j-z-2; } | |
| // else | |
| { memcpy(&newvarnam[newvarplc],&st[z ],j-z ); newvarplc += j-z ; } | |
| newvarnam[newvarplc++] = 0; | |
| newvar[newvarnum].proti = newvarplc; | |
| newvar[newvarnum].parnum = 0; | |
| z = j; arrind = parse_dimensions(st,&z,1); | |
| if (!arrind) { globi = -1; return(-1); } | |
| if (st[z] == ' ') z++; | |
| if (st[z] == '=') //Parse static initializers | |
| { | |
| z++; j = 0; p = 0; | |
| do | |
| { | |
| while ((st[z] == '{') || (st[z] == ' ')) { p += (st[z] == '{'); z++; } | |
| checkinitvals(ginitvalnum+1); | |
| ginitval[ginitvalnum].i = arrnum+j; | |
| k = z; | |
| #if 0 | |
| if ((st[z] == '0') && (st[z+1] == 'X')) //Handle HEX numbers | |
| { | |
| ginitval[ginitvalnum].v = strtoul(&st[z],(char **)&z,0); | |
| if (ginitval[ginitvalnum].v >= 2147483648.0) ginitval[ginitvalnum].v -= 4294967296.0; | |
| } else { ginitval[ginitvalnum].v = strtod(&st[z],(char **)&z); } | |
| z -= (long)st; | |
| #else | |
| //This version handles expressions.. left old case for nostalgia? | |
| { | |
| long oglobi, ogecnt, np; | |
| char *nst; | |
| oglobi = globi; ogecnt = gecnt; | |
| np = 0; | |
| do | |
| { | |
| if (!st[z]) { strcpy(kasm87err,"ERROR: static init missing , or ;"); return(-1); } | |
| if (st[z] == '{') { np++; } | |
| if (st[z] == '}') { np--; if (np < 0) break; } | |
| if (((st[z] == ';') || (st[z] == ',')) && (np <= 0)) break; | |
| z++; | |
| } while (1); | |
| nst = (char *)malloc(z-k+1); if (!nst) { strcpy(kasm87err,"ERROR: malloc failed"); return(-1); } | |
| memcpy(nst,&st[k],z-k); nst[z-k] = 0; parsefunc(nst,-1,-1); free(nst); | |
| if (globi == -1) return(-1); | |
| kasmoptimizations(ogecnt,1); | |
| if ((gecnt-ogecnt != 1) || (gasm[ogecnt].f != MOV) || ((gasm[ogecnt].r[1].r&0xf0000000) != KEDX)) | |
| { st[z] = 0; sprintf(kasm87err,"ERROR: could not simplify static init (%s) to constant",&st[k]); st[z] = ']'; return(-1); } | |
| ginitval[ginitvalnum].v = globval[(gasm[ogecnt].r[1].r&0x0fffffff)>>3]; | |
| globi = oglobi; gecnt = ogecnt; gnext[oglobi] = 0x7fffffff; | |
| } | |
| #endif | |
| if ((j >= (arrind<<3)) && (z > k)) | |
| { sprintf(kasm87err,"ERROR: too many initializers for %s",&newvarnam[newvar[newvarnum].nami]); globi = -1; return(-1); } | |
| if (ginitval[ginitvalnum].v != 0.0) ginitvalnum++; | |
| j += 8; | |
| while ((st[z] == '}') || (st[z] == ' ')) { p -= (st[z] == '}'); z++; if (!p) break; } | |
| if (!p) break; | |
| if (st[z] != ',') { strcpy(kasm87err,"ERROR: bad static init syntax"); globi = -1; return(-1); } | |
| z++; | |
| } while (1); | |
| } | |
| newvar[newvarnum].r = arrnum+KARR; arrnum += (arrind<<3); | |
| newvar[newvarnum].maxind = arrind; | |
| newvar[newvarnum].parnum ^= -1; //1's complement | |
| k = getnewvarhash(&newvarnam[newvar[newvarnum].nami]); | |
| newvar[newvarnum].hashn = newvarhash[k]; newvarhash[k] = newvarnum; | |
| newvarnum++; | |
| if (st[z] == ' ') z++; | |
| if ((st[z] != ';') && (st[z] != ',')) { strcpy(kasm87err,"ERROR: bad static init syntax"); globi = -1; return(-1); } | |
| z++; //skip ';' or ',' | |
| } while (st[z-1] == ','); | |
| return(z); | |
| } | |
| static void parsefunc (char *st, long breaklab, long contlab) | |
| { | |
| rtyp *rp; | |
| gasmtyp tg; | |
| long i, j, k, z, oz, p, fmode, newvari, fparm, ogi, parm, negit, arrind, inquotes, isaddr; | |
| char ch, ch2, *cptr; | |
| //printf("|%s|\n",st); //Enable to debug expression splitting | |
| if (!st[0]) st = "0"; | |
| z = 0; fmode = NOP; newvari = -1; isaddr = 0; | |
| prebegit:; | |
| ogi = globi; | |
| checkops(ogi+1); gnext[ogi] = 0x7fffffff; //write ending val to gnext for null expressions (Ex:GOTO) | |
| begit:; | |
| if (!z) | |
| { | |
| if ((st[z] == '-') || (st[z] == '+')) //Hack for first '-' or '+' to fix priority of "-x^2" | |
| { | |
| //Insert '0' before '-' or '+' if sign is 1st char in expression | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = globi*8+KECX; gasm[gecnt].r[1].r = gccnt*8+KEDX; gasm[gecnt].r[2].r = KUNUSED; gasm[gecnt].n = 1; gasm[gecnt].f = MOV; gecnt++; | |
| checkops(gccnt+1); globval[gccnt++] = 0.0; | |
| checkops(globi+2); gnext[globi] = globi+1; globi++; gop[globi] = NUL; | |
| } | |
| negit = 1; | |
| } | |
| else | |
| { | |
| //Hack to make "2^(+3)" not translate as: "2+3" | |
| //handle unary '-' operator. 0:nothing special, 1:negate next node! | |
| for(negit=1;(st[z] == '+') || (st[z] == '-');z++) if (st[z] == '-') negit ^= ((+1)^(-1)); | |
| } | |
| while (st[z]) | |
| { | |
| oz = z; | |
| switch(st[z]) | |
| { | |
| case '(': | |
| p = 1; z++; oz = z; parm = 1; i = globi; inquotes = 0; | |
| for(;st[z];z++) | |
| { | |
| if ((st[z] == '\"') && (st[z-1] != '\\')) inquotes ^= 1; | |
| if (inquotes) continue; | |
| if (st[z] == '(') { p++; continue; } | |
| if (st[z] == ')') { p--; if (!p) break; } | |
| if ((st[z] == ',') && (p == 1)) | |
| { | |
| if (z == oz) | |
| { | |
| //Insert '0' if blank param | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = globi*8+KECX; gasm[gecnt].r[1].r = gccnt*8+KEDX; gasm[gecnt].r[2].r = KUNUSED; gasm[gecnt].n = 1; gasm[gecnt].f = MOV; gecnt++; | |
| checkops(gccnt+1); globval[gccnt++] = 0.0; | |
| checkops(globi+2); gnext[globi] = globi+1; globi++; gop[globi] = NUL; | |
| } | |
| else | |
| { | |
| ch = st[z]; st[z] = 0; parsefunc(&st[oz],breaklab,contlab); st[z] = ch; if (globi == -1) return; | |
| } | |
| parm++; oz = z+1; | |
| } | |
| } | |
| if (z == oz) | |
| { | |
| //Insert '0' if blank param | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = globi*8+KECX; gasm[gecnt].r[1].r = gccnt*8+KEDX; gasm[gecnt].r[2].r = KUNUSED; gasm[gecnt].n = 1; gasm[gecnt].f = MOV; gecnt++; | |
| checkops(gccnt+1); globval[gccnt++] = 0.0; | |
| checkops(globi+2); gnext[globi] = globi+1; globi++; gop[globi] = NUL; | |
| } | |
| else | |
| { | |
| ch = st[z]; st[z] = 0; parsefunc(&st[oz],breaklab,contlab); st[z] = ch; if (globi == -1) return; | |
| } | |
| if (st[z] != ')') { strcpy(kasm87err,"ERROR: missing )"); globi = -1; return; } | |
| z++; //Skip ')' | |
| if ((fmode == LOG) && (parm == 2)) fmode = LOGB; //Choose function based on parm # | |
| fparm = -1; | |
| if (fmode < PARAM1) ; | |
| else if ((fmode >= PARAM1) && (fmode < PARAM2)) fparm = 1; | |
| else if ((fmode >= PARAM2) && (fmode < PARAM3)) fparm = 2; | |
| else if ((fmode >= PARAM3) && (fmode != USERFUNC)) ; | |
| else if (newvari >= 0) fparm = newvar[newvari].parnum; | |
| //Hack to support function overloading :) | |
| tg.g = newvari; | |
| if ((fmode == USERFUNC) && (newvari >= 0) && (parm != fparm)) | |
| { | |
| if (newvarnam[newvar[newvari].proti+fparm-1] == 'e') fparm = parm; | |
| else | |
| { | |
| cptr = &newvarnam[newvar[newvari].nami]; | |
| while ((parm != fparm) || (stricmp(cptr,&newvarnam[newvar[newvari].nami]))) | |
| { | |
| newvari = newvar[newvari].hashn; | |
| if (newvari < 0) { fparm = -1; break; } //function with same # parms not found :/ | |
| fparm = newvar[newvari].parnum; | |
| } | |
| } | |
| } | |
| if (parm != fparm) | |
| { | |
| strcpy(kasm87err,"ERROR: "); | |
| tg.f = fmode; getfuncnam(&tg,&kasm87err[strlen(kasm87err)]); | |
| sprintf(&kasm87err[strlen(kasm87err)]," doesn't take %d param",parm); | |
| if (parm != 1) strcat(kasm87err,"s"); | |
| globi = -1; return; | |
| } | |
| if (fmode == USERFUNC) | |
| { | |
| long skip4ellipses = 0; | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = i*8+KECX; | |
| if (fparm > 2) { checkrxi(numrxi+fparm-2); memset(&rxi[numrxi],0,sizeof(rxi[0])*(fparm-2)); } | |
| for(j=i,k=0;k<fparm;j=gnext[j],k++) | |
| { | |
| if (k < 2) rp = &gasm[gecnt].r[k+1]; else rp = &rxi[numrxi+k-2]; | |
| if ((unsigned)j > (unsigned)globi) | |
| { | |
| strcpy(kasm87err,"ERROR: "); | |
| tg.f = fmode; getfuncnam(&tg,&kasm87err[strlen(kasm87err)]); | |
| sprintf(&kasm87err[strlen(kasm87err)]," param %d: pointer invalid",k+1); | |
| globi = -1; return; | |
| } | |
| rp->r = j*8+KECX; | |
| if (newvarnam[newvar[newvari].proti+k] == 'e') skip4ellipses = 1; | |
| if (skip4ellipses) continue; | |
| if (newvarnam[newvar[newvari].proti+k] >= 'a') continue; | |
| for(p=gecnt-1;p>=0;p--) //pointer params (double* or char*) must not be an expression | |
| if (gasm[p].r[0].r == rp->r) | |
| { | |
| if (gasm[p].f != MOV) | |
| { | |
| strcpy(kasm87err,"ERROR: "); | |
| tg.f = fmode; getfuncnam(&tg,&kasm87err[strlen(kasm87err)]); | |
| sprintf(&kasm87err[strlen(kasm87err)]," param %d: pointer invalid",k+1); | |
| globi = -1; return; | |
| } | |
| //if ((gasm[p].r[1].r&0xf0000000) == KEDX) break; //FUKFUKFUK | |
| (*rp) = gasm[p].r[1]; | |
| break; | |
| } | |
| } | |
| if (fparm > 2) { gasm[gecnt].rxi = numrxi; numrxi += fparm-2; } | |
| gasm[gecnt].f = fmode; | |
| if (newvari >= 0) gasm[gecnt].g = newvari; else gasm[gecnt].g = 0; | |
| gasm[gecnt].n = fparm; | |
| gecnt++; | |
| } | |
| else if (fmode != NOP) | |
| { | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = gasm[gecnt].r[1].r = i*8+KECX; | |
| if (fmode < PARAM2) { gasm[gecnt].n = 1; gasm[gecnt].r[2].r = KUNUSED; } | |
| else { gasm[gecnt].n = 2; gasm[gecnt].r[2].r = gnext[i]*8+KECX; } | |
| gasm[gecnt].f = fmode; | |
| if (newvari >= 0) gasm[gecnt].g = newvari; else gasm[gecnt].g = 0; | |
| gecnt++; | |
| } | |
| gnext[i] = globi; | |
| if (negit < 0) | |
| { | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = gasm[gecnt].r[1].r = i*8+KECX; gasm[gecnt].r[2].r = KUNUSED; gasm[gecnt].n = 1; gasm[gecnt].f = NEGMOV; gecnt++; | |
| } | |
| fmode = NOP; | |
| break; | |
| //Parse operators | |
| case '=': if (st[z+1] == '=') { gop[globi] = EQU; z += 2; goto begit; } break; | |
| case '!': if (st[z+1] == '=') { gop[globi] = NEQU; z += 2; goto begit; } break; | |
| case '&': if (st[z+1] == '&') { gop[globi] = LAND; z += 2; goto begit; } | |
| if (isvarchar(st[z+1])) isaddr = 1; z++; break; | |
| case '|': if (st[z+1] == '|') { gop[globi] = LOR; z += 2; goto begit; } break; | |
| case '^': gop[globi] = POW; z++; goto begit; | |
| case '*': gop[globi] = TIMES; z++; goto begit; | |
| case '/': gop[globi] = SLASH; z++; goto begit; | |
| case '%': gop[globi] = PERC; z++; goto begit; | |
| case '+': gop[globi] = PLUS; z++; goto begit; | |
| case '-': gop[globi] = MINUS; z++; goto begit; | |
| case '<': if (st[z+1] == '=') { gop[globi] = LESEQ; z += 2; } else { gop[globi] = LES; z++; } goto begit; | |
| case '>': if (st[z+1] == '=') { gop[globi] = MOREQ; z += 2; } else { gop[globi] = MOR; z++; } goto begit; | |
| //Parse constants | |
| case '0': case '1': case '2': case '3': case '4': | |
| case '5': case '6': case '7': case '8': case '9': case '.': | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = globi*8+KECX; gasm[gecnt].r[1].r = gccnt*8+KEDX; gasm[gecnt].r[2].r = KUNUSED; gasm[gecnt].n = 1; gasm[gecnt].f = MOV; gecnt++; | |
| checkops(gccnt+1); | |
| if ((st[z] == '0') && (st[z+1] == 'X')) //Handle HEX numbers | |
| { | |
| globval[gccnt] = strtoul(&st[z],(char **)&z,0); | |
| if (globval[gccnt] >= 2147483648.0) globval[gccnt] -= 4294967296.0; | |
| } | |
| else | |
| globval[gccnt] = strtod(&st[z],(char **)&z)*(double)negit; | |
| gccnt++; z -= (long)st; | |
| checkops(globi+2); gnext[globi] = globi+1; globi++; gop[globi] = NUL; | |
| break; | |
| //Parse functions&statements | |
| case 'A': if (!strncmp(&st[z],"ACOS(",5)) { fmode = ACOS; z += 4; } | |
| else if (!strncmp(&st[z],"ASIN(",5)) { fmode = ASIN; z += 4; } | |
| else if (!strncmp(&st[z],"ATAN2(",6)) { fmode = ATAN2; z += 5; } | |
| else if (!strncmp(&st[z],"ATAN(",5)) { fmode = ATAN; z += 4; } | |
| else if (!strncmp(&st[z],"ATN(",4)) { fmode = ATAN; z += 3; } | |
| else if (!strncmp(&st[z],"ABS(",4)) { fmode = FABS; z += 3; } break; | |
| case 'B': if (!strncmp(&st[z],"BREAK;",6)) | |
| { | |
| z += 6; | |
| if (breaklab < 0) { sprintf(kasm87err,"ERROR: BREAK not allowed outside loop"); globi = -1; return; } | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].f = GOTO; | |
| gasm[gecnt].r[0].r = KUNUSED; | |
| gasm[gecnt].r[1].r = breaklab+KEIP; | |
| gasm[gecnt].n = 1; | |
| gecnt++; | |
| } break; | |
| case 'C': if (!strncmp(&st[z],"COS(",4)) { fmode = COS; z += 3; } | |
| else if (!strncmp(&st[z],"CEIL(",5)) { fmode = CEIL; z += 4; } | |
| else if (!strncmp(&st[z],"CONTINUE;",9)) | |
| { | |
| z += 9; | |
| if (contlab < 0) { sprintf(kasm87err,"ERROR: CONTINUE not allowed outside loop"); globi = -1; return; } | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].f = GOTO; | |
| gasm[gecnt].r[0].r = KUNUSED; | |
| gasm[gecnt].r[1].r = contlab+KEIP; | |
| gasm[gecnt].n = 1; | |
| gecnt++; | |
| } break; | |
| case 'D': if ((!strncmp(&st[z],"DO",2)) && (!isvarchar(st[z+2]))) | |
| { | |
| z += 2; | |
| //l1: (fmode) | |
| // <code_true> | |
| //l2: (fmode+1) | |
| // if (expression) goto l1 | |
| //l3: (fmode+2) | |
| //Reserve label # | |
| checklabs(numlabels+3); | |
| fmode = numlabels; numlabels += 3; //fmode used for unrelated temp here | |
| //Insert label | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].f = NUL; gasm[gecnt].r[0].r = fmode+KEIP; gecnt++; | |
| if ((p = findscope(st,z,&oz,&z)) < 0) | |
| { strcpy(kasm87err,"ERROR: DO needs statement"); globi = -1; return; } | |
| ch = st[z]; st[z] = 0; parsefunc(&st[oz],fmode+2,fmode+1); st[z] = ch; if (globi == -1) return; | |
| z = p; | |
| //Insert label | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].f = NUL; gasm[gecnt].r[0].r = fmode+1+KEIP; gecnt++; | |
| if ((strncmp(&st[z],"WHILE",5)) || (isvarchar(st[z+5])) || (!st[z+5])) | |
| { strcpy(kasm87err,"ERROR: DO needs WHILE"); globi = -1; return; } | |
| z += 5; | |
| p = 1; z++; oz = z; i = globi; inquotes = 0; | |
| for(;st[z];z++) | |
| { | |
| if ((st[z] == '\"') && ((!z) || (st[z-1] != '\\'))) inquotes ^= 1; | |
| if (inquotes) continue; | |
| if (st[z] == '(') { p++; continue; } | |
| if (st[z] == ')') { p--; if (!p) break; } | |
| if ((st[z] == ',') && (p == 1)) | |
| { sprintf(kasm87err,"ERROR: DO takes 1 param"); globi = -1; return; } | |
| } | |
| ch = st[z]; st[z] = 0; parsefunc(&st[oz],breaklab,contlab); st[z] = ch; if (globi == -1) return; | |
| if (st[z] != ')') { strcpy(kasm87err,"ERROR: missing )"); globi = -1; return; } | |
| z++; //Skip ')' | |
| if (st[z] != ';') { strcpy(kasm87err,"ERROR: missing ; after WHILE"); globi = -1; return; } | |
| z++; | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = KUNUSED; | |
| gasm[gecnt].r[1].r = fmode+KEIP; | |
| gasm[gecnt].r[2].r = i*8+KECX; | |
| gasm[gecnt].n = 2; | |
| gasm[gecnt].f = IF1; gecnt++; | |
| gnext[i] = globi; | |
| //Insert label | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].f = NUL; gasm[gecnt].r[0].r = fmode+2+KEIP; gecnt++; | |
| fmode = NOP; | |
| } break; | |
| case 'E': if (!strncmp(&st[z],"EXP(",4)) { fmode = EXP; z += 3; } | |
| else if ((!strncmp(&st[z],"ENUM",4)) && (!isvarchar(st[z+4]))) { z = parse_enum(st,z); if (globi < 0) return; } | |
| break; | |
| case 'F': if (!strncmp(&st[z],"FABS(",5)) { fmode = FABS; z += 4; } | |
| else if (!strncmp(&st[z],"FACT(",5)) { fmode = FACT; z += 4; } | |
| else if (!strncmp(&st[z],"FADD(",5)) { fmode = FADD; z += 4; } | |
| else if (!strncmp(&st[z],"FLOOR(",6)) { fmode = FLOOR; z += 5; } | |
| else if (!strncmp(&st[z],"FMOD(",5)) { fmode = FMOD; z += 4; } | |
| else if (!strncmp(&st[z],"FOR(",4)) | |
| { | |
| z += 4; | |
| //Do Initial Condition of (;;) | |
| p = 1; oz = z; inquotes = 0; | |
| for(;st[z];z++) | |
| { | |
| if ((st[z] == '\"') && ((!z) || (st[z-1] != '\\'))) inquotes ^= 1; | |
| if (inquotes) continue; | |
| if (st[z] == '(') { p++; continue; } | |
| if (st[z] == ')') { p--; if (!p) { strcpy(kasm87err,"ERROR: FOR needs 3 fields"); globi = -1; return; } continue; } | |
| if ((st[z] == ';') && (p == 1)) break; | |
| } | |
| if (st[z] != ';') { strcpy(kasm87err,"ERROR: FOR missing ;"); globi = -1; return; } | |
| z++; //Skip ';' | |
| ch = st[z]; st[z] = 0; parsefunc(&st[oz],breaklab,contlab); st[z] = ch; if (globi == -1) return; | |
| // Init; | |
| //l1: (fmode) | |
| // if !(expression) goto l3 | |
| // <code_true> | |
| //l2: (fmode+1) | |
| // Iterate; | |
| // goto l1 | |
| //l3: (fmode+2) | |
| //Reserve label # | |
| checklabs(numlabels+3); | |
| fmode = numlabels; numlabels += 3; //fmode used for unrelated temp here | |
| //Insert label | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].f = NUL; gasm[gecnt].r[0].r = fmode+KEIP; gecnt++; | |
| p = 1; oz = z; i = globi; inquotes = 0; | |
| for(;st[z];z++) | |
| { | |
| if ((st[z] == '\"') && ((!z) || (st[z-1] != '\\'))) inquotes ^= 1; | |
| if (inquotes) continue; | |
| if (st[z] == '(') { p++; continue; } | |
| if (st[z] == ')') { p--; if (!p) { strcpy(kasm87err,"ERROR: FOR needs 3 fields"); globi = -1; return; } continue; } | |
| if ((st[z] == ';') && (p == 1)) break; | |
| } | |
| ch = st[z]; st[z] = 0; parsefunc(&st[oz],breaklab,contlab); st[z] = ch; if (globi == -1) return; | |
| if (st[z] != ';') { strcpy(kasm87err,"ERROR: FOR missing ;"); globi = -1; return; } | |
| z++; //Skip ';' | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = KUNUSED; | |
| gasm[gecnt].r[1].r = fmode+2+KEIP; | |
| gasm[gecnt].r[2].r = i*8+KECX; | |
| gasm[gecnt].n = 2; | |
| gasm[gecnt].f = IF0; gecnt++; | |
| gnext[i] = globi; | |
| //Save 3rd param of (;;) as j&k for Iteration (which is done after {}) | |
| j = z; p = 1; inquotes = 0; | |
| for(;st[z];z++) | |
| { | |
| if ((st[z] == '\"') && ((!z) || (st[z-1] != '\\'))) inquotes ^= 1; | |
| if (inquotes) continue; | |
| if (st[z] == '(') { p++; continue; } | |
| if (st[z] == ')') { p--; if (!p) break; } | |
| } | |
| if (st[z] != ')') { strcpy(kasm87err,"ERROR: FOR missing )"); globi = -1; return; } | |
| z++; //Skip ')' | |
| k = z; | |
| if ((p = findscope(st,z,&oz,&z)) < 0) | |
| { strcpy(kasm87err,"ERROR: FOR needs statement"); globi = -1; return; } | |
| ch = st[z]; st[z] = 0; parsefunc(&st[oz],fmode+2,fmode+1); st[z] = ch; if (globi == -1) return; | |
| z = p; | |
| //Insert label | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].f = NUL; gasm[gecnt].r[0].r = fmode+1+KEIP; gecnt++; | |
| //Do Iteration of (;;) | |
| ch2 = st[k-1]; st[k-1] = ';'; | |
| ch = st[k]; st[k] = 0; parsefunc(&st[j],breaklab,contlab); st[k] = ch; | |
| st[k-1] = ch2; | |
| if (globi == -1) return; | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = KUNUSED; | |
| gasm[gecnt].r[1].r = fmode+KEIP; gasm[gecnt].n = 1; gasm[gecnt].f = GOTO; gecnt++; | |
| //Insert label | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].f = NUL; gasm[gecnt].r[0].r = fmode+2+KEIP; gecnt++; | |
| fmode = NOP; | |
| } break; | |
| case 'G': if (!strncmp(&st[z],"GOTO ",5)) | |
| { | |
| z += 5; | |
| for(j=z;isvarchar(st[j]);j++); | |
| if (j-z <= 0) { sprintf(kasm87err,"ERROR: GOTO needs label"); globi = -1; return; } | |
| ch = st[j]; st[j] = 0; | |
| for(i=0,cptr=newlabnam;i<newlabnum;i++,cptr=&cptr[k+1]) | |
| { | |
| for(k=1;cptr[k];k++); | |
| if (!strcmp(&st[z],cptr)) break; | |
| } | |
| if (i >= newlabnum) | |
| { | |
| checklabchars(newlabplc+j-z+2); | |
| checklabs(numlabels+1); | |
| strcpy(&newlabnam[newlabplc],&st[z]); newlabplc += j-z+1; | |
| newlabind[newlabnum++] = (numlabels|0x80000000); i = numlabels++; | |
| } else i = (newlabind[i]&0x7fffffff); | |
| st[j] = ch; | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = KUNUSED; | |
| gasm[gecnt].r[1].r = i+KEIP; gasm[gecnt].f = GOTO; gasm[gecnt].n = 1; gecnt++; | |
| z = j+1; //skip label | |
| } break; | |
| case 'I': if (!strncmp(&st[z],"INT(",4)) { fmode = ROUND0; z += 3; } | |
| else if ((!strncmp(&st[z],"IF",2)) && (!isvarchar(st[z+2])) && (st[z+2])) | |
| { | |
| z += 2; | |
| checklabs(numlabels+1); | |
| fmode = numlabels; numlabels++; //fmode used for unrelated temp here | |
| p = 1; z++; oz = z; i = globi; inquotes = 0; | |
| for(;st[z];z++) | |
| { | |
| if ((st[z] == '\"') && ((!z) || (st[z-1] != '\\'))) inquotes ^= 1; | |
| if (inquotes) continue; | |
| if (st[z] == '(') { p++; continue; } | |
| if (st[z] == ')') { p--; if (!p) break; } | |
| if (((st[z] == ',') && (p == 1)) || (st[z] == ';')) | |
| { strcpy(kasm87err,"ERROR: IF condition invalid"); globi = -1; return; } | |
| } | |
| ch = st[z]; st[z] = 0; parsefunc(&st[oz],breaklab,contlab); st[z] = ch; if (globi == -1) return; | |
| if (st[z] != ')') { strcpy(kasm87err,"ERROR: IF missing )"); globi = -1; return; } | |
| z++; //Skip ')' | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = KUNUSED; | |
| gasm[gecnt].r[1].r = fmode+KEIP; | |
| gasm[gecnt].r[2].r = i*8+KECX; | |
| gasm[gecnt].n = 2; | |
| gasm[gecnt].f = IF0; gecnt++; | |
| gnext[i] = globi; | |
| i = globi; | |
| if ((p = findscope(st,z,&oz,&z)) < 0) | |
| { strcpy(kasm87err,"ERROR: IF needs statement"); globi = -1; return; } | |
| ch = st[z]; st[z] = 0; parsefunc(&st[oz],breaklab,contlab); st[z] = ch; if (globi == -1) return; | |
| z = p; | |
| if ((!strncmp(&st[z],"ELSE",4)) && (!isvarchar(st[z+4]))) | |
| { | |
| z += 4; | |
| //if !(expression) goto l1 | |
| // <code_true> | |
| // goto l2 | |
| //l1: | |
| // <code_false> | |
| //l2: | |
| checklabs(numlabels+2); | |
| //Insert goto so true case skips 'else' part | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = KUNUSED; | |
| gasm[gecnt].r[1].r = numlabels+KEIP; gasm[gecnt].f = GOTO; gasm[gecnt].n = 1; numlabels++; | |
| gecnt++; | |
| //Insert label to begin else part | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].f = NUL; gasm[gecnt].r[0].r = fmode+KEIP; gecnt++; | |
| fmode = numlabels-1; | |
| i = globi; | |
| //Some cases to verify code with: | |
| //"(x)if(x<0)r=0;else if(x<1) r=x; else r=1; r" | |
| //"(x)if(x<0)r=0;else{if(x<1) r=x; else r=1;} r" | |
| //"(x)if(x<0)r=0;else{if(x<1){r=x;}else{r=1;}}r" | |
| //"(x)if(x<0)r=0;else if(x<1) r=x; else{r=1;} r" | |
| if (st[z] == ' ') z++; | |
| oz = z; p = 0; | |
| for(;;z++) | |
| { | |
| if (!st[z]) { strcpy(kasm87err,"ERROR: ELSE needs statement"); globi = -1; return; } | |
| if (st[z] == '{') { p++; continue; } | |
| if (st[z] == '}') p--; | |
| if (((st[z] == '}') || (st[z] == ';')) && (p <= 0)) | |
| { | |
| z++; | |
| if ((!strncmp(&st[z],"ELSE",4)) && (!isvarchar(st[z+4]))) { z += 4-1; continue; } | |
| break; | |
| } | |
| } | |
| if (st[oz] == '{') { oz++; z--; } | |
| ch = st[z]; st[z] = 0; parsefunc(&st[oz],breaklab,contlab); st[z] = ch; if (globi == -1) return; | |
| if (st[z] == '}') z++; | |
| } | |
| //Insert label at } | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].f = NUL; gasm[gecnt].r[0].r = fmode+KEIP; gecnt++; | |
| fmode = NOP; | |
| } break; | |
| case 'L': if (!strncmp(&st[z],"LOG(",4)) { fmode = LOG; z += 3; } break; | |
| case 'M': if (!strncmp(&st[z],"MIN(",4)) { fmode = MIN; z += 3; } | |
| else if (!strncmp(&st[z],"MAX(",4)) { fmode = MAX; z += 3; } break; | |
| case 'N': if ((!strncmp(&st[z],"NRND",4)) && (!isvarchar(st[z+4]))) | |
| { | |
| z += 4; | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = globi*8+KECX; gasm[gecnt].r[1].r = gasm[gecnt].r[2].r = KUNUSED; gasm[gecnt].f = NRND; gecnt++; | |
| checkops(globi+2); gnext[globi] = globi+1; globi++; gop[globi] = NUL; | |
| } break; | |
| case 'P': if (!strncmp(&st[z],"POW(",4)) { fmode = POW; z += 3; } | |
| else if ((!strncmp(&st[z],"PI",2)) && (!isvarchar(st[z+2]))) | |
| { | |
| z += 2; | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = globi*8+KECX; gasm[gecnt].r[1].r = gccnt*8+KEDX; gasm[gecnt].r[2].r = KUNUSED; gasm[gecnt].n = 1; gasm[gecnt].f = MOV; gecnt++; | |
| checkops(gccnt+1); globval[gccnt++] = PI*(double)negit; | |
| checkops(globi+2); gnext[globi] = globi+1; globi++; gop[globi] = NUL; | |
| } break; | |
| case 'R': if ((!strncmp(&st[z],"RND",3)) && (!isvarchar(st[z+3]))) | |
| { | |
| z += 3; | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = globi*8+KECX; gasm[gecnt].r[1].r = gasm[gecnt].r[2].r = KUNUSED; gasm[gecnt].f = RND; gecnt++; | |
| checkops(globi+2); gnext[globi] = globi+1; globi++; gop[globi] = NUL; | |
| } | |
| else if ((!strncmp(&st[z],"RETURN",6)) && (!isvarchar(st[z+6]))) | |
| { | |
| z += 6; | |
| if (st[z] == ' ') z++; | |
| p = 0; oz = z; i = globi; inquotes = 0; | |
| for(;st[z];z++) | |
| { | |
| if ((st[z] == '\"') && ((!z) || (st[z-1] != '\\'))) inquotes ^= 1; | |
| if (inquotes) continue; | |
| if (st[z] == '(') { p++; continue; } | |
| if (st[z] == ')') { p--; continue; } | |
| if ((p == 0) && (st[z] == ';')) break; | |
| if ((p == 1) && (st[z] == ',')) | |
| { strcpy(kasm87err,"ERROR: RETURN ',' invalid syntax"); globi = -1; return; } | |
| } | |
| ch = st[z]; st[z] = 0; parsefunc(&st[oz],-1,-1); st[z] = ch; if (globi == -1) return; | |
| if (st[z] != ';') { strcpy(kasm87err,"ERROR: RETURN missing ;"); globi = -1; return; } | |
| z++; //Skip ';' | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = KUNUSED; | |
| gasm[gecnt].r[1].r = i*8+KECX; | |
| gasm[gecnt].r[2].r = KUNUSED; | |
| gasm[gecnt].n = 1; | |
| gasm[gecnt].f = RETURN; gecnt++; | |
| } break; | |
| case 'S': if (!strncmp(&st[z],"SQRT(",5)) { fmode = SQRT; z += 4; } | |
| //else if (!strncmp(&st[z],"SQR(",4)) { fmode = SQRT; z += 3; } | |
| else if (!strncmp(&st[z],"SIN(",4)) { fmode = SIN; z += 3; } | |
| else if (!strncmp(&st[z],"SGN(",4)) { fmode = SGN; z += 3; } | |
| else if ((!strncmp(&st[z],"STATIC",6)) && (!isvarchar(st[z+6]))) { z = parse_static(st,z); if (globi < 0) return; goto prebegit; } | |
| break; | |
| case 'T': if (!strncmp(&st[z],"TAN(",4)) { fmode = TAN; z += 3; } break; | |
| case 'U': if (!strncmp(&st[z],"UNIT(",5)) { fmode = UNIT; z += 4; } break; | |
| case 'W': if ((!strncmp(&st[z],"WHILE",5)) && (!isvarchar(st[z+5])) && (st[z+5])) | |
| { | |
| z += 5; | |
| //l1: (fmode) | |
| // if !(expression) goto l2 | |
| // <code_true> | |
| // goto l1 | |
| //l2: (fmode+1) | |
| //Reserve label # | |
| checklabs(numlabels+2); | |
| fmode = numlabels; numlabels += 2; //fmode used for unrelated temp here | |
| //Insert label | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].f = NUL; gasm[gecnt].r[0].r = fmode+KEIP; gecnt++; | |
| p = 1; z++; oz = z; i = globi; inquotes = 0; | |
| for(;st[z];z++) | |
| { | |
| if ((st[z] == '\"') && ((!z) || (st[z-1] != '\\'))) inquotes ^= 1; | |
| if (inquotes) continue; | |
| if (st[z] == '(') { p++; continue; } | |
| if (st[z] == ')') { p--; if (!p) break; } | |
| if ((st[z] == ',') && (p == 1)) | |
| { strcpy(kasm87err,"ERROR: WHILE takes 1 param"); globi = -1; return; } | |
| } | |
| ch = st[z]; st[z] = 0; parsefunc(&st[oz],breaklab,contlab); st[z] = ch; if (globi == -1) return; | |
| if (st[z] != ')') { strcpy(kasm87err,"ERROR: WHILE missing )"); globi = -1; return; } | |
| z++; //Skip ')' | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = KUNUSED; | |
| gasm[gecnt].r[1].r = fmode+1+KEIP; | |
| gasm[gecnt].r[2].r = i*8+KECX; | |
| gasm[gecnt].n = 2; | |
| gasm[gecnt].f = IF0; gecnt++; | |
| gnext[i] = globi; | |
| if ((p = findscope(st,z,&oz,&z)) < 0) | |
| { strcpy(kasm87err,"ERROR: WHILE needs statement"); globi = -1; return; } | |
| ch = st[z]; st[z] = 0; parsefunc(&st[oz],fmode+1,fmode); st[z] = ch; if (globi == -1) return; | |
| z = p; | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = KUNUSED; | |
| gasm[gecnt].r[1].r = fmode+KEIP; gasm[gecnt].n = 1; gasm[gecnt].f = GOTO; gecnt++; | |
| //Insert label | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].f = NUL; gasm[gecnt].r[0].r = fmode+1+KEIP; gecnt++; | |
| fmode = NOP; | |
| } break; | |
| case '{': | |
| if ((p = findscope(st,z,&oz,&z)) < 0) { strcpy(kasm87err,"ERROR: missing }"); globi = -1; return; } | |
| ch = st[z]; st[z] = 0; parsefunc(&st[oz],breaklab,contlab); st[z] = ch; if (globi == -1) return; | |
| z = p; fmode = NOP; break; | |
| case ';': case ',': z++; fmode = NOP; break; | |
| case ' ': z++; break; | |
| default: break; | |
| } | |
| if (oz != z) continue; //Token found: no more processing | |
| //Undefined token... see if it's a variable/function name | |
| cptr = &st[z]; | |
| if (st[z] == '\"') //Support filenames | |
| { for(z++;(st[z]) && ((st[z] != '\"') || (st[z-1] == '\\'));z++); if (st[z]) z++; } | |
| else { while (isvarchar(st[z])) z++; } | |
| p = z; j = z-oz; | |
| ch = st[p]; st[p] = 0; | |
| for(i=newvarhash[getnewvarhash(cptr)];i>=0;i=newvar[i].hashn) | |
| { | |
| //printf("|%s| vs. |%s|\n",cptr,&newvarnam[newvar[i].nami]); //nice for debugging | |
| if (!strcmp(cptr,&newvarnam[newvar[i].nami])) break; | |
| } | |
| st[p] = ch; | |
| if (i < 0) z = oz; | |
| else | |
| { | |
| if (st[z] == '[') | |
| { | |
| char *cptr2; | |
| p = 1; z++; oz = z; parm = 1; //pal[c[x][y][z]] | |
| for(;st[z];z++) | |
| { | |
| if (st[z] == '[') { p++; continue; } | |
| if (st[z] == ']') | |
| { | |
| p--; if (p) continue; | |
| if ((st[z+1] == ' ') && (st[z+2] == '[')) z++; | |
| if (st[z+1] == '[') { parm++; continue; } | |
| break; | |
| } | |
| } | |
| if (!st[z]) { sprintf(kasm87err,"ERROR: %s missing ]",&newvarnam[newvar[i].nami]); globi = -1; return; } | |
| if (z == oz) { sprintf(kasm87err,"ERROR: Blank param in %s[]",&newvarnam[newvar[i].nami]); globi = -1; return; } | |
| if (parm > (~newvar[i].parnum)) { sprintf(kasm87err,"ERROR: Array (%s) too many dimensions",&newvarnam[newvar[i].nami]); globi = -1; return; } | |
| if (parm == 1) | |
| { | |
| //Doing enum/strtol check here is merely an optimization... (not necessary) | |
| arrind = -1; //First see if array index is an "enum" style name... | |
| for(k=0,cptr2=enumnam;k<enumnum;k++,cptr2=&cptr2[p+1]) | |
| { | |
| for(p=1;cptr2[p];p++); | |
| if ((z-oz == p) && (!strncmp(&st[oz],cptr2,p))) | |
| { arrind = (long)enumval[k]; p = z; break; } | |
| } | |
| if (arrind < 0) { arrind = strtol(&st[oz],(char **)&p,10); p -= (long)st; } | |
| } | |
| if (p == z) | |
| { | |
| if ((unsigned long)arrind >= (unsigned long)newvar[i].maxind) | |
| { sprintf(kasm87err,"ERROR: array index out of bounds (%s)",&newvarnam[newvar[i].nami]); globi = -1; return; } | |
| z++; | |
| } | |
| else //Array parameter is not enum, constant, or < 2 dimens; treat index as expression | |
| { | |
| k = globi; | |
| #if 0 | |
| ch = st[z]; st[z] = 0; parsefunc(&st[oz],breaklab,contlab); st[z] = ch; if (globi == -1) return; | |
| if (st[z] != ']') { sprintf(kasm87err,"ERROR: array (%s) missing ]",&newvarnam[newvar[i].nami]); globi = -1; return; } | |
| z++; //Skip ']' | |
| #else | |
| p = 1; fparm = 0; //multidimensional arrays | |
| for(z=oz;st[z];z++) | |
| { | |
| if (st[z] == '[') { p++; continue; } | |
| if (st[z] == ']') | |
| { | |
| p--; if (p) continue; | |
| p = globi; | |
| ch = st[z]; st[z] = 0; parsefunc(&st[oz],breaklab,contlab); st[z] = ch; | |
| if (globi == -1) return; | |
| if (fparm < parm-1) | |
| { | |
| long l; //if (parm > 2) | |
| //p = int(p); | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = gasm[gecnt].r[1].r = p*8+KECX; | |
| gasm[gecnt].r[2].r = KUNUSED; gasm[gecnt].n = 1; | |
| gasm[gecnt].f = ROUND0_32; gecnt++; //(array indices only need 32-bit precision) | |
| gnext[p] = globi; | |
| //p *= product_right; | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = gasm[gecnt].r[1].r = p*8+KECX; | |
| gasm[gecnt].r[2].r = gccnt*8+KEDX; checkops(gccnt+1); | |
| //static buf3d[5][3][2]; | |
| //? = buf3d[a]; a | |
| //? = buf3d[a][b]; int(a)*2 + b | |
| //? = buf3d[a][b][c]; int(a)*3*2 + int(b)*2 + c | |
| globval[gccnt] = 1.0; //dimension combined multiplier | |
| for(l=(~newvar[i].parnum)+fparm-parm+1;l<(~newvar[i].parnum);l++) | |
| globval[gccnt] *= ((long *)&newvarnam[newvar[i].proti])[l]; | |
| gccnt++; | |
| gasm[gecnt].n = 2; | |
| gasm[gecnt].f = TIMES; gecnt++; | |
| gnext[p] = globi; | |
| } | |
| if (fparm > 0) | |
| { | |
| //k += p; | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = gasm[gecnt].r[1].r = k*8+KECX; | |
| gasm[gecnt].r[2].r = p*8+KECX; gasm[gecnt].n = 2; | |
| gasm[gecnt].f = PLUS; gecnt++; | |
| gnext[k] = p; | |
| } | |
| p = 0; | |
| if ((st[z+1] == ' ') && (st[z+2] == '[')) z++; | |
| if (st[z+1] == '[') { fparm++; oz = z+2; continue; } | |
| z++; break; | |
| } | |
| } | |
| #endif | |
| //FUK: combine this with other =,*=,... code? | |
| if (((st[z] == '=') && (st[z+1] != '=')) || // = | |
| ((st[z] == '*') && (st[z+1] == '=')) || // *= | |
| ((st[z] == '/') && (st[z+1] == '=')) || // /= | |
| ((st[z] == '%') && (st[z+1] == '=')) || // %= | |
| ((st[z] == '+') && (st[z+1] == '=')) || // += | |
| ((st[z] == '-') && (st[z+1] == '=')) || // -= | |
| ((st[z] == '+') && (st[z+1] == '+')) || // ++ | |
| ((st[z] == '-') && (st[z+1] == '-'))) // -- | |
| { | |
| long l; | |
| if (newvar[i].parnum >= 0) | |
| { | |
| ch = st[z]; st[z] = 0; sprintf(kasm87err,"ERROR: %s can't be used as variable",&st[z-j]); st[z] = ch; | |
| globi = -1; return; | |
| } | |
| j = z; | |
| if (st[z] == '=') oz = z+1; else oz = z+2; | |
| checkops(globi+1); gop[globi] = NUL; l = globi; inquotes = 0; | |
| for(z=oz,p=1;;z++) | |
| { | |
| if (!st[z]) { strcpy(kasm87err,"ERROR: missing ;"); globi = -1; return; } | |
| if ((st[z] == '\"') && ((!z) || (st[z-1] != '\\'))) inquotes ^= 1; | |
| if (inquotes) continue; | |
| if (st[z] == '(') { p++; continue; } | |
| if (st[z] == ')') { p--; continue; } | |
| if (((st[z] == ';') || (st[z] == ',')) && (p == 1)) break; | |
| } | |
| ch = st[z]; st[z] = 0; parsefunc(&st[oz],breaklab,contlab); st[z] = ch; if (globi == -1) return; | |
| z++; //skip ';' | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| checkrxi(numrxi+1); memset(&rxi[numrxi],0,sizeof(rxi[0])); | |
| gasm[gecnt].r[0].r = KUNUSED; gasm[gecnt].n = 3; | |
| gasm[gecnt].r[1].r = newvar[i].r; gasm[gecnt].r[1].nv = i; | |
| gasm[gecnt].r[2].r = k*8+KECX; | |
| rxi[numrxi].r = l*8+KECX; | |
| if (st[j] == '=') { gasm[gecnt].f = POKE; } | |
| else if (st[j] == '*') { gasm[gecnt].f = POKETIMES; } | |
| else if (st[j] == '/') { gasm[gecnt].f = POKESLASH; } | |
| else if (st[j] == '%') { gasm[gecnt].f = POKEPERC; } | |
| else if (st[j] == '+') | |
| { | |
| if (st[j+1] == '=') { gasm[gecnt].f = POKEPLUS; } | |
| else { rxi[numrxi].r = gccnt*8+KEDX; checkops(gccnt+1); globval[gccnt++] = 1.0; gasm[gecnt].f = POKEPLUS; } | |
| } | |
| else if (st[j] == '-') | |
| { | |
| if (st[j+1] == '=') { gasm[gecnt].f = POKEMINUS; } | |
| else { rxi[numrxi].r = gccnt*8+KEDX; checkops(gccnt+1); globval[gccnt++] = 1.0; gasm[gecnt].f = POKEMINUS; } | |
| } | |
| gasm[gecnt].rxi = numrxi++; gecnt++; | |
| goto prebegit; | |
| } | |
| else | |
| { | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = k*8+KECX; | |
| gasm[gecnt].r[1].r = newvar[i].r; gasm[gecnt].r[1].nv = i; | |
| gasm[gecnt].r[2].r = k*8+KECX; gasm[gecnt].n = 2; | |
| gasm[gecnt].f = PEEK; gecnt++; | |
| gnext[k] = globi; | |
| if (negit < 0) | |
| { | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = gasm[gecnt].r[1].r = k*8+KECX; gasm[gecnt].r[2].r = KUNUSED; gasm[gecnt].n = 1; gasm[gecnt].f = NEGMOV; gecnt++; | |
| } | |
| } | |
| continue; | |
| } | |
| } | |
| else arrind = 0; | |
| if (((st[z] == '=') && (st[z+1] != '=')) || // = | |
| ((st[z] == '*') && (st[z+1] == '=')) || // *= | |
| ((st[z] == '/') && (st[z+1] == '=')) || // /= | |
| ((st[z] == '%') && (st[z+1] == '=')) || // %= | |
| ((st[z] == '+') && (st[z+1] == '=')) || // += | |
| ((st[z] == '-') && (st[z+1] == '=')) || // -= | |
| ((st[z] == '+') && (st[z+1] == '+')) || // ++ | |
| ((st[z] == '-') && (st[z+1] == '-'))) // -- | |
| { | |
| if (newvar[i].parnum >= 0) | |
| { | |
| ch = st[z]; st[z] = 0; sprintf(kasm87err,"ERROR: %s can't be used as variable",&st[z-j]); st[z] = ch; | |
| globi = -1; return; | |
| } | |
| j = z; | |
| if (st[z] == '=') oz = z+1; else oz = z+2; | |
| checkops(globi+1); gop[globi] = NUL; k = globi; inquotes = 0; | |
| for(z=oz,p=1;;z++) | |
| { | |
| if (!st[z]) { strcpy(kasm87err,"ERROR: missing ;"); globi = -1; return; } | |
| if ((st[z] == '\"') && ((!z) || (st[z-1] != '\\'))) inquotes ^= 1; | |
| if (inquotes) continue; | |
| if (st[z] == '(') { p++; continue; } | |
| if (st[z] == ')') { p--; continue; } | |
| if (((st[z] == ';') || (st[z] == ',')) && (p == 1)) break; | |
| } | |
| ch = st[z]; st[z] = 0; parsefunc(&st[oz],breaklab,contlab); st[z] = ch; if (globi == -1) return; | |
| z++; //skip ';' | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = newvar[i].r; gasm[gecnt].r[0].q = arrind; gasm[gecnt].r[0].nv = i; | |
| gasm[gecnt].n = 2; | |
| if (st[j] == '=') { gasm[gecnt].r[1].r = k*8+KECX; gasm[gecnt].r[2].r = KUNUSED; gasm[gecnt].n = 1; gasm[gecnt].f = MOV; } | |
| else if (st[j] == '*') { gasm[gecnt].r[1] = gasm[gecnt].r[0]; gasm[gecnt].r[2].r = k*8+KECX; gasm[gecnt].f = TIMES; } | |
| else if (st[j] == '/') { gasm[gecnt].r[1] = gasm[gecnt].r[0]; gasm[gecnt].r[2].r = k*8+KECX; gasm[gecnt].f = SLASH; } | |
| else if (st[j] == '%') { gasm[gecnt].r[1] = gasm[gecnt].r[0]; gasm[gecnt].r[2].r = k*8+KECX; gasm[gecnt].f = PERC; } | |
| else if (st[j] == '+') | |
| { | |
| if (st[j+1] == '=') { gasm[gecnt].r[1] = gasm[gecnt].r[0]; gasm[gecnt].r[2].r = k*8+KECX; gasm[gecnt].f = PLUS; } | |
| else { gasm[gecnt].r[1] = gasm[gecnt].r[0]; gasm[gecnt].r[2].r = gccnt*8+KEDX; checkops(gccnt+1); globval[gccnt++] = 1.0; gasm[gecnt].f = PLUS; } | |
| } | |
| else if (st[j] == '-') | |
| { | |
| if (st[j+1] == '=') { gasm[gecnt].r[1] = gasm[gecnt].r[0]; gasm[gecnt].r[2].r = k*8+KECX; gasm[gecnt].f = MINUS; } | |
| else { gasm[gecnt].r[1] = gasm[gecnt].r[0]; gasm[gecnt].r[2].r = gccnt*8+KEDX; checkops(gccnt+1); globval[gccnt++] = 1.0; gasm[gecnt].f = MINUS; } | |
| } | |
| gecnt++; | |
| goto prebegit; | |
| } | |
| else if (st[z] == '(') | |
| { | |
| if (newvar[i].parnum > 0) | |
| { fmode = USERFUNC; newvari = i; } | |
| else | |
| { | |
| ch = st[z]; st[z] = 0; sprintf(kasm87err,"ERROR: %s can't be used as function",&st[z-j]); st[z] = ch; | |
| globi = -1; return; | |
| } | |
| } | |
| else | |
| { | |
| if (newvar[i].parnum > 0) | |
| { | |
| ch = st[z]; st[z] = 0; sprintf(kasm87err,"ERROR: %s can't be used as variable",&st[z-j]); st[z] = ch; | |
| globi = -1; return; | |
| } | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = globi*8+KECX; | |
| gasm[gecnt].r[1].r = newvar[i].r; gasm[gecnt].r[1].q = arrind; gasm[gecnt].r[1].nv = i; | |
| gasm[gecnt].r[2].r = KUNUSED; | |
| gasm[gecnt].n = 1; | |
| if (negit < 0) gasm[gecnt].f = NEGMOV; else gasm[gecnt].f = MOV; gecnt++; | |
| checkops(globi+2); gnext[globi] = globi+1; globi++; gop[globi] = NUL; | |
| } | |
| continue; //Name was found; no more processing | |
| } | |
| if (st[z] == '\"') //Extended name support (to support filenames) | |
| { for(j=z+1;st[j];j++) if ((st[j] == '\"') && (st[j-1] != '\\')) break; if (!st[j]) j = z; j++; } | |
| else | |
| { for(j=z;isvarchar(st[j]);j++); } //Standard C name support | |
| if (j <= z) { sprintf(kasm87err,"ERROR: '=' bad dest (%.32s)",&st[z]); globi = -1; return; } | |
| if ((st[j] == '=') && (st[j+1] != '=')) //FUKFUK //New variables MUST use assignment operator '=' | |
| { | |
| //Don't allow new variable to be same name as existing enum.. | |
| for(k=0,cptr=enumnam;k<enumnum;k++,cptr=&cptr[p+1]) | |
| { | |
| for(p=1;cptr[p];p++); | |
| if ((j-z == p) && (!strncmp(&st[z],cptr,p))) | |
| { | |
| ch = st[j]; st[j] = 0; sprintf(kasm87err,"ERROR: name conflict: %s",&st[z]); st[j] = ch; | |
| globi = -1; return; | |
| } | |
| } | |
| checkvarchars(newvarplc+j-z+2); | |
| checkvars(newvarnum+1); | |
| newvar[newvarnum].nami = newvarplc; | |
| //Save new variable name&index to list | |
| if (st[z] == '\"') { memcpy(&newvarnam[newvarplc],&st[z+1],j-z-2); newvarplc += j-z-2; } | |
| else { memcpy(&newvarnam[newvarplc],&st[z ],j-z ); newvarplc += j-z ; } | |
| newvarnam[newvarplc++] = 0; | |
| newvar[newvarnum].r = globi*8+KECX; | |
| newvar[newvarnum].maxind = 0; | |
| newvar[newvarnum].parnum = -1; | |
| newvar[newvarnum].proti = -1; | |
| k = getnewvarhash(&newvarnam[newvar[newvarnum].nami]); | |
| newvar[newvarnum].hashn = newvarhash[k]; newvarhash[k] = newvarnum; | |
| newvarnum++; | |
| oz = j+1; checkops(globi+1); gop[globi] = NUL; inquotes = 0; | |
| for(z=oz,p=1;;z++) | |
| { | |
| if (!st[z]) { strcpy(kasm87err,"ERROR: missing ;"); globi = -1; return; } | |
| if ((st[z] == '\"') && ((!z) || (st[z-1] != '\\'))) inquotes ^= 1; | |
| if (inquotes) continue; | |
| if (st[z] == '(') { p++; continue; } | |
| if (st[z] == ')') { p--; continue; } | |
| if (((st[z] == ';') || (st[z] == ',')) && (p == 1)) break; | |
| } | |
| if (z == oz) { sprintf(kasm87err,"ERROR: blank function"); globi = -1; return; } | |
| ch = st[z]; st[z] = 0; parsefunc(&st[oz],breaklab,contlab); st[z] = ch; if (globi == -1) return; | |
| z++; //skip ';' | |
| goto prebegit; | |
| } | |
| else if (st[j] == ':') //New label | |
| { | |
| if (j-z <= 0) { sprintf(kasm87err,"ERROR: : needs label"); globi = -1; return; } | |
| //Save new variable name&index to list | |
| ch = st[j]; st[j] = 0; | |
| for(i=0,cptr=newlabnam;i<newlabnum;i++,cptr=&cptr[k+1]) | |
| { | |
| for(k=1;cptr[k];k++); | |
| if (!strcmp(&st[z],cptr)) break; | |
| } | |
| if (i >= newlabnum) | |
| { | |
| checklabchars(newlabplc+j-z+2); | |
| checklabs(numlabels+1); | |
| strcpy(&newlabnam[newlabplc],&st[z]); newlabplc += j-z+1; | |
| newlabind[newlabnum++] = numlabels; i = numlabels++; | |
| } else { newlabind[i] &= 0x7fffffff; i = newlabind[i]; } //(&0x7fffffff: label acked) | |
| st[j] = ch; | |
| //Insert label at } | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].f = NUL; gasm[gecnt].r[0].r = i+KEIP; gecnt++; | |
| z = j+1; //skip ':' | |
| } | |
| else | |
| { | |
| //Check if name is "enum" | |
| while (isvarchar(st[z])) z++; | |
| for(k=0,cptr=enumnam;k<enumnum;k++,cptr=&cptr[p+1]) | |
| { | |
| for(p=1;cptr[p];p++); | |
| if ((z-oz == p) && (!strncmp(&st[oz],cptr,p))) | |
| { | |
| //Parse constants | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = globi*8+KECX; gasm[gecnt].r[1].r = gccnt*8+KEDX; gasm[gecnt].r[2].r = KUNUSED; gasm[gecnt].n = 1; gasm[gecnt].f = MOV; gecnt++; | |
| checkops(gccnt+1); globval[gccnt++] = enumval[k]*(double)negit; | |
| checkops(globi+2); gnext[globi] = globi+1; globi++; gop[globi] = NUL; | |
| break; | |
| } | |
| } | |
| if (k < enumnum) continue; //It was enum | |
| z = oz; | |
| if (st[z] == '\"') | |
| { | |
| //Parse const strings (must be used as userfunc parameters) | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = globi*8+KECX; gasm[gecnt].r[1].r = gstnum+KSTR; gasm[gecnt].r[2].r = KUNUSED; gasm[gecnt].n = 1; gasm[gecnt].f = MOV; gecnt++; | |
| checkstrings(gstnum+j-z-1); | |
| #if 0 | |
| memcpy(&gstring[gstnum],&st[z+1],j-z-2); | |
| gstring[gstnum+j-z-2] = 0; | |
| gstnum += j-z-1; | |
| #else | |
| for(k=z+1;k<j-1;k++) | |
| { | |
| if ((k < j-2) && (st[k] == '\\') && (st[k+1] == '\"')) continue; | |
| gstring[gstnum++] = st[k]; | |
| } | |
| gstring[gstnum++] = 0; | |
| #endif | |
| checkops(globi+2); gnext[globi] = globi+1; globi++; gop[globi] = NUL; | |
| break; | |
| } | |
| if (j > z) | |
| { | |
| ch = st[j]; st[j] = 0; | |
| #if 1 | |
| //FUKFUKFUK: this version is known to work | |
| sprintf(kasm87err,"ERROR: %s undefined",&st[z]); | |
| #else | |
| if (!isaddr) //<- FUKFUKFUK: should autogenerate for this too! | |
| sprintf(kasm87err,"ERROR: %s undefined",&st[z]); | |
| else | |
| { | |
| checkvarchars(newvarplc+j-z+2); | |
| checkvars(newvarnum+1); | |
| newvar[newvarnum].nami = newvarplc; | |
| //Save new variable name&index to list | |
| if (st[z] == '\"') { memcpy(&newvarnam[newvarplc],&st[z+1],j-z-2); newvarplc += j-z-2; } | |
| else { memcpy(&newvarnam[newvarplc],&st[z ],j-z ); newvarplc += j-z ; } | |
| newvarnam[newvarplc++] = 0; | |
| newvar[newvarnum].r = globi*8+KECX; | |
| newvar[newvarnum].maxind = 0; | |
| newvar[newvarnum].parnum = -1; | |
| newvar[newvarnum].proti = -1; | |
| k = getnewvarhash(&newvarnam[newvar[newvarnum].nami]); | |
| newvar[newvarnum].hashn = newvarhash[k]; newvarhash[k] = newvarnum; | |
| i = newvarnum; | |
| newvarnum++; | |
| st[j] = ch; | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = globi*8+KECX; gasm[gecnt].r[0].nv = i; | |
| gasm[gecnt].r[1].r = gccnt*8+KEDX; gasm[gecnt].n = 1; gasm[gecnt].f = MOV; checkops(gccnt+1); globval[gccnt++] = 0.0; | |
| gasm[gecnt].r[2].r = KUNUSED; | |
| gasm[gecnt].n = 1; | |
| gasm[gecnt].f = MOV; gecnt++; | |
| checkops(globi+2); gnext[globi] = globi+1; globi++; gop[globi] = NUL; | |
| oz = z; //+1; | |
| z = j; | |
| inquotes = 0; continue; //Name was generated; no more processing | |
| //goto prebegit; | |
| } | |
| #endif | |
| st[j] = ch; | |
| } | |
| else | |
| sprintf(kasm87err,"ERROR: %c undefined",st[j]); | |
| globi = -1; return; | |
| } | |
| } | |
| if (gop[globi] != NUL) | |
| { | |
| strcpy(kasm87err,"ERROR: "); | |
| tg.f = gop[globi]; getfuncnam(&tg,&kasm87err[strlen(kasm87err)]); | |
| strcat(kasm87err," missing parameter"); | |
| globi = -1; return; | |
| } | |
| #if 1 | |
| //PEMDAS, goes through entire list for every priority... quite wasteful! | |
| for(i=0;i<7;i++) | |
| for(z=ogi;gnext[z]<globi;) | |
| { | |
| p = (long)oprio[gop[gnext[z]]]; if (i != p) { z = gnext[z]; continue; } | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = gasm[gecnt].r[1].r = z*8+KECX; | |
| gasm[gecnt].r[2].r = gnext[z]*8+KECX; gasm[gecnt].f = gop[gnext[z]]; | |
| gasm[gecnt].n = 2; gecnt++; | |
| gnext[z] = gnext[gnext[z]]; //Remove 2nd param from linked list | |
| } | |
| #else | |
| //PEMDAS, faster algo, keeps circling until done | |
| //for(i=0;i<8;i++) printf("gop[%d] = %d, gnext[%d] = %d\n",i,gop[i],i,gnext[i]); | |
| //printf("ogi=%d,globi=%d\n",ogi,globi); | |
| // | |
| //gop[0] = 0 gnext[0] = 1 ogi = 0, globi = 5 | |
| //gop[1] = TIMES gnext[1] = 2 | |
| //gop[2] = PLUS gnext[2] = 3 r0 = X * X | |
| //gop[3] = TIMES gnext[3] = 4 r1 = Y * Y | |
| //gop[4] = LES gnext[4] = 5 r1 = r1 < 1 | |
| //gop[5] = 0 gnext[5] = 0 r0 = r0 + r1 | |
| // | |
| // 1 2 1 3 <priority 2 1 3 <priority 2 3 <priority | |
| // * + * < <gop[i] + * < <gop[i] + < <gop[i] | |
| // x*x+y*y<1 <equation x+y*y<1 <equation x+y<1 <equation | |
| // ^ 0->2->3->4->5 ^ ^ 0->2->4->5 ^ | |
| // | |
| //Find leftmost operator satisfying: my_priority <= next_operator_priority | |
| z = ogi; | |
| while (gnext[z] != globi) | |
| { | |
| //Can't evaluate operator yet if next operator is higher priority... | |
| if ((gop[gnext[z]] == NUL) || (oprio[gop[gnext[z]]] > oprio[gop[gnext[gnext[z]]]])) | |
| { z = gnext[z]; continue; } | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0] = gasm[gecnt].r[1] = z*8+KECX; | |
| gasm[gecnt].r[2] = gnext[z]*8+KECX; gasm[gecnt].f = gop[gnext[z]]; | |
| gasm[gecnt].n = 2; gecnt++; | |
| gnext[z] = gnext[gnext[z]]; //Remove 2nd param from linked list | |
| z = ogi; | |
| } | |
| #endif | |
| } | |
| static char fpustat; | |
| static long putwrite = 0; | |
| static void put1byte (long a) { if (putwrite) compcode[kasm87leng] = (char)a; kasm87leng++; } | |
| static void put2byte (long a) { if (putwrite) *(short *)&compcode[kasm87leng] = (short)a; kasm87leng += 2; } | |
| static void put4byte (long a) { if (putwrite) *(long *)&compcode[kasm87leng] = a; kasm87leng += 4; } | |
| static void putsib (long opcode, long a, rtyp b) | |
| { | |
| long hbr, lbr; | |
| if ((b.r&0xf0000000) == KPTR) | |
| { | |
| b.r &= 0x0fffffff; | |
| if ((b.r >= -128) && (b.r < 128)) put4byte((b.r<<24)+0x244c8b); //mov ecx, [esp+imm8] | |
| else { put2byte(0x8c8b); put1byte(0x24); put4byte(b.r); } //mov ecx, [esp+imm32] | |
| b.r = KECX+b.q*8; | |
| } | |
| else if ((b.r&0xf0000000) == KEDX) b.r += b.q*8; | |
| if (!(opcode&0xffffff00)) put1byte(opcode); | |
| else put2byte(opcode); | |
| hbr = (((unsigned long)b.r)>>28); lbr = (b.r&0x0fffffff); | |
| if (hbr == (KFST>>28)) | |
| { | |
| //d9 c0 fld st(0) | |
| //d9 c8 fxch st(0) | |
| //dd c0 ffree st(0) | |
| //dd d0 fst st(0) | |
| //dd d8 fstp st(0) | |
| // ... | |
| if (putwrite) | |
| { | |
| if ((compcode[kasm87leng-1] != 0xdd) || ((a&0x30) != 0x10)) //Don't do hack for FST or FSTP... | |
| compcode[kasm87leng-1] -= 0x04; //Nasty hack!!! | |
| } | |
| fpustat |= (1<<(lbr>>3)); | |
| put1byte(a+0xc0+(lbr>>3)); return; | |
| } | |
| a &= 0x38; //Throw away stack pointer position when using memory access! | |
| if ((hbr == (KIMM>>28)) || (hbr == (KGLB>>28))) | |
| { | |
| put1byte(a+0x05); | |
| #if (COMPILE == 0) | |
| if (hbr == (KIMM>>28)) put4byte((long)(gevalext[lbr].ptr)+b.q*8); //access static variable (imm32) | |
| else put4byte(gstatmem + lbr+b.q*8); //access static variable (imm32) | |
| #else | |
| if (putwrite) | |
| { | |
| checkpatch(patchnum+1); | |
| patch[patchnum].lptr = (long *)&compcode[kasm87leng]; | |
| patch[patchnum].ind = b.r; | |
| patchnum++; | |
| } | |
| put4byte(b.q*8); //access static variable (imm32) | |
| #endif | |
| return; | |
| } | |
| if ((lbr < -128) || (lbr >= 128)) { put1byte(a+0x80+hbr); if (hbr == 4) put1byte(0x24); put4byte(lbr); } | |
| else if ((lbr) && (hbr != 5)) { put1byte(a+0x40+hbr); if (hbr == 4) put1byte(0x24); put1byte(lbr); } | |
| else { put1byte(a +hbr); if (hbr == 4) put1byte(0x24); } | |
| } | |
| static long putlen (rtyp b) | |
| { | |
| long r, lng; | |
| lng = 0; | |
| if ((b.r&0xf0000000) == KPTR) | |
| { | |
| b.r &= 0x0fffffff; | |
| if ((b.r >= -128) && (b.r < 128)) lng = 4; else lng = 7; | |
| b.r = KECX+b.q*8; | |
| } | |
| else if ((b.r&0xf0000000) == KEDX) b.r += b.q*8; | |
| r = (((unsigned long)b.r)>>28); b.r &= 0x0fffffff; | |
| if (r == (KFST>>28)) return(lng+1); | |
| if ((r == (KIMM>>28)) || (r == (KGLB>>28))) return(lng+5); | |
| if ((b.r < -128) || (b.r >= 128)) return(lng+(r==4)+5); | |
| else if ((b.r) && (r != 5)) return(lng+(r==4)+2); | |
| else return(lng+(r==4)+1); | |
| } | |
| //This function helps find sequences like this which can be safely removed: | |
| // fld qword ptr [esp+0x28] | |
| // fstp qword ptr [esp+0x28] | |
| // ... (qword ptr [esp+0x28] not used again) | |
| // | |
| // r0 = ? op ?; | |
| // ? = r0 + ?; | |
| // (r0 written before read) | |
| static long anyreads1stop, anyreads1stinst; | |
| static long anyreadsbeforewritesrec (long i, rtyp r) | |
| { | |
| for(;i<gecnt;i++) | |
| { | |
| //static char gmybuf[256]; | |
| //getfuncnam(&gasm[i],gmybuf); //For debugging only | |
| //printf("%d %d %08x (q:%08x) %s\n",firstop,i,r.r,r.q,gmybuf); | |
| if (!anyreads1stop) | |
| { | |
| rtyp *rp; | |
| long k; | |
| for(k=gasm[i].n;k>0;k--) | |
| { | |
| if (k < 3) rp = &gasm[i].r[k]; else rp = &rxi[gasm[i].rxi+k-3]; | |
| if (gasmeq(*rp,r)) return(1); | |
| } | |
| } else anyreads1stop = 0; | |
| //Don't go past starting instruction (if in a loop) | |
| if (anyreads1stinst < 0) anyreads1stinst &= 0x7fffffff; else if (i == anyreads1stinst) return(0); | |
| if ((gasm[i].f == IF0) || (gasm[i].f == IF1) || (gasm[i].f == GOTO)) | |
| { | |
| long j = lablinum[gasm[i].r[1].r&0x0fffffff]; | |
| if (j >= 0) //Don't jump to same label again - already processed | |
| { | |
| lablinum[gasm[i].r[1].r&0x0fffffff] |= 0x80000000; | |
| if (anyreadsbeforewritesrec(j,r)) return(1); | |
| } | |
| } | |
| if ((gasm[i].f == GOTO) || (gasm[i].f == RETURN)) break; | |
| if (gasmeq(gasm[i].r[0],r)) return(0); | |
| } | |
| return(((r.r&0xf0000000) == KPTR) || ((r.r&0xf0000000) == KIMM) || ((r.r&0xf0000000) == KARR) || ((r.r&0xf0000000) == KGLB) | |
| || (((r.r&0xf0000000) == KEDX) && ((r.r&0x0fffffff) >= gccnt*8+gstnum)) ); | |
| } | |
| static long anyreadsbeforewrites (long i, rtyp r, long firstop) | |
| { | |
| long j; | |
| #if 0 | |
| //This block is just an optimization that failed miserably :/ | |
| //See if labels are already set correctly (numlabels < gecnt, so faster) | |
| long k; | |
| for(k=numlabels-1;k>=0;k--) | |
| { | |
| j = (lablinum[k]&0x7fffffff); if ((unsigned long)j >= gecnt) break; | |
| if ((gasm[j].f != NUL) || (gasm[j].r[0].r != k)) break; | |
| lablinum[k] = j; | |
| } | |
| if (k >= 0) //Oh well... must check the whole list again | |
| #endif | |
| for(j=gecnt-1;j>=0;j--) | |
| if (gasm[j].f == NUL) | |
| lablinum[gasm[j].r[0].r&0x0fffffff] = j; | |
| anyreads1stop = firstop; | |
| anyreads1stinst = (i|0x80000000); | |
| return(anyreadsbeforewritesrec(i,r)); | |
| } | |
| static void put1stfld (long i, rtyp r) | |
| { | |
| if ((gasm[i-1].f) && (gasmeq(gasm[i-1].r[0],r))) | |
| { | |
| long j = (putlen(gasm[i-1].r[0])+1); | |
| #if (COMPILE != 0) | |
| if ((patchnum > 0) && (patch[patchnum-1].lptr >= (long *)&compcode[kasm87leng-j]) && | |
| (putwrite) && (patch[patchnum-1].lptr <= (long *)&compcode[kasm87leng-4])) patchnum--; | |
| #endif | |
| kasm87leng -= j; | |
| //Replace fld...fstp to same location with fst | |
| if (gasmeq(gasm[i].r[1],gasm[i].r[2]) || (anyreadsbeforewrites(i,r,1))) | |
| putsib(0xdd,0x11,r); //fst qword ptr [?] | |
| } | |
| else | |
| putsib(0xdd,0x00,r); //fld qword ptr [?] | |
| } | |
| void kasm87freeall () | |
| { | |
| #if (COMPILE != 0) | |
| if (patch) { free(patch); patch = 0; } maxpatch = 0; | |
| #endif | |
| if (jumpback) { free(jumpback); jumpback = 0; } maxjumpbacks = 0; | |
| if (rxi) { free(rxi); rxi = 0; } maxrxi = 0; | |
| if (enumnam) { free(enumnam); enumnam = 0; } maxenumchars = 0; | |
| if (enumval) { free(enumval); enumval = 0; } maxenum = 0; | |
| if (gasm) { free(gasm); gasm = 0; } | |
| if (lablinum) { free(lablinum); lablinum = 0; } | |
| if (jumpat) { free(jumpat); jumpat = 0; } | |
| if (labpat) { free(labpat); labpat = 0; } | |
| if (newlabind) { free(newlabind); newlabind = 0; } maxlabs = 0; | |
| if (newlabnam) { free(newlabnam); newlabnam = 0; } maxlabchars = 0; | |
| if (newvar) { free(newvar); newvar = 0; } maxvars = 0; | |
| if (newvarnam) { free(newvarnam); newvarnam = 0; } maxvarchars = 0; | |
| if (gvl) { free(gvl); gvl = 0; } | |
| if (gstring) { free(gstring); gstring = 0; } maxst = 0; | |
| if (ginitval) { free(ginitval); ginitval = 0; } maxinitval = 0; | |
| if (globval) { free(globval); globval = 0; } | |
| if (gnext) { free(gnext); gnext = 0; } | |
| if (gop) { free(gop); gop = 0; } maxops = 0; | |
| if (funcst) { free(funcst); funcst = 0; } maxfuncst = 0; | |
| if (texttrans) { free(texttrans); texttrans = 0; } texttransmal = 0; | |
| } | |
| //mingecnt: hack telling optimizer not to touch gasm[0 .. mingecnt-1]. Set it to 0 for standard behavior. | |
| //duringparse: 0:safe to rename registers, 1:not safe | |
| static long kasmoptimizations (long mingecnt, long duringparse) | |
| { | |
| rtyp tr, *rp; | |
| long i, j, k, l, m, got; | |
| do //Optimizations! | |
| { | |
| //{ char debuf[16384]; kasm87_showdebug(1,debuf,sizeof(debuf)); printf("%s\n",debuf); } | |
| got = 0; | |
| #if 1 | |
| //Remove duplicate constants | |
| for(j=gccnt-1;j>=0;j--) | |
| for(i=j-1;i>=0;i--) | |
| if (globval[i] == globval[j]) //Rewire: j to i, then rewire: (gccnt-1) to j | |
| { | |
| gccnt--; got = 1; | |
| globval[j] = globval[gccnt]; | |
| for(k=gecnt-1;k>=0;k--) | |
| for(l=gasm[k].n;l>=0;l--) | |
| { | |
| if (l < 3) rp = &gasm[k].r[l]; else rp = &rxi[gasm[k].rxi+l-3]; | |
| if (rp->r == j*8+KEDX) rp->r = i*8+KEDX; | |
| if (rp->r == gccnt*8+KEDX) rp->r = j*8+KEDX; | |
| } | |
| break; | |
| } | |
| #endif | |
| #if 1 | |
| //Remove dead (unused) constants | |
| for(i=gccnt-1;i>=0;i--) | |
| { | |
| for(j=gecnt-1;j>=0;j--) | |
| { | |
| for(k=gasm[j].n;k>0;k--) | |
| { | |
| if (k < 3) rp = &gasm[j].r[k]; else rp = &rxi[gasm[j].rxi+k-3]; | |
| if (rp->r == i*8+KEDX) break; | |
| } | |
| if (k > 0) break; | |
| } | |
| if (j < 0) //Constant i not used. Rewire: (gccnt-1) to i | |
| { | |
| gccnt--; got = 1; | |
| globval[i] = globval[gccnt]; | |
| for(j=gecnt-1;j>=0;j--) | |
| for(k=gasm[j].n;k>0;k--) | |
| { | |
| if (k < 3) rp = &gasm[j].r[k]; else rp = &rxi[gasm[j].rxi+k-3]; | |
| if (rp->r == gccnt*8+KEDX) rp->r = i*8+KEDX; | |
| } | |
| } | |
| } | |
| #endif | |
| #if 1 | |
| //Convert NEGMOV by constant to MOV (and negate constant) | |
| for(i=gecnt-1;i>=0;i--) | |
| if ((gasm[i].f == NEGMOV) && ((gasm[i].r[1].r&0xf0000000) == KEDX)) | |
| { | |
| gasm[i].f = MOV; | |
| checkops(gccnt+1); | |
| globval[gccnt] = -globval[(gasm[i].r[1].r&0x0fffffff)>>3]; | |
| gasm[i].r[1].r = gccnt*8+KEDX; | |
| gccnt++; | |
| got = 1; | |
| } | |
| #endif | |
| #if 1 | |
| //Convert NEQU0 to MOV if input guaranteed to be 0.0 or 1.0 | |
| for(i=gecnt-1;i>=0;i--) | |
| if ((gasm[i].f == NEQU0) && ((gasm[i].r[1].r&0xf0000000) == KECX)) | |
| for(j=i-1;j>=0;j--) | |
| if (gasmeq(gasm[j].r[0],gasm[i].r[1])) | |
| { | |
| switch(gasm[j].f) | |
| { | |
| case LES: case LESEQ: case MOR: case MOREQ: case EQU: case NEQU: case LAND: case LOR: | |
| gasm[i].f = MOV; gasm[i].n = 1; got = 1; break; | |
| default: break; | |
| } | |
| if (gasm[i].f == MOV) break; | |
| } | |
| #endif | |
| #if 1 | |
| //FUKFUKFUK: can't remove this without also taking some other block out ??? (ceilflor2.kc crashes) | |
| //Remove MOV with same src & dest | |
| for(i=gecnt-1;i>=mingecnt;i--) | |
| if ((gasm[i].f == MOV) && (gasmeq(gasm[i].r[0],gasm[i].r[1]))) | |
| { | |
| gecnt--; got = 1; //Delete MOV instruction and re-wire registers at same time) | |
| for(j=i;j<gecnt;j++) gasm[j] = gasm[j+1]; //Register is overwritten - simply copy rest now | |
| } | |
| #endif | |
| #if 1 | |
| //Simplify math expressions, such as POW(x,2), TIMES(x,1), etc... | |
| for(i=gecnt-1;i>=mingecnt;i--) | |
| { | |
| j = ((gasm[i].r[1].r&0x0fffffff)>>3); | |
| k = ((gasm[i].r[2].r&0x0fffffff)>>3); | |
| switch(gasm[i].f) | |
| { | |
| case POW: | |
| if ((gasm[i].r[1].r&0xf0000000) == KEDX) | |
| { | |
| if (globval[j] == 1.0) { gasm[i].f = MOV; gasm[i].n = 1; got = 1; break; } | |
| } | |
| else if ((gasm[i].r[2].r&0xf0000000) == KEDX) | |
| { | |
| if (globval[k] == 4.0) | |
| { | |
| checkops(gecnt+1); gecnt++; for(j=gecnt-1;j>i;j--) gasm[j] = gasm[j-1]; | |
| gasm[i ].f = TIMES; gasm[i].r[2] = gasm[i].r[1]; | |
| gasm[i+1].f = TIMES; gasm[i+1].r[1] = gasm[i+1].r[2] = gasm[i+1].r[0]; | |
| got = 1; break; | |
| } | |
| else if ((globval[k] == 3.0) && (!gasmeq(gasm[i].r[0],gasm[i].r[1]))) //a = pow(b,3), &a != &b | |
| { | |
| checkops(gecnt+1); gecnt++; for(j=gecnt-1;j>i;j--) gasm[j] = gasm[j-1]; | |
| gasm[i ].f = TIMES; gasm[i].r[2] = gasm[i].r[1]; | |
| gasm[i+1].f = TIMES; gasm[i+1].r[2] = gasm[i+1].r[0]; | |
| got = 1; break; | |
| } | |
| else if (globval[k] == 2.0) { gasm[i].f = TIMES; gasm[i].r[2] = gasm[i].r[1]; got = 1; break; } | |
| else if (globval[k] == 1.0) { gasm[i].f = MOV; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; got = 1; break; } | |
| else if (globval[k] == 0.0) { gasm[i].f = MOV; gasm[i].r[1].r = gccnt*8+KEDX; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; checkops(gccnt+1); globval[gccnt++] = 1.0; got = 1; break; } | |
| else if (globval[k] == 0.5) { gasm[i].f = SQRT; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; got = 1; break; } | |
| else if (globval[k] ==-1.0) { gasm[i].f = SLASH; gasm[i].r[2] = gasm[i].r[1]; gasm[i].r[1].r = gccnt*8+KEDX; checkops(gccnt+1); globval[gccnt++] = 1.0; got = 1; break; } | |
| } | |
| break; | |
| case TIMES: | |
| if ((gasm[i].r[1].r&0xf0000000) == KEDX) | |
| { | |
| if (globval[j] ==-1.0) { gasm[i].f = NEGMOV; gasm[i].r[1] = gasm[i].r[2]; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; got = 1; break; } | |
| else if (globval[j] == 0.0) { gasm[i].f = MOV; gasm[i].r[1].r = gccnt*8+KEDX; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; checkops(gccnt+1); globval[gccnt++] = 0.0; got = 1; break; } | |
| else if (globval[j] == 1.0) { gasm[i].f = MOV; gasm[i].r[1] = gasm[i].r[2]; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; got = 1; break; } | |
| else if (globval[j] == 2.0) { gasm[i].f = PLUS; gasm[i].r[1] = gasm[i].r[2]; got = 1; break; } | |
| } | |
| else if ((gasm[i].r[2].r&0xf0000000) == KEDX) | |
| { | |
| if (globval[k] ==-1.0) { gasm[i].f = NEGMOV; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; got = 1; break; } | |
| else if (globval[k] == 0.0) { gasm[i].f = MOV; gasm[i].r[1].r = gccnt*8+KEDX; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; checkops(gccnt+1); globval[gccnt++] = 0.0; got = 1; break; } | |
| else if (globval[k] == 1.0) { gasm[i].f = MOV; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; got = 1; break; } | |
| else if (globval[k] == 2.0) { gasm[i].f = PLUS; gasm[i].r[2] = gasm[i].r[1]; got = 1; break; } | |
| } | |
| break; | |
| case SLASH: | |
| if ((gasm[i].r[2].r&0xf0000000) == KEDX) | |
| { | |
| if (globval[k] ==-1.0) { gasm[i].f = NEGMOV; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; got = 1; break; } | |
| if (globval[k] == 1.0) { gasm[i].f = MOV; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; got = 1; break; } | |
| if (globval[k] == 0.5) { gasm[i].f = PLUS; gasm[i].r[2] = gasm[i].r[1]; got = 1; break; } | |
| //if (!((*(__int64 *)&globval[k])&0x000fffffffffffff)) //check for exact reciprocal (power of 2) | |
| { gasm[i].f = TIMES; checkops(gccnt+1); globval[gccnt] = 1.0 / globval[k]; gasm[i].r[2].r = gccnt*8+KEDX; checkops(gccnt+1); gccnt++; got = 1; break; } | |
| } | |
| break; | |
| case PLUS: | |
| if (((gasm[i].r[2].r&0xf0000000) == KEDX) && (globval[k] == 0.0)) { gasm[i].f = MOV; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; got = 1; break; } | |
| if (((gasm[i].r[1].r&0xf0000000) == KEDX) && (globval[j] == 0.0)) { gasm[i].f = MOV; gasm[i].r[1] = gasm[i].r[2]; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; got = 1; break; } | |
| break; | |
| case MINUS: | |
| if (((gasm[i].r[2].r&0xf0000000) == KEDX) && (globval[k] == 0.0)) { gasm[i].f = MOV; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; got = 1; break; } | |
| if (((gasm[i].r[1].r&0xf0000000) == KEDX) && (globval[j] == 0.0)) { gasm[i].f = NEGMOV; gasm[i].r[1] = gasm[i].r[2]; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; got = 1; break; } | |
| if (gasmeq(gasm[i].r[1],gasm[i].r[2])) { gasm[i].f = MOV; gasm[i].r[1].r = gccnt*8+KEDX; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; checkops(gccnt+1); globval[gccnt++] = 0.0; got = 1; break; } | |
| break; | |
| case NEQU: | |
| if (((gasm[i].r[2].r&0xf0000000) == KEDX) && (globval[k] == 0.0)) { gasm[i].f = NEQU0; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; got = 1; break; } | |
| if (((gasm[i].r[1].r&0xf0000000) == KEDX) && (globval[j] == 0.0)) { gasm[i].f = NEQU0; gasm[i].r[1] = gasm[i].r[2]; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; got = 1; break; } | |
| break; | |
| case LAND: | |
| if (((gasm[i].r[2].r&0xf0000000) == KEDX) && (globval[k] == 0.0)) { gasm[i].f = MOV; gasm[i].r[1].r = gccnt*8+KEDX; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; checkops(gccnt+1); globval[gccnt++] = 0.0; got = 1; break; } | |
| if (((gasm[i].r[1].r&0xf0000000) == KEDX) && (globval[j] == 0.0)) { gasm[i].f = MOV; gasm[i].r[1].r = gccnt*8+KEDX; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; checkops(gccnt+1); globval[gccnt++] = 0.0; got = 1; break; } | |
| if (((gasm[i].r[2].r&0xf0000000) == KEDX) && (globval[k] != 0.0)) { gasm[i].f = NEQU0; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; got = 1; break; } | |
| if (((gasm[i].r[1].r&0xf0000000) == KEDX) && (globval[j] != 0.0)) { gasm[i].f = NEQU0; gasm[i].r[1] = gasm[i].r[2]; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; got = 1; break; } | |
| break; | |
| case LOR: | |
| if (((gasm[i].r[2].r&0xf0000000) == KEDX) && (globval[k] != 0.0)) { gasm[i].f = MOV; gasm[i].r[1].r = gccnt*8+KEDX; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; checkops(gccnt+1); globval[gccnt++] = 1.0; got = 1; break; } | |
| if (((gasm[i].r[1].r&0xf0000000) == KEDX) && (globval[j] != 0.0)) { gasm[i].f = MOV; gasm[i].r[1].r = gccnt*8+KEDX; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; checkops(gccnt+1); globval[gccnt++] = 1.0; got = 1; break; } | |
| if (((gasm[i].r[2].r&0xf0000000) == KEDX) && (globval[k] == 0.0)) { gasm[i].f = NEQU0; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; got = 1; break; } | |
| if (((gasm[i].r[1].r&0xf0000000) == KEDX) && (globval[j] == 0.0)) { gasm[i].f = NEQU0; gasm[i].r[1] = gasm[i].r[2]; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; got = 1; break; } | |
| break; | |
| case ATAN2: | |
| if (((gasm[i].r[2].r&0xf0000000) == KEDX) && (globval[k] == 1.0)) { gasm[i].f = ATAN; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; got = 1; break; } | |
| break; | |
| } | |
| } | |
| #endif | |
| #if 1 | |
| //Evaluate expressions based purely on constants | |
| for(i=gecnt-1;i>=mingecnt;i--) | |
| { | |
| if ((gasm[i].f == IF0) || (gasm[i].f == IF1)) | |
| { | |
| if ((gasm[i].r[2].r&0xf0000000) == KEDX) | |
| { | |
| if ((gasm[i].f == IF0) == (globval[(gasm[i].r[2].r&0x0fffffff)>>3] == 0.0)) | |
| { | |
| got = 1; gasm[i].f = GOTO; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; | |
| } | |
| else | |
| { | |
| gecnt--; got = 1; //Delete instruction [i] | |
| for(j=i;j<gecnt;j++) gasm[j] = gasm[j+1]; | |
| i++; | |
| } | |
| } | |
| continue; | |
| } | |
| if ((gasm[i].f == GOTO) || (gasm[i].f == RETURN)) continue; | |
| if ((gasm[i].f < PARAM1) || (gasm[i].f == MOV) || (gasm[i].f == NEGMOV)) continue; | |
| if (gasm[i].f < PARAM2) gasm[i].r[2].r = KUNUSED; | |
| if (((gasm[i].r[2].r&0xf0000000) == KEDX) && | |
| (((gasm[i].r[1].r&0xf0000000) == KIMM) || ((gasm[i].r[1].r&0xf0000000) == KPTR) || ((gasm[i].r[1].r&0xf0000000) == KARR) || ((gasm[i].r[1].r&0xf0000000) == KGLB))) | |
| { | |
| double p2; | |
| if (gasm[i].f == PEEK) | |
| { | |
| p2 = *(double *)(((long)globval)+gasm[i].r[2].r-KEDX); | |
| gasm[i].r[1].q = (long)p2; | |
| if ((unsigned long)gasm[i].r[1].q >= (unsigned long)newvar[gasm[i].r[1].nv].maxind) | |
| { sprintf(kasm87err,"ERROR: array index out of bounds"); return(-1); } | |
| gasm[i].r[2].r = KUNUSED; | |
| gasm[i].n = 1; | |
| gasm[i].f = MOV; got = 1; | |
| continue; | |
| } | |
| if (gasm[i].f == POKE) | |
| { | |
| p2 = *(double *)(((long)globval)+gasm[i].r[2].r-KEDX); | |
| gasm[i].r[0] = gasm[i].r[1]; | |
| gasm[i].r[0].q = (long)p2; | |
| if ((unsigned long)gasm[i].r[0].q >= (unsigned long)newvar[gasm[i].r[0].nv].maxind) | |
| { sprintf(kasm87err,"ERROR: array index out of bounds"); return(-1); } | |
| gasm[i].r[1] = rxi[gasm[i].rxi]; //FUK: deallocate spot on rxi? | |
| gasm[i].r[2].r = KUNUSED; | |
| gasm[i].n = 1; | |
| gasm[i].f = MOV; got = 1; | |
| continue; | |
| } | |
| if ((gasm[i].f == POKETIMES) || (gasm[i].f == POKESLASH) || (gasm[i].f == POKEPERC) || | |
| (gasm[i].f == POKEPLUS) || (gasm[i].f == POKEMINUS)) | |
| { | |
| p2 = *(double *)(((long)globval)+gasm[i].r[2].r-KEDX); | |
| gasm[i].r[1].q = (long)p2; | |
| if ((unsigned long)gasm[i].r[1].q >= (unsigned long)newvar[gasm[i].r[1].nv].maxind) | |
| { sprintf(kasm87err,"ERROR: array index out of bounds"); return(-1); } | |
| gasm[i].r[0] = gasm[i].r[1]; | |
| gasm[i].r[2] = rxi[gasm[i].rxi]; //FUK: deallocate spot on rxi? | |
| gasm[i].n = 2; | |
| switch(gasm[i].f) | |
| { | |
| case POKETIMES: gasm[i].f = TIMES; break; | |
| case POKESLASH: gasm[i].f = SLASH; break; | |
| case POKEPERC: gasm[i].f = PERC; break; | |
| case POKEPLUS: gasm[i].f = PLUS; break; | |
| case POKEMINUS: gasm[i].f = MINUS; break; | |
| } | |
| got = 1; | |
| continue; | |
| } | |
| } | |
| if (((gasm[i].r[1].r&0xf0000000) == KEDX) && ((gasm[i].r[2].r&0xf0000000) == KEDX)) | |
| { | |
| double p0, *p1, *p2; | |
| p1 = (double *)(((long)globval)+gasm[i].r[1].r-KEDX); | |
| p2 = (double *)(((long)globval)+gasm[i].r[2].r-KEDX); | |
| j = 0; | |
| switch(gasm[i].f) | |
| { | |
| case NEQU0: p0 = ((*p1) != 0.0); break; | |
| case FABS: p0 = fabs(*p1); break; | |
| case SGN: p0 = ((*p1) > 0.0) - ((*p1) < 0.0); break; | |
| case UNIT: p0 = ((*p1) == 0.0)*.5 + ((*p1) > 0.0); break; | |
| case FLOOR: p0 = floor(*p1); break; | |
| case CEIL: p0 = ceil(*p1); break; | |
| case ROUND0: case ROUND0_32: if (*p1 >= 0) p0 = floor(*p1); else p0 = -floor(-(*p1)); break; | |
| case SIN: p0 = sin(*p1); break; | |
| case COS: p0 = cos(*p1); break; | |
| case TAN: p0 = tan(*p1); break; | |
| case ASIN: p0 = asin(*p1); break; | |
| case ACOS: p0 = acos(*p1); break; | |
| case ATAN: p0 = atan(*p1); break; | |
| case SQRT: p0 = sqrt(*p1); break; | |
| case EXP: p0 = exp(*p1); break; | |
| case FACT: p0 = fact(*p1); break; | |
| case LOG: p0 = log(*p1); break; | |
| case TIMES: p0 = (*p1)*(*p2); break; | |
| case SLASH: p0 = (*p1)/(*p2); break; | |
| case PERC: p0 = (*p1)-floor((*p1)/fabs(*p2))*fabs(*p2); break; | |
| case PLUS: p0 = (*p1)+(*p2); break; | |
| case MINUS: p0 = (*p1)-(*p2); break; | |
| case POW: p0 = pow(*p1,*p2); break; | |
| case MIN: if ((*p2) < (*p1)) p0 = (*p2); else p0 = (*p1); break; | |
| case MAX: if ((*p2) > (*p1)) p0 = (*p2); else p0 = (*p1); break; | |
| case FADD: j = -1; break; //NOTE! The entire purpose of FADD is to NOT optimize it here! Keep this as a placeholder. | |
| case FMOD: p0 = fmod(*p1,*p2); break; | |
| case ATAN2: p0 = atan2(*p1,*p2); break; | |
| case LOGB: p0 = log(*p1)/log(*p2); break; | |
| case LES: p0 = ((*p1) < (*p2)); break; | |
| case LESEQ: p0 = ((*p1) <= (*p2)); break; | |
| case MOR: p0 = ((*p1) > (*p2)); break; | |
| case MOREQ: p0 = ((*p1) >= (*p2)); break; | |
| case EQU: p0 = ((*p1) == (*p2)); break; | |
| case NEQU: p0 = ((*p1) != (*p2)); break; | |
| case LAND: p0 = ((*p1) && (*p2)); break; | |
| case LOR: p0 = ((*p1) || (*p2)); break; | |
| default: j = -1; break; | |
| } | |
| if (!j) | |
| { | |
| gasm[i].r[1].r = gccnt*8+KEDX; gasm[i].r[2].r = KUNUSED; gasm[i].n = 1; | |
| gasm[i].f = MOV; checkops(gccnt+1); globval[gccnt++] = p0; got = 1; | |
| } | |
| } | |
| } | |
| #endif | |
| #if 0 | |
| //This block is no longer necessary with register renaming, but it speeds things up. | |
| //I disable it for safety reasons (because code isn't checked as carefully as register renaming) | |
| // | |
| //Remove unnecessary MOV instructions, replacing src's in later code with dest of this mov | |
| // r1 = X; | |
| // r3 = r1 + r2; -> r3 = X + r2; | |
| // r4 = sqrt(r1); r4 = sqrt(X); | |
| for(i=gecnt-2;i>=mingecnt;i--) | |
| if ((gasm[i].f == MOV) && ((gasm[i].r[0].r&0xf0000000) == KECX)) | |
| { | |
| k = gasm[i].r[0].r; tr = gasm[i].r[1]; | |
| //When writing a variable in a loop that reads the variable ABOVE the write, it can't be | |
| //optimized. To be safe, make sure dest of mov (gasm[i].r[0]) isn't a 'named' variable! | |
| for(j=gnumarg;j<newvarnum;j++) | |
| if (newvar[j].r == k) break; | |
| if (j < newvarnum) continue; //finish abort | |
| m = 0; | |
| for(j=i+1;j<gecnt;j++) //temp register is used later... abort optimization | |
| { | |
| //writes register in other code that may or may not be executed... must abort | |
| if ((m) && ((gasm[j].r[0].r == k) || (gasm[j].r[1].r == k) || (gasm[j].r[2].r == k))) break; | |
| if ((gasm[j].f == NUL) || (gasm[j].f == IF0) || (gasm[j].f == IF1) || (gasm[j].f == GOTO) || (gasm[j].f == RETURN)) m = 1; | |
| } | |
| if (j < gecnt) continue; //finish abort | |
| //Make sure source of mov (tr) doesn't get written before last access of (k) | |
| for(j=i+1;j<gecnt;j++) | |
| if (gasmeq(gasm[j].r[0],tr)) | |
| { | |
| for(j++;j<gecnt;j++) | |
| if ((gasm[j].r[0].r == k) || (gasm[j].r[1].r == k) || (gasm[j].r[2].r == k)) break; | |
| break; | |
| } | |
| if (j < gecnt) continue; //finish abort | |
| gecnt--; got = 1; //Delete MOV instruction and re-wire registers at same time) | |
| for(j=i;j<gecnt;j++) | |
| { | |
| gasm[j] = gasm[j+1]; | |
| if (gasm[j].r[1].r == k) gasm[j].r[1] = tr; | |
| if (gasm[j].r[2].r == k) gasm[j].r[2] = tr; | |
| if (gasm[j].r[0].r == k) { j++; break; } | |
| } | |
| for(;j<gecnt;j++) gasm[j] = gasm[j+1]; //Register is overwritten - simply copy rest now | |
| } | |
| #endif | |
| #if 1 | |
| //Register renaming: (when result of calculation is used only once) | |
| // | |
| // Change: To: | |
| // inst i: r2 = ? +r3 -> r1 = ? +r3 (Later optimizations may remove r2) | |
| // inst j: r1 = r2+r4 r1 = r1+r4 | |
| // | |
| //Special case for MOV instructions: | |
| // Change: To: | |
| // inst i: r2 = ? -> r2 = ? (Later optimizations may remove r2) | |
| // inst j: r1 = r2+r4 r1 = ? +r4 | |
| // | |
| /// ÚÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄ¿ | |
| /// Rules: ³ i ³...³ j ³...³ ³ | |
| ///ÚÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ 1. i < (r1 access) < j not allowed | |
| ///³ r0 read ³ û ³ X ³ X ³ û ³ ³ 2. (first r1 access) > j must be write | |
| ///³ r0 write ³ û ³ X ³ û ³ û ³ ³ 3. (r0 read) at j not allowed | |
| ///³ r1 read ³ û ³ X ³(X)³>W ³ ³ 4. i < (r0 access) < j not allowed | |
| ///³ r1 write ³ û ³ X ³ û ³<R ³ ³ 5. (r1 read) at i not allowed if inside possible loop | |
| ///ÀÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÙ ³ | |
| //06/23/2004: Above rules rewritten to use anyreadsbeforewrite&now much cleaner! | |
| for(i=gecnt-1;i>=mingecnt;i--) | |
| { | |
| if ((gasm[i].r[0].r&0xf0000000) != KECX) continue; | |
| for(j=i+1;j<gecnt;j++) | |
| { | |
| if ((gasm[j].f == NUL) || (gasm[j].f == GOTO)) break; | |
| for(k=gasm[j].n;k>0;k--) | |
| { | |
| if (k < 3) rp = &gasm[j].r[k]; else rp = &rxi[gasm[j].rxi+k-3]; | |
| if (gasmeq(gasm[i].r[0],*rp)) break; | |
| } | |
| if (k > 0) | |
| { | |
| //Can't rename function parameters that are pointers: | |
| if ((gasm[j].f == USERFUNC) && (newvarnam[newvar[gasm[j].g].proti+k-1] <= 'Z')) break; | |
| //Another case: .f .r[0] .r[1] .r[2] | |
| //m2 = 0 MOV m2 0 KUNUSED | |
| //IF !(m2) GOTO l1 IF0 KUNUSED m2 l1 | |
| if (gasm[i].f == MOV) | |
| { | |
| for(k=i+1;k<j;k++) //Make sure no instructions in between write the register | |
| { | |
| if (gasmeq(gasm[i].r[1],gasm[k].r[0])) break; | |
| if (gasm[k].f == USERFUNC) //pointer params can write register.. | |
| { | |
| for(l=gasm[k].n;l>0;l--) | |
| { | |
| m = newvarnam[newvar[gasm[k].g].proti+l-1]; if (m > 'Z') continue; //not a pointer param | |
| if (l < 3) rp = &gasm[k].r[l]; else rp = &rxi[gasm[k].rxi+l-3]; | |
| if ((gasm[i].r[1].r == rp->r) && (gasm[i].r[1].q == rp->q)) break; //pointer matches var | |
| } | |
| if (l > 0) break; | |
| } | |
| } | |
| if (k < j) break; | |
| //All is good! | |
| for(k=gasm[j].n;k>0;k--) | |
| { | |
| if (k < 3) rp = &gasm[j].r[k]; else rp = &rxi[gasm[j].rxi+k-3]; | |
| if (gasmeq(gasm[i].r[0],*rp)) *rp = gasm[i].r[1]; | |
| } | |
| got = 1; | |
| break; | |
| } | |
| else | |
| { | |
| //Don't allow "nop" to set got = 1; (would result in endless loop) | |
| if (gasmeq(gasm[i].r[0],gasm[j].r[0])) break; | |
| if (gasm[j].r[0].r == KUNUSED) break; | |
| //Don't allow this to happen: | |
| //m8 = m8 * 3 m4 = m8 * 3 | |
| //m4 = m5 + 1 -> m4 = m5 + 1 | |
| //m4 = m4 - m8 m4 = m4 - m4 | |
| for(k=i+1;k<j;k++) | |
| if (gasmeq(gasm[j].r[0],gasm[k].r[0])) break; | |
| if (k < j) break; | |
| //beg: | |
| //i: r3 = r2 * r2 | |
| //j: r2 = r3 | |
| // goto beg | |
| if (anyreadsbeforewrites(j,gasm[i].r[0],1)) break; | |
| if (anyreadsbeforewrites(i,gasm[j].r[0],1)) break; | |
| //All is good! | |
| for(k=gasm[j].n;k>0;k--) | |
| { | |
| if (k < 3) rp = &gasm[j].r[k]; else rp = &rxi[gasm[j].rxi+k-3]; | |
| if (gasmeq(gasm[i].r[0],*rp)) *rp = gasm[j].r[0]; | |
| } | |
| gasm[i].r[0] = gasm[j].r[0]; | |
| got = 1; | |
| break; | |
| } | |
| } | |
| if (gasmeq(gasm[i].r[0],gasm[j].r[0])) break; | |
| if (gasm[j].r[0].r == KUNUSED) break; | |
| } | |
| } | |
| #endif | |
| #if 1 | |
| //Compact unused registers | |
| if (!duringparse) | |
| { | |
| k = KECX; | |
| for(i=gecnt-1;i>=mingecnt;i--) | |
| { | |
| if ((gasm[i].r[0].r < k) || ((gasm[i].r[0].r&0xf0000000) != KECX)) continue; | |
| for(j=i+1;j<gecnt;j++) if (gasmeq(gasm[i].r[0],gasm[j].r[0])) break; | |
| if (j >= gecnt) | |
| { | |
| if (gasm[i].r[0].r != k) | |
| { //swap registers: k,gasm[i].r[0] | |
| got = 1; m = gasm[i].r[0].r; | |
| for(j=gecnt-1;j>=0;j--) | |
| for(l=gasm[j].n;l>=0;l--) | |
| { | |
| if (l < 3) rp = &gasm[j].r[l]; else rp = &rxi[gasm[j].rxi+l-3]; | |
| if (rp->r == k) rp->r = m; else if (rp->r == m) rp->r = k; | |
| } | |
| //Make sure variable names match their associated registers (for flow control) | |
| for(j=gnumarg;j<newvarnum;j++) | |
| { if (newvar[j].r == k) newvar[j].r = m; else if (newvar[j].r == m) newvar[j].r = k; } | |
| } | |
| k += 8; | |
| } | |
| } | |
| } | |
| #endif | |
| #if 1 | |
| //Remove dead code, for example: { r0 = cos(r1); r0 = i1; } -> { r0 = i1; } | |
| for(i=gecnt-2;i>=mingecnt;i--) | |
| { //Can't remove labels or jumps | |
| if ((gasm[i].f == NUL) || (gasm[i].f == USERFUNC) || (gasm[i].r[0].r == KUNUSED)) continue; | |
| if (!anyreadsbeforewrites(i+1,gasm[i].r[0],0)) | |
| { | |
| gecnt--; got = 1; //Delete instruction [i] | |
| for(j=i;j<gecnt;j++) gasm[j] = gasm[j+1]; | |
| } | |
| } | |
| #endif | |
| #if 1 | |
| //Change IF0 to IF1 if: | |
| // 1.Next line is GOTO, and... | |
| // 2.Dest of IF's GOTO is exactly 2 lines ahead (this is important!) | |
| //"if !(m0) goto l1; goto l2;l1:" -> "if (m0) goto l2" | |
| for(i=gecnt-3;i>=mingecnt;i--) | |
| if ((gasm[i].f == IF0) && | |
| (gasm[i+1].f == GOTO) && | |
| ((gasm[i+2].f == NUL) && (gasmeq(gasm[i].r[1],gasm[i+2].r[0])))) | |
| { | |
| gasm[i].f = IF1; gasm[i].r[1] = gasm[i+1].r[1]; | |
| gecnt--; got = 1; //Delete instruction i+1 | |
| for(j=i+1;j<gecnt;j++) gasm[j] = gasm[j+1]; | |
| } | |
| #endif | |
| #if 1 | |
| //Remove GOTO/IF0/IF1 if its label points to next line | |
| for(i=gecnt-2;i>=mingecnt;i--) | |
| if (((gasm[i].f == GOTO) || (gasm[i].f == IF0) || (gasm[i].f == IF1)) && (gasm[i+1].f == NUL) && (gasmeq(gasm[i].r[1],gasm[i+1].r[0]))) | |
| { | |
| gecnt--; got = 1; //Delete instruction i | |
| for(j=i;j<gecnt;j++) gasm[j] = gasm[j+1]; | |
| } | |
| #endif | |
| #if 1 | |
| //Remove anything after a GOTO or RETURN that isn't a label | |
| for(i=gecnt-2;i>mingecnt;i--) | |
| if (((gasm[i-1].f == GOTO) || (gasm[i-1].f == RETURN)) && (gasm[i].f != NUL)) | |
| { | |
| for(k=i+1;k<gecnt;k++) if (gasm[k].f == NUL) break; | |
| k -= i; | |
| gecnt -= k; got = 1; //Delete instructions {i .. i+k-1} | |
| for(j=i;j<gecnt;j++) gasm[j] = gasm[j+k]; | |
| } | |
| #endif | |
| #if 1 | |
| //Remove unused labels (except for gasm[0] which is used as dummy filler later) | |
| for(i=gecnt-1;i>mingecnt;i--) | |
| if (gasm[i].f == NUL) | |
| { | |
| for(j=gecnt-1;j>=0;j--) | |
| if (((gasm[j].f == IF0) || (gasm[j].f == IF1) || (gasm[j].f == GOTO)) && | |
| (gasmeq(gasm[j].r[1],gasm[i].r[0]))) break; | |
| if (j < 0) | |
| { | |
| k = gasm[i].r[0].r; | |
| gecnt--; got = 1; //Delete instruction i (label) | |
| for(j=i;j<gecnt;j++) gasm[j] = gasm[j+1]; | |
| //Re-wire labels (can't have holes!) | |
| numlabels--; | |
| for(j=gecnt-1;j>=0;j--) | |
| { | |
| if (gasm[j].f == NUL) m = 0; else m = 1; | |
| if (gasm[j].r[m].r == (signed)KEIP+numlabels) gasm[j].r[m].r = k; | |
| } | |
| } | |
| } | |
| #endif | |
| } while (got); | |
| return(0); | |
| } | |
| #if (COMPILE == 0) | |
| //kasm87c: similar functionality to kasm87, but pure C code - making it slower and more portable | |
| //ANSI va_arg: supported on all compilers | |
| #include <stdarg.h> | |
| static long gkasm87cptr; | |
| typedef struct | |
| { | |
| //0xc7 0x05 [gkasm87cptr] [imm32] ;mov gkasm87cptr, kcd | |
| //0xe9 [imm32] ;jmp kasm87c()-eip; | |
| char codestub[16]; //Must be first in structure | |
| double *globval; long gccnt, gstnum, arrnum; | |
| gasmtyp *gasm; long gecnt; | |
| rtyp *rxi; long numrxi; | |
| evalextyp *gevalext; long gevalextnum; | |
| newvartyp *newvar; long newvarnum, gnumarg; | |
| char *newvarnam; long newvarplc; | |
| long stackdoubs; | |
| char data[0]; //Must be last in structure | |
| } kcd_t; //Kasm87C Data | |
| double kasm87c_run (char *parmdat, kcd_t *kcd) | |
| { | |
| double *p[17]; | |
| long i, j, k, plst[16]; | |
| if (kcd->gecnt <= 0) return(0.0); | |
| plst[((unsigned long)KECX)>>28] = ((long)gvlp -KECX); | |
| plst[((unsigned long)KEDX)>>28] = ((long)kcd->globval-KEDX); | |
| plst[((unsigned long)KESP)>>28] = ((long)parmdat -KESP); | |
| plst[((unsigned long)KPTR)>>28] = ((long)parmdat -KPTR); | |
| plst[((unsigned long)KIMM)>>28] = ((long) -KIMM); | |
| plst[((unsigned long)KGLB)>>28] = ((long)gstatmem -KGLB); | |
| gvlp += kcd->stackdoubs; //FUK:could do stack overflow check here | |
| for(i=0;i<kcd->gecnt;i++) | |
| { | |
| //{ | |
| //char mybuf[260]; getfuncnam(&kcd->gasm[i],mybuf); //For debugging only | |
| //printf("%3d: %10s %08x %08x %08x\n",i,mybuf,kcd->gasm[i].r[0].r,kcd->gasm[i].r[1].r,kcd->gasm[i].r[2].r); | |
| //} | |
| for(j=2;j>=0;j--) | |
| { | |
| p[j] = (double *)(plst[((unsigned long)kcd->gasm[i].r[j].r)>>28]+(long)kcd->gasm[i].r[j].r); | |
| if ((kcd->gasm[i].r[j].r&0xf0000000) == KPTR) p[j] = (*(double **)p[j]) + (kcd->gasm[i].r[j].q); | |
| if ((kcd->gasm[i].r[j].r&0xf0000000) == KIMM) p[j] = (double *)(((long)kcd->gevalext[((long)p[j])].ptr)+kcd->gasm[i].r[j].q*8); | |
| if ((kcd->gasm[i].r[j].r&0xf0000000) == KGLB) p[j] = (double *)((gstatmem + ((long)p[j]))+kcd->gasm[i].r[j].q*8); | |
| if ((kcd->gasm[i].r[j].r&0xf0000000) == KEDX) p[j] += kcd->gasm[i].r[j].q; | |
| } | |
| switch(kcd->gasm[i].f) | |
| { | |
| case NUL: break; | |
| case GOTO: i = kcd->gasm[i].r[0].r; break; //r[1].r is label, r[0].r is gasm index | |
| case RETURN:gvlp -= kcd->stackdoubs; return(*p[1]); | |
| case RND: (*p[0]) = ((double)krand())*(double)oneover2_31; break; | |
| case NRND: (*p[0]) = nrnd(); break; | |
| case MOV: (*p[0]) = (*p[1]); break; | |
| case NEGMOV:(*p[0]) = -(*p[1]); break; | |
| case NEQU0: (*p[0]) = ((*p[1]) != 0); break; | |
| case IF0: if ((*p[2]) == 0) { i = kcd->gasm[i].r[0].r; } break; //r[1].r is label, r[0].r is gasm index | |
| case IF1: if ((*p[2]) != 0) { i = kcd->gasm[i].r[0].r; } break; //r[1].r is label, r[0].r is gasm index | |
| case FABS: (*p[0]) = fabs(*p[1]); break; | |
| case SGN: (*p[0]) = ((*p[1])>0) - ((*p[1])<0); break; | |
| case UNIT: (*p[0]) = ((*p[1])==0)*.5 + ((*p[1])>0); break; | |
| case FLOOR: (*p[0]) = floor(*p[1]); break; | |
| case CEIL: (*p[0]) = ceil(*p[1]); break; | |
| case ROUND0: case ROUND0_32: if (*p[1] >= 0) (*p[0]) = floor(*p[1]); else (*p[0]) = -floor(-(*p[1])); break; | |
| case SIN: (*p[0]) = sin(*p[1]); break; | |
| case COS: (*p[0]) = cos(*p[1]); break; | |
| case TAN: (*p[0]) = tan(*p[1]); break; | |
| case ASIN: (*p[0]) = asin(*p[1]); break; | |
| case ACOS: (*p[0]) = acos(*p[1]); break; | |
| case ATAN: (*p[0]) = atan(*p[1]); break; | |
| case SQRT: (*p[0]) = sqrt(*p[1]); break; | |
| case EXP: (*p[0]) = exp(*p[1]); break; | |
| case FACT: (*p[0]) = fact(*p[1]); break; | |
| case LOG: (*p[0]) = log(*p[1]); break; | |
| case TIMES: (*p[0]) = (*p[1])*(*p[2]); break; | |
| case SLASH: (*p[0]) = (*p[1])/(*p[2]); break; | |
| case PERC: (*p[0]) = (*p[1])-floor((*p[1])/fabs(*p[2]))*fabs(*p[2]); break; | |
| case PLUS: //no break intentional | |
| case FADD: (*p[0]) = (*p[1])+(*p[2]); break; | |
| case MINUS: (*p[0]) = (*p[1])-(*p[2]); break; | |
| case POW: (*p[0]) = pow(*p[1],*p[2]); break; | |
| case MIN: if ((*p[2]) < (*p[1])) (*p[0]) = (*p[2]); else (*p[0]) = (*p[1]); break; | |
| case MAX: if ((*p[2]) > (*p[1])) (*p[0]) = (*p[2]); else (*p[0]) = (*p[1]); break; | |
| case FMOD: (*p[0]) = fmod(*p[1],*p[2]); break; | |
| case ATAN2: (*p[0]) = atan2(*p[1],*p[2]); break; | |
| case LOGB: (*p[0]) = log(*p[1])/log(*p[2]); break; | |
| case LES: (*p[0]) = ((*p[1]) < (*p[2])); break; | |
| case LESEQ: (*p[0]) = ((*p[1]) <= (*p[2])); break; | |
| case MOR: (*p[0]) = ((*p[1]) > (*p[2])); break; | |
| case MOREQ: (*p[0]) = ((*p[1]) >= (*p[2])); break; | |
| case EQU: (*p[0]) = ((*p[1]) == (*p[2])); break; | |
| case NEQU: (*p[0]) = ((*p[1]) != (*p[2])); break; | |
| case LAND: (*p[0]) = ((*p[1]) && (*p[2])); break; | |
| case LOR: (*p[0]) = ((*p[1]) || (*p[2])); break; | |
| case PEEK: | |
| { | |
| j = (long)(*p[2]); | |
| k = kcd->newvar[kcd->gasm[i].r[1].nv].maxind; //quick&dirty bounds check; if 2^x, use "and" | |
| if ((k) && (!((k-1)&k))) j &= (k-1); else if ((unsigned long)j >= (unsigned long)k) j = 0; | |
| (*p[0]) = p[1][j]; | |
| } | |
| break; | |
| case POKE: case POKETIMES: case POKESLASH: case POKEPERC: case POKEPLUS: case POKEMINUS: | |
| { | |
| rtyp *rp = &kcd->rxi[kcd->gasm[i].rxi]; | |
| p[3] = (double *)(plst[((unsigned long)rp->r)>>28]+(long)rp->r); | |
| if ((rp->r&0xf0000000) == KPTR) p[3] = (*(double **)p[3]) + (rp->q); | |
| if ((rp->r&0xf0000000) == KIMM) p[3] = (double *)(((long)kcd->gevalext[((long)p[3])].ptr)+rp->q*8); | |
| if ((rp->r&0xf0000000) == KGLB) p[3] = (double *)((gstatmem + ((long)p[3]))+rp->q*8); | |
| j = (long)(*p[2]); | |
| k = kcd->newvar[kcd->gasm[i].r[1].nv].maxind; //quick&dirty bounds check; if 2^x, use "and" | |
| if ((k) && (!((k-1)&k))) j &= (k-1); else if ((unsigned long)j >= (unsigned long)k) j = 0; | |
| switch(kcd->gasm[i].f) | |
| { | |
| case POKE: p[1][j] = (*p[3]); break; | |
| case POKETIMES: p[1][j] *= (*p[3]); break; | |
| case POKESLASH: p[1][j] /= (*p[3]); break; | |
| case POKEPERC: p[1][j] -= floor((p[1][j])/fabs(*p[3]))*fabs(*p[3]); break; | |
| case POKEPLUS: p[1][j] += (*p[3]); break; | |
| case POKEMINUS: p[1][j] -= (*p[3]); break; | |
| } | |
| } | |
| break; | |
| case USERFUNC: | |
| { | |
| char *cptr; | |
| rtyp *rp; | |
| double (__cdecl *dafunc)(double,...); | |
| if (kcd->gasm[i].n > 16) { gvlp -= kcd->stackdoubs; return(*p[0]); } //Display error! | |
| for(j=kcd->gasm[i].n;j>2;j--) | |
| { | |
| rp = &kcd->rxi[kcd->gasm[i].rxi+j-3]; | |
| p[j] = (double *)(plst[((unsigned long)rp->r)>>28]+(long)rp->r); | |
| if ((rp->r&0xf0000000) == KPTR) p[j] = (*(double **)p[j]) + (rp->q); | |
| if ((rp->r&0xf0000000) == KIMM) p[j] = (double *)(((long)kcd->gevalext[((long)p[j])].ptr)+rp->q*8); | |
| if ((rp->r&0xf0000000) == KGLB) p[j] = (double *)((gstatmem + ((long)p[j]))+rp->q*8); | |
| } | |
| if ((kcd->newvar[kcd->gasm[i].g].r&0xf0000000) == KIMM) | |
| dafunc = ((double (__cdecl *)(double,...))kcd->gevalext[kcd->newvar[kcd->gasm[i].g].r&0x0fffffff].ptr); | |
| else dafunc = ((double (__cdecl *)(double,...))*(long *)(plst[((unsigned long)KESP)>>28]+kcd->newvar[gasm[i].g].r)); | |
| cptr = &kcd->newvarnam[kcd->newvar[kcd->gasm[i].g].proti]; | |
| switch(kcd->gasm[i].n) //This seems to be the only way to do pure C implementation; it sucks! | |
| { | |
| case 1: | |
| if (!strncmp(cptr,"d",1)) { (*p[0]) = dafunc(*p[1]); break; } | |
| break; | |
| case 2: | |
| if (!strncmp(cptr,"dd",2)) { (*p[0]) = dafunc(*p[1],*p[2]); break; } | |
| if (!strncmp(cptr,"dD",2)) { (*p[0]) = dafunc(*p[1], p[2]); break; } | |
| break; | |
| case 3: | |
| if (!strncmp(cptr,"ddd",3)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3]); break; } | |
| if (!strncmp(cptr,"ddD",3)) { (*p[0]) = dafunc(*p[1],*p[2], p[3]); break; } | |
| if (!strncmp(cptr,"dDD",3)) { (*p[0]) = dafunc(*p[1], p[2], p[3]); break; } | |
| break; | |
| case 4: | |
| if (!strncmp(cptr,"dddd",4)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4]); break; } | |
| if (!strncmp(cptr,"dddD",4)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3], p[4]); break; } | |
| if (!strncmp(cptr,"ddDD",4)) { (*p[0]) = dafunc(*p[1],*p[2], p[3], p[4]); break; } | |
| if (!strncmp(cptr,"dDDD",4)) { (*p[0]) = dafunc(*p[1], p[2], p[3], p[4]); break; } | |
| break; | |
| case 5: | |
| if (!strncmp(cptr,"ddddd",5)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4],*p[5]); break; } | |
| if (!strncmp(cptr,"ddddD",5)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4], p[5]); break; } | |
| if (!strncmp(cptr,"dddDD",5)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3], p[4], p[5]); break; } | |
| if (!strncmp(cptr,"ddDDD",5)) { (*p[0]) = dafunc(*p[1],*p[2], p[3], p[4], p[5]); break; } | |
| if (!strncmp(cptr,"dDDDD",5)) { (*p[0]) = dafunc(*p[1], p[2], p[3], p[4], p[5]); break; } | |
| break; | |
| case 6: | |
| if (!strncmp(cptr,"dddddd",6)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4],*p[5],*p[6]); break; } | |
| if (!strncmp(cptr,"dddddD",6)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4],*p[5], p[6]); break; } | |
| if (!strncmp(cptr,"ddddDD",6)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4], p[5], p[6]); break; } | |
| if (!strncmp(cptr,"dddDDD",6)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3], p[4], p[5], p[6]); break; } | |
| if (!strncmp(cptr,"ddDDDD",6)) { (*p[0]) = dafunc(*p[1],*p[2], p[3], p[4], p[5], p[6]); break; } | |
| if (!strncmp(cptr,"dDDDDD",6)) { (*p[0]) = dafunc(*p[1], p[2], p[3], p[4], p[5], p[6]); break; } | |
| break; | |
| case 7: | |
| if (!strncmp(cptr,"ddddddd",7)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4],*p[5],*p[6],*p[7]); break; } | |
| if (!strncmp(cptr,"ddddddD",7)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4],*p[5],*p[6], p[7]); break; } | |
| if (!strncmp(cptr,"dddddDD",7)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4],*p[5], p[6], p[7]); break; } | |
| if (!strncmp(cptr,"ddddDDD",7)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4], p[5], p[6], p[7]); break; } | |
| if (!strncmp(cptr,"dddDDDD",7)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3], p[4], p[5], p[6], p[7]); break; } | |
| if (!strncmp(cptr,"ddDDDDD",7)) { (*p[0]) = dafunc(*p[1],*p[2], p[3], p[4], p[5], p[6], p[7]); break; } | |
| if (!strncmp(cptr,"dDDDDDD",7)) { (*p[0]) = dafunc(*p[1], p[2], p[3], p[4], p[5], p[6], p[7]); break; } | |
| break; | |
| case 8: | |
| if (!strncmp(cptr,"dddddddd",8)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4],*p[5],*p[6],*p[7],*p[8]); break; } | |
| if (!strncmp(cptr,"dddddddD",8)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4],*p[5],*p[6],*p[7], p[8]); break; } | |
| if (!strncmp(cptr,"ddddddDD",8)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4],*p[5],*p[6], p[7], p[8]); break; } | |
| if (!strncmp(cptr,"dddddDDD",8)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4],*p[5], p[6], p[7], p[8]); break; } | |
| if (!strncmp(cptr,"ddddDDDD",8)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4], p[5], p[6], p[7], p[8]); break; } | |
| if (!strncmp(cptr,"dddDDDDD",8)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3], p[4], p[5], p[6], p[7], p[8]); break; } | |
| if (!strncmp(cptr,"ddDDDDDD",8)) { (*p[0]) = dafunc(*p[1],*p[2], p[3], p[4], p[5], p[6], p[7], p[8]); break; } | |
| if (!strncmp(cptr,"dDDDDDDD",8)) { (*p[0]) = dafunc(*p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8]); break; } | |
| break; | |
| case 9: | |
| if (!strncmp(cptr,"ddddddddd",9)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4],*p[5],*p[6],*p[7],*p[8],*p[9]); break; } | |
| if (!strncmp(cptr,"ddddddddD",9)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4],*p[5],*p[6],*p[7],*p[8], p[9]); break; } | |
| if (!strncmp(cptr,"dddddddDD",9)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4],*p[5],*p[6],*p[7], p[8], p[9]); break; } | |
| if (!strncmp(cptr,"ddddddDDD",9)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4],*p[5],*p[6], p[7], p[8], p[9]); break; } | |
| if (!strncmp(cptr,"dddddDDDD",9)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4],*p[5], p[6], p[7], p[8], p[9]); break; } | |
| if (!strncmp(cptr,"ddddDDDDD",9)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4], p[5], p[6], p[7], p[8], p[9]); break; } | |
| if (!strncmp(cptr,"dddDDDDDD",9)) { (*p[0]) = dafunc(*p[1],*p[2],*p[3], p[4], p[5], p[6], p[7], p[8], p[9]); break; } | |
| if (!strncmp(cptr,"ddDDDDDDD",9)) { (*p[0]) = dafunc(*p[1],*p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]); break; } | |
| if (!strncmp(cptr,"dDDDDDDDD",9)) { (*p[0]) = dafunc(*p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]); break; } | |
| break; | |
| case 10: (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4],*p[5],*p[6],*p[7],*p[8],*p[9],*p[10]); break; | |
| case 11: (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4],*p[5],*p[6],*p[7],*p[8],*p[9],*p[10],*p[11]); break; | |
| case 12: (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4],*p[5],*p[6],*p[7],*p[8],*p[9],*p[10],*p[11],*p[12]); break; | |
| case 13: (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4],*p[5],*p[6],*p[7],*p[8],*p[9],*p[10],*p[11],*p[12],*p[13]); break; | |
| case 14: (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4],*p[5],*p[6],*p[7],*p[8],*p[9],*p[10],*p[11],*p[12],*p[13],*p[14]); break; | |
| case 15: (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4],*p[5],*p[6],*p[7],*p[8],*p[9],*p[10],*p[11],*p[12],*p[13],*p[14],*p[15]); break; | |
| case 16: (*p[0]) = dafunc(*p[1],*p[2],*p[3],*p[4],*p[5],*p[6],*p[7],*p[8],*p[9],*p[10],*p[11],*p[12],*p[13],*p[14],*p[15],*p[16]); break; | |
| //how to do unlimited cases without using switch? | |
| } | |
| break; | |
| } | |
| } | |
| } | |
| gvlp -= kcd->stackdoubs; return(*p[0]); | |
| } | |
| double __cdecl kasm87cp (double *first, ...) | |
| { | |
| va_list marker; | |
| kcd_t *kcd; | |
| long i, j; | |
| char parmdat[sizeof(double)*16]; | |
| kcd = (kcd_t *)gkasm87cptr; | |
| *(double **)&parmdat[0] = first; | |
| va_start(marker,first); j = 4; | |
| for(i=1;i<kcd->gnumarg;i++) | |
| { | |
| if ((newvar[i].parnum < 0) && ((newvar[i].r&0xf0000000) == KESP)) | |
| { *(double *)&parmdat[j] = va_arg(marker,double); j += 8; } //8 byte variable | |
| else { *(long *)&parmdat[j] = va_arg(marker,long ); j += 4; } //4 byte variable/function pointer | |
| } | |
| va_end(marker); //Keep for compatibility | |
| return(kasm87c_run(parmdat,kcd)); | |
| } | |
| double __cdecl kasm87c (double first, ...) | |
| { | |
| va_list marker; | |
| kcd_t *kcd; | |
| long i, j; | |
| char parmdat[sizeof(double)*16]; | |
| kcd = (kcd_t *)gkasm87cptr; | |
| *(double *)&parmdat[0] = first; | |
| va_start(marker,first); j = 8; | |
| for(i=1;i<gnumarg;i++) | |
| { | |
| if ((newvar[i].parnum < 0) && ((newvar[i].r&0xf0000000) == KESP)) | |
| { *(double *)&parmdat[j] = va_arg(marker,double); j += 8; } //8 byte variable | |
| else { *(long *)&parmdat[j] = va_arg(marker,long ); j += 4; } //4 byte variable/function pointer | |
| } | |
| va_end(marker); //Keep for compatibility | |
| return(kasm87c_run(parmdat,kcd)); | |
| } | |
| kcd_t *kasm87c_copyglob2struct (long stackdoubs) | |
| { | |
| kcd_t *kcd; | |
| long l; | |
| l = sizeof(kcd_t); | |
| l += gccnt*sizeof( globval[0]) + gstnum + arrnum; | |
| l += gecnt*sizeof( gasm[0]); | |
| l += numrxi*sizeof( rxi[0]); | |
| l += gevalextnum*sizeof( gevalext[0]); | |
| l += newvarnum*sizeof( newvar[0]); | |
| l += newvarplc*sizeof(newvarnam[0]); | |
| kcd = (kcd_t *)malloc(l); if (!kcd) return(0); | |
| kcd->gccnt = gccnt; kcd->gstnum = gstnum; kcd->arrnum = arrnum; | |
| kcd->gecnt = gecnt; | |
| kcd->numrxi = numrxi; | |
| kcd->gevalextnum = gevalextnum; | |
| kcd->newvarnum = newvarnum; kcd->gnumarg = gnumarg; | |
| kcd->newvarplc = newvarplc; | |
| l = (long)&kcd->data; | |
| kcd->globval = (double *)l; l += gccnt*sizeof( globval[0]) + gstnum + arrnum; | |
| kcd->gasm = (gasmtyp *)l; l += gecnt*sizeof( gasm[0]); | |
| kcd->rxi = (rtyp *)l; l += numrxi*sizeof( rxi[0]); | |
| kcd->gevalext = (evalextyp *)l; l += gevalextnum*sizeof( gevalext[0]); | |
| kcd->newvar = (newvartyp *)l; l += newvarnum*sizeof( newvar[0]); | |
| kcd->newvarnam = (char *)l; l += newvarplc*sizeof(newvarnam[0]); | |
| #if 0 | |
| printf("kcd_t:%d",sizeof(kcd_t)); | |
| if (gccnt|gstnum|arrnum) printf(" + globval:%d" , gccnt*sizeof(globval[0]) + gstnum + arrnum); | |
| if (gecnt) printf(" + gasm:%d" , gecnt*sizeof(gasm[0])); | |
| if (numrxi) printf(" + rxi:%d" , numrxi*sizeof(rxi[0])); | |
| if (gevalextnum) printf(" + gevalext:%d" ,gevalextnum*sizeof(gevalext[0])); | |
| if (newvarnum) printf(" + newvar:%d" , newvarnum*sizeof(newvar[0])); | |
| if (newvarplc) printf(" + newvarnam:%d", newvarplc*sizeof(newvarnam[0])); | |
| printf(" = %d\n",l); | |
| #endif | |
| if (gccnt|gstnum|arrnum) memcpy(kcd->globval ,globval , gccnt*sizeof( globval[0]) + gstnum + arrnum); | |
| if (gecnt) memcpy(kcd->gasm ,gasm , gecnt*sizeof( gasm[0])); | |
| if (numrxi) memcpy(kcd->rxi ,rxi , numrxi*sizeof( rxi[0])); | |
| if (gevalextnum) memcpy(kcd->gevalext ,gevalext ,gevalextnum*sizeof( gevalext[0])); //need only the ptr's | |
| if (newvarnum) memcpy(kcd->newvar ,newvar , newvarnum*sizeof( newvar[0])); | |
| if (newvarplc) memcpy(kcd->newvarnam,newvarnam, newvarplc*sizeof(newvarnam[0])); | |
| kcd->stackdoubs = stackdoubs; | |
| #if defined(_M_IX86) || defined(__i386__) | |
| *(short *)kcd->codestub = 0x5c7; | |
| *(long *)&kcd->codestub[2] = (long)&gkasm87cptr; | |
| *(long *)&kcd->codestub[6] = (long)kcd; | |
| kcd->codestub[10] = 0xe9; | |
| if ((newvar[0].parnum < 0) && ((newvar[0].r&0xf0000000) == KESP)) | |
| *(long *)&kcd->codestub[11] = ((long)kasm87c )-((long)&kcd->codestub[15]); | |
| else *(long *)&kcd->codestub[11] = ((long)kasm87cp)-((long)&kcd->codestub[15]); | |
| #else | |
| //Increase codestub size to 32? | |
| //PPC guess: | |
| //lis r4,imm16hi | |
| //ori r4,r4,imm16lo | |
| //?mflr r5 | |
| //addis r4,?r5,ha16(_glob-?) | |
| //stw r4,lo16(_glob-?)(r2) | |
| //b (kasm87c - &codestub[20?]) | |
| //FUK: This temp hack allows 1 script in memory to run on a non-x86 platform | |
| //To fix it: machine code for the native architecture must be written to move and jump (like above) | |
| gkasm87cptr = (long)kcd; | |
| if ((newvar[0].parnum < 0) && ((newvar[0].r&0xf0000000) == KESP)) | |
| return((void *)kasm87c); | |
| else return((void *)kasm87cp); | |
| #endif | |
| return(kcd); | |
| } | |
| #endif | |
| //mode=0: overwrite backward jumps to 0's | |
| //mode=1: restore backward jumps | |
| void kasm87jumpback (void *f, long mode) | |
| { | |
| long i, jbn; | |
| jumpback_t *jb; | |
| jbn = *(long *)(((long)f)-FUNCBYTEOFFS+0); if (jbn <= 0) return; | |
| jb = (jumpback_t *)((*(long *)(((long)f)-FUNCBYTEOFFS+4)) + ((long)f)); | |
| switch(mode) | |
| { | |
| case 0: | |
| for(i=jbn-1;i>=0;i--) if (*(long *)(jb[i].addr) < 0) *(long *)(jb[i].addr) = 0; | |
| break; | |
| case 1: | |
| for(i=jbn-1;i>=0;i--) *(long *)(jb[i].addr) = jb[i].val; | |
| break; | |
| } | |
| } | |
| void kasm87free (void *f) | |
| { | |
| long i; | |
| if (!f) return; | |
| i = *(long *)(((long)f)-FUNCBYTEOFFS+8); if (i) free((void *)i); //free global static block | |
| free((void *)(((long)f)-FUNCBYTEOFFS)); | |
| } | |
| static void *kasm87comp (char *bakz) | |
| { | |
| rtyp tr, *rp; | |
| long i, j, k, l, pcnt, bcnt, maxpcnt, got, jumpatnum, whitespc, regnum, subparms, isaddr, espoff, inquotes; | |
| long onewvarplc, ibody, ljumpbacknum; | |
| char *tbuf; | |
| globi = 0; gop[globi] = NUL; arrnum = 0; | |
| gecnt = 0; gccnt = 0; gstnum = 0; ginitvalnum = 0; kasm87leng = 0; | |
| //Insert dummy label at beginning | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].f = NUL; gasm[gecnt].r[0].r = KEIP; gecnt = 1; | |
| numlabels = 1; | |
| tbuf = (char *)malloc(strlen(bakz)+1); | |
| if (!tbuf) { strcpy(kasm87err,"ERROR: malloc failed"); return(0); } | |
| //Pre-processor | |
| ibody = 0; l = 0; got = 0; pcnt = 0; bcnt = 0; maxpcnt = 0; gnumarg = -1; whitespc = 0; onewvarplc = newvarplc; | |
| newlabplc = 0; newlabnum = 0; | |
| inquotes = 0; | |
| for(i=0;bakz[i];i++) | |
| { | |
| if ((!inquotes) && (bakz[i] == 32) /*|| (bakz[i] == 9)*/) { whitespc = 1; continue; } //strip Space/Tab | |
| if (got) continue; | |
| tbuf[l] = bakz[i]; | |
| if ((tbuf[l] == '\"') && ((!l) || (tbuf[l-1] != '\\'))) inquotes ^= 1; | |
| if (inquotes) { l++; continue; } | |
| if (tbuf[l] == '(') | |
| { | |
| pcnt++; if (pcnt > maxpcnt) maxpcnt = pcnt; | |
| if ((gnumarg < 0) && (pcnt == 1)) { ibody = l; continue; } //Note: '(' not stored in tbuf because no l++; | |
| } | |
| if (tbuf[l] == ')') | |
| { | |
| pcnt--; if (pcnt < 0) break; | |
| if ((!pcnt) && (gnumarg < 0)) | |
| { | |
| tbuf[l] = ','; subparms = -2; isaddr = 0; espoff = 0; | |
| //Save new variable/function names,indices,parameters to list | |
| for(k=ibody;k<=l;k++) //verify parnam //Example param string: "a,b(,&),&c,d()," | |
| { // ibody l | |
| //Save new variable name&index to list | |
| if (tbuf[k] == '(') | |
| { | |
| pcnt++; subparms = 0; | |
| if (pcnt == 1) | |
| { | |
| checkvarchars(newvarplc+1); newvarnam[newvarplc++] = 0; | |
| newvar[newvarnum].proti = newvarplc; | |
| } | |
| continue; | |
| } | |
| if (tbuf[k] == ')') | |
| { | |
| pcnt--; | |
| if (pcnt == 0) | |
| { | |
| checkvarchars(newvarplc+1); | |
| switch(isaddr) | |
| { | |
| case 0: newvarnam[newvarplc] = 'd'; break; //double | |
| case 1: newvarnam[newvarplc] = 'D'; break; //double* | |
| case 2: newvarnam[newvarplc] = 'C'; break; //char* (const string only) | |
| } | |
| newvarplc++; subparms++; isaddr = 0; continue; | |
| } | |
| continue; | |
| } | |
| if (tbuf[k] == '[') | |
| { | |
| if (isaddr) { sprintf(kasm87err,"ERROR: bad syntax, param %d",newvarnum); free(tbuf); return(0); } | |
| checkvarchars(newvarplc+1); newvarnam[newvarplc++] = 0; | |
| checkvars(newvarnum+1); | |
| newvar[newvarnum].proti = newvarplc; | |
| newvar[newvarnum].parnum = 0; | |
| bcnt = parse_dimensions(tbuf,&k,1); | |
| if (!bcnt) { free(tbuf); return(0); } | |
| isaddr = 1; | |
| k--; //without this, continue would skip past comma | |
| continue; | |
| } | |
| if (tbuf[k] == '&') | |
| { | |
| if (isaddr) { sprintf(kasm87err,"ERROR: bad syntax, param %d",newvarnum); free(tbuf); return(0); } | |
| isaddr = 1; continue; | |
| } | |
| if (tbuf[k] == '$') | |
| { | |
| if (isaddr) { sprintf(kasm87err,"ERROR: bad syntax, param %d",newvarnum); free(tbuf); return(0); } | |
| isaddr = 2; continue; | |
| } | |
| if (tbuf[k] == ',') | |
| { | |
| //if (tbuf[k] != ',') { sprintf(kasm87err,"ERROR: bad syntax, param %d",newvarnum); free(tbuf); return(0); } | |
| if (pcnt == 1) | |
| { | |
| checkvarchars(newvarplc+1); | |
| switch(isaddr) | |
| { | |
| case 0: newvarnam[newvarplc] = 'd'; break; //double | |
| case 1: newvarnam[newvarplc] = 'D'; break; //double* | |
| case 2: newvarnam[newvarplc] = 'C'; break; //char* (const string only) | |
| } | |
| newvarplc++; subparms++; isaddr = 0; continue; | |
| } | |
| if ((k == ibody) && (k == l)) break; //special case with no arguments | |
| if (((tbuf[k+1] == ',') && (k < l)) || (k == ibody) || (k == l-1)) | |
| { sprintf(kasm87err,"ERROR: bad syntax, param %d",newvarnum); free(tbuf); return(0); } | |
| //if (!isaddr) isaddr = -1; | |
| if (subparms < 0) | |
| { | |
| if (!bcnt) | |
| { | |
| checkvarchars(newvarplc+1); newvarnam[newvarplc++] = 0; | |
| checkvars(newvarnum+1); newvar[newvarnum].proti = -1; | |
| } | |
| } | |
| checkvars(newvarnum+1); | |
| newvar[newvarnum].nami = onewvarplc; onewvarplc = newvarplc; | |
| newvar[newvarnum].r = espoff+KESP; | |
| newvar[newvarnum].maxind = bcnt; | |
| //Subparms: < 0 for double/array, >= 0 for user function pointers (# is # parms) | |
| if (subparms < 0) | |
| { | |
| if (!isaddr) { espoff += 8; } | |
| else { newvar[newvarnum].r = espoff+KPTR; espoff += 4; } | |
| } | |
| else | |
| { | |
| if (isaddr) { sprintf(kasm87err,"ERROR: bad syntax, param %d",newvarnum); free(tbuf); return(0); } | |
| espoff += 4; | |
| } | |
| if ((subparms < 0) && (bcnt)) newvar[newvarnum].parnum ^= -1; else //1's complement | |
| newvar[newvarnum].parnum = subparms; | |
| j = getnewvarhash(&newvarnam[newvar[newvarnum].nami]); | |
| newvar[newvarnum].hashn = newvarhash[j]; newvarhash[j] = newvarnum; | |
| subparms = -2; | |
| newvarnum++; | |
| isaddr = 0; bcnt = 0; | |
| continue; | |
| } | |
| if (isvarchar(tbuf[k])) | |
| { if (!bcnt) { checkvarchars(newvarplc+1); newvarnam[newvarplc++] = tbuf[k]; } continue; } | |
| sprintf(kasm87err,"ERROR: bad syntax, param %d",newvarnum); free(tbuf); return(0); | |
| } | |
| gnumarg = newvarnum; | |
| tbuf[l] = 0; ibody = l+1; | |
| } | |
| } | |
| if (whitespc) | |
| { | |
| whitespc = 0; | |
| if ((l > ibody) && (isvarchar(tbuf[l-1])) && (isvarchar(tbuf[l]))) | |
| { tbuf[l+1] = tbuf[l]; tbuf[l] = ' '; l++; } | |
| } | |
| l++; | |
| } | |
| tbuf[l++] = 0; | |
| //if (ibody+1 >= l) { strcpy(kasm87err,"ERROR: no function defined"); free(tbuf); return(0); } | |
| if (pcnt < 0) { strcpy(kasm87err,"ERROR: too many )"); free(tbuf); return(0); } | |
| if (pcnt > 0) { strcpy(kasm87err,"ERROR: not enough )"); free(tbuf); return(0); } | |
| if (maxpcnt > 16) { strcpy(kasm87err,"ERROR: () nested > 16"); free(tbuf); return(0); } | |
| //Add global library variables&functions to parameter list | |
| for(i=0;i<gevalextnum;i++) | |
| { | |
| bcnt = 0; subparms = -2; k = 0; | |
| //printf("|%s|:",gevalext[i].nam); //useful for debugging | |
| checkvars(newvarnum+1); | |
| newvar[newvarnum].nami = newvarplc; | |
| if (gevalext[i].nam[k] == '&') { k++; bcnt = 1; newvar[newvarnum].proti = -1; } | |
| if (gevalext[i].nam[k] == '$') k++; //ignore/invalid | |
| if (gevalext[i].nam[k] == '\"') //Handle compiled strings for pic(),snd(),getpicsiz(),fil(),etc.. | |
| { | |
| do | |
| { | |
| checkvarchars(newvarplc+1); newvarnam[newvarplc++] = gevalext[i].nam[k++]; | |
| } while ((gevalext[i].nam[k]) && (gevalext[i].nam[k] != '\"')); | |
| } | |
| while ((gevalext[i].nam[k]) && (gevalext[i].nam[k] != '(') && (gevalext[i].nam[k] != '[')) //FUK && (gevalext[i].nam[k] != ' ')) | |
| { checkvarchars(newvarplc+1); newvarnam[newvarplc++] = gevalext[i].nam[k++]; } | |
| if ((newvarplc > newvar[newvarnum].nami) && (newvarnam[newvarplc-1] == ' ')) newvarplc--; | |
| checkvarchars(newvarplc+1); newvarnam[newvarplc++] = 0; | |
| if (gevalext[i].nam[k] == ' ') k++; //Remove whitespace after function name (doesn't work) | |
| if (gevalext[i].nam[k] == '(') | |
| { | |
| newvar[newvarnum].proti = newvarplc; subparms = 0; | |
| do | |
| { | |
| checkvarchars(newvarplc+1); subparms++; k++; | |
| while ((isvarchar(gevalext[i].nam[k])) || (gevalext[i].nam[k] == ' ')) k++; //Don't care about variable name here; skip it | |
| newvarnam[newvarplc] = 'd'; | |
| if (gevalext[i].nam[k] == '&') { newvarnam[newvarplc] = 'D'; k++; } | |
| if (gevalext[i].nam[k] == '$') { newvarnam[newvarplc] = 'C'; k++; } | |
| if (gevalext[i].nam[k] == '.') { newvarnam[newvarplc] = 'e'; k++; } | |
| newvarplc++; | |
| while ((isvarchar(gevalext[i].nam[k])) || (gevalext[i].nam[k] == ' ')) k++; //Don't care about variable name here; skip it | |
| if (gevalext[i].nam[k] == '[') | |
| { | |
| newvarnam[newvarplc-1] = 'D'; | |
| if (!parse_dimensions(gevalext[i].nam,&k,0)) | |
| { sprintf(kasm87err,"ERROR: %s has bad dimensions",&newvarnam[newvar[newvarnum].nami]); free(tbuf); return(0); } | |
| } | |
| } while (gevalext[i].nam[k] == ','); | |
| //printf("|"); for(j=newvar[newvarnum].proti;j<newvarplc;j++) printf("%c",newvarnam[j]); printf("|"); //useful for debugging | |
| } | |
| else if (gevalext[i].nam[k] == '[') | |
| { | |
| newvar[newvarnum].proti = newvarplc; | |
| newvar[newvarnum].parnum = 0; | |
| bcnt = parse_dimensions(gevalext[i].nam,&k,1); | |
| if (!bcnt) { sprintf(kasm87err,"ERROR: %s has bad dimensions",&newvarnam[newvar[newvarnum].nami]); free(tbuf); return(0); } | |
| subparms = ~newvar[newvarnum].parnum; //1's complement | |
| } | |
| //printf("|%s|:maxind:%d,parnum:%d\n",&newvarnam[newvar[newvarnum].nami],bcnt,subparms); //useful for debugging | |
| newvar[newvarnum].r = i+KIMM; | |
| newvar[newvarnum].maxind = bcnt; | |
| newvar[newvarnum].parnum = subparms; //<0 for double/arrays:~#dimens, >=0 for function where #parms | |
| j = getnewvarhash(&newvarnam[newvar[newvarnum].nami]); | |
| newvar[newvarnum].hashn = newvarhash[j]; newvarhash[j] = newvarnum; | |
| newvarnum++; | |
| } | |
| gnumglob = newvarnum; | |
| parsefunc(&tbuf[ibody],-1,-1); free(tbuf); | |
| if (globi == -1) { gecnt = 0; return(0); } | |
| //Make sure all label destinations exist | |
| for(i=0,tbuf=newlabnam;i<newlabnum;i++,tbuf=&tbuf[strlen(newlabnam)+1]) | |
| if (newlabind[i] < 0) | |
| { sprintf(kasm87err,"ERROR: label %s not found",tbuf); gecnt = 0; return(0); } | |
| //If last instruction is label, add return(0); at end | |
| if (!gasm[gecnt-1].f) | |
| { | |
| checkops(gecnt+1); memset(&gasm[gecnt],0,sizeof(gasmtyp)); | |
| gasm[gecnt].r[0].r = KUNUSED; | |
| gasm[gecnt].r[1].r = gccnt*8+KEDX; | |
| gasm[gecnt].r[2].r = KUNUSED; | |
| gasm[gecnt].n = 1; | |
| gasm[gecnt].f = RETURN; gecnt++; | |
| checkops(gccnt+1); globval[gccnt++] = 0.0; | |
| } | |
| //Align section after strings to 8-byte boundary | |
| checkstrings((gstnum+7)&~7); | |
| while (gstnum&7) gstring[gstnum++] = 0; | |
| if (kasm87optimize) | |
| { | |
| if (kasmoptimizations(0,0) < 0) { gecnt = 0; return(0); } | |
| } | |
| //Get number of 8-byte memory locations needed for temp storage | |
| regnum = 0; | |
| for(i=gecnt-1;i>=0;i--) | |
| for(j=gasm[i].n;j>=0;j--) | |
| { | |
| if (j < 3) rp = &gasm[i].r[j]; else rp = &rxi[gasm[i].rxi+j-3]; | |
| if (((rp->r&0xf0000000) == KECX) && ((rp->r&0x0fffffff) > regnum)) regnum = (rp->r&0x0fffffff); | |
| if ((rp->r&0xf0000000) == KSTR) rp->r = (rp->r&0x0fffffff)+gccnt*8+KEDX; | |
| if ((rp->r&0xf0000000) == KARR) rp->r = (rp->r&0x0fffffff)+gccnt*8+gstnum+KEDX; | |
| } | |
| regnum = (regnum>>3)+1; | |
| #if (COMPILE == 0) | |
| //Necessary hack to make jumps in kasm87c faster (Uses gasm[*].r[0] which isn't used for jumps anyway) | |
| for(i=0;i<gecnt;i++) | |
| if ((gasm[i].f == IF0) || (gasm[i].f == IF1) || (gasm[i].f == GOTO)) | |
| for(j=0;j<gecnt;j++) | |
| if ((gasm[j].f == NUL) && (gasmeq(gasm[j].r[0],gasm[i].r[1]))) | |
| { gasm[i].r[0].r = j; break; } | |
| //Hack: append strings&static array variables&data to globval[] | |
| if (gccnt*8+gstnum+arrnum > sizeof(double)*maxops) | |
| if (!(globval = (double *)realloc(globval,gccnt*8+gstnum+arrnum))) | |
| { strcpy(kasm87err,"ERROR: malloc failed"); gecnt = 0; return(0); } | |
| memcpy(&globval[gccnt],gstring,gstnum); //Copy string tables to globval | |
| //memset(&globval[gccnt+(gstnum>>3)],0,arrnum); //Fill static variables&arrays with 0's | |
| //Fill static variables&arrays with 0's | |
| for(i=j=0;i<arrnum;i+=8,kasm87leng+=8) | |
| { | |
| if ((j < ginitvalnum) && (i == ginitval[j].i)) | |
| { *(double *)&globval[((gstnum+i)>>3)+gccnt] = ginitval[j].v; j++; } | |
| else *(double *)&globval[((gstnum+i)>>3)+gccnt] = 0; | |
| } | |
| if (!gvl) | |
| { | |
| gvl = (double *)malloc(65536*sizeof(double)); | |
| if (!gvl) { strcpy(kasm87err,"ERROR: malloc failed"); gecnt = 0; return(0); } | |
| gvlp = gvl; | |
| } | |
| return((void *)kasm87c_copyglob2struct(regnum)); | |
| //if ((newvar[0].parnum < 0) && ((newvar[0].r&0xf0000000) == KESP)) | |
| // return((void *)kasm87c); | |
| //else return((void *)kasm87cp); | |
| #else | |
| if (regnum > 4) { memnum = regnum-4; regnum = 4; } else memnum = 0; | |
| //0-4 temps: memnum = 0 | |
| // 5 temps: memnum = 1 | |
| // 6 temps: memnum = 2, etc... | |
| //Use up to 4 registers supported on the x87 stack | |
| //Replace all [ecx] with [esp], adjusting offsets by +4 or +(memnum<<3)+8 | |
| // | |
| //Ex. for: memnum=0,myfunc(x,y) | //Ex. for: memnum=2,myfunc(x,y) | |
| //------------------------------+--------------------------------- | |
| // [esp+ 0]: return address | [esp+ 0]: tempdat1 | |
| //->[esp+ 4]: param 1 (x) | [esp+ 8]: tempdat2 | |
| // [esp+12]: param 2 (y) | [esp+16]: (filler) | |
| // | [esp+20]: return address | |
| // |->[esp+24]: param 1 (x) | |
| // | [esp+32]: param 2 (y) | |
| if (!memnum) espoff = 4; else espoff = (memnum<<3)+8; //Offset ESP (passed params) by temp register size | |
| for(i=gecnt-1;i>=0;i--) | |
| for(j=gasm[i].n;j>=0;j--) | |
| { | |
| if (j < 3) rp = &gasm[i].r[j]; else rp = &rxi[gasm[i].rxi+j-3]; | |
| if ((rp->r&0xf0000000) == KESP) rp->r += espoff; | |
| else if ((rp->r&0xf0000000) == KPTR) rp->r += espoff; | |
| else if ((rp->r&0xf0000000) == KECX) | |
| { | |
| if ((rp->r&0x0fffffff) < (4<<3)) rp->r += (KFST-KECX); | |
| else rp->r += (KESP-KECX)-(4<<3); | |
| } | |
| } | |
| for(i=0;i<newvarnum;i++) | |
| if (((newvar[i].r&0xf0000000) == KESP) || ((newvar[i].r&0xf0000000) == KPTR)) | |
| newvar[i].r += espoff; | |
| #if 0 | |
| { //Show registers associated with variable names (for debug only) | |
| for(i=0;i<newvarnum;i++) | |
| { | |
| printf("%s: ",&newvarnam[newvar[i].nami]); | |
| if (i < gnumarg) printf("i%d\n",i); else printf("r%d\n",newvar[i].r); | |
| } printf("\n"); | |
| } | |
| #endif | |
| ljumpbacknum = 0; | |
| for(putwrite=0;putwrite<2;putwrite++) | |
| { | |
| kasm87leng = 0; jumpatnum = 0; fpustat = 0; | |
| //mov edx, ? (to be filled with globval pointer later) | |
| if ((gccnt) || (gstnum) || (arrnum)) { put1byte(0xba); put4byte(0); } | |
| if (memnum) | |
| { | |
| if ((memnum<<3)+4 <= 128) { put2byte(0xc483); put1byte(-((memnum<<3)+4)); } //add esp, -((memnum<<3)+4) | |
| else { put2byte(0xc481); put4byte(-((memnum<<3)+4)); } //add esp, -((memnum<<3)+4) | |
| } | |
| for(j=regnum;j>0;j--) put2byte(0xeed9); //fldz | |
| for(i=1;i<gecnt;i++) | |
| { | |
| switch(gasm[i].f) | |
| { | |
| case NUL: | |
| checklabs((gasm[i].r[0].r&0x0fffffff)+1); | |
| labpat[gasm[i].r[0].r&0x0fffffff] = kasm87leng; | |
| break; | |
| case GOTO: | |
| //put1byte(0xeb); put1byte(0); //jmp short ? | |
| put1byte(0xe9); put4byte(gasm[i].r[1].r&0x0fffffff); //jmp ? | |
| checklabs(jumpatnum+1); jumpat[jumpatnum++] = kasm87leng; | |
| if (putwrite) { checkjumpbacks(jumpbacknum+1); jumpback[jumpbacknum++].addr = (long)&compcode[kasm87leng-4]; } else ljumpbacknum++; | |
| break; | |
| case RETURN: | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| //Ensure stack is empty (except for return value which is st(0)) | |
| //WARNING: fcompp trick only works properly if BOTH regs are known to be valid (not empty) | |
| //for(j=regnum;j>0;j--) put2byte(0xd9dd); //fstp st(1) | |
| for(j=regnum;j>0;j--) /*if (fpustat&(1<<(j-1)))*/ put2byte(0xc0dd+(j<<8)); //ffree st(j) (ffree faster on P4: less dependency?) | |
| if (memnum) | |
| { | |
| if ((memnum<<3)+4 <= 128) { put2byte(0xec83); put1byte(-((memnum<<3)+4)); } //sub esp, -((memnum<<3)+4) | |
| else { put2byte(0xec81); put4byte(-((memnum<<3)+4)); } //sub esp, -((memnum<<3)+4) | |
| } | |
| put1byte(0xc3); //ret | |
| break; | |
| case MOV: //gvl[gasm[i].r[0]] = globval[gasm[i].r[1]]; | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| break; | |
| case NEGMOV: //gvl[gasm[i].r[0]] = -globval[gasm[i].r[1]]; | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| put2byte(0xe0d9); //fchs | |
| break; | |
| case NEQU0: //gvl[gasm[i].r[0]] = gvl[gasm[i].r[1]] != 0 | |
| if (((gasm[i].r[1].r&0xf0000000) == KFST) || ((gasm[i].r[1].r&0xf0000000) == KPTR) || ((gasm[i].r[1].r&0xf0000000) == KIMM) || ((gasm[i].r[1].r&0xf0000000) == KGLB)) | |
| { | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| if (!(cputype&(1<<15))) //no CMOV | |
| { | |
| put2byte(0xe4d9); //ftst | |
| put2byte(0xe0df); //fnstsw ax | |
| put2byte(0xd8dd); //fstp st(0) | |
| put2byte(0xe480); put1byte(0x40); //and ah, 0x40 | |
| put1byte(0x75); put1byte(4); //jnz short ? | |
| } | |
| else | |
| { | |
| put2byte(0xeed9); //fldz | |
| put2byte(0xf1df); //fcomip st(1) ;Requires >=PPRO | |
| put2byte(0xd8dd); //fstp st(0) | |
| put1byte(0x74); put1byte(4); //jz short ? | |
| } | |
| } | |
| else | |
| { | |
| putsib(0x8b,0x00,gasm[i].r[1]); //mov eax, dword ptr [?] | |
| gasm[i].r[1].r += 4; | |
| putsib(0x0b,0x00,gasm[i].r[1]); //or eax, dword ptr [?+4] | |
| gasm[i].r[1].r -= 4; | |
| put1byte(0x74); put1byte(4); //jz short ? | |
| } | |
| put2byte(0xe8d9); //fld1 | |
| put1byte(0xeb); put1byte(2); //jmp short endit | |
| put2byte(0xeed9); //skip: fldz | |
| break; | |
| case IF0: | |
| if (((gasm[i].r[2].r&0xf0000000) == KFST) || ((gasm[i].r[2].r&0xf0000000) == KPTR) || ((gasm[i].r[2].r&0xf0000000) == KIMM) || ((gasm[i].r[2].r&0xf0000000) == KGLB)) | |
| { | |
| put1stfld(i,gasm[i].r[2]); //fld qword ptr [?] | |
| if (!(cputype&(1<<15))) //no CMOV | |
| { | |
| put2byte(0xe4d9); //ftst | |
| put2byte(0xe0df); //fnstsw ax | |
| put2byte(0xd8dd); //fstp st(0) | |
| put2byte(0xe480); put1byte(0x40); //and ah, 0x40 | |
| put2byte(0x850f); put4byte(gasm[i].r[1].r&0x0fffffff); //jnz ? | |
| } | |
| else | |
| { | |
| put2byte(0xeed9); //fldz | |
| put2byte(0xf1df); //fcomip st(1) ;Requires >=PPRO | |
| put2byte(0xd8dd); //fstp st(0) | |
| put2byte(0x840f); put4byte(gasm[i].r[1].r&0x0fffffff); //jz ? | |
| } | |
| } | |
| else | |
| { | |
| putsib(0x8b,0x00,gasm[i].r[2]); //mov eax, dword ptr [?] | |
| gasm[i].r[2].r += 4; | |
| putsib(0x0b,0x00,gasm[i].r[2]); //or eax, dword ptr [?+4] | |
| gasm[i].r[2].r -= 4; | |
| put2byte(0x840f); put4byte(gasm[i].r[1].r&0x0fffffff); //jz ? | |
| } | |
| checklabs(jumpatnum+1); jumpat[jumpatnum++] = kasm87leng; | |
| if (putwrite) { checkjumpbacks(jumpbacknum+1); jumpback[jumpbacknum++].addr = (long)&compcode[kasm87leng-4]; } else ljumpbacknum++; | |
| break; | |
| case IF1: | |
| if (((gasm[i].r[2].r&0xf0000000) == KFST) || ((gasm[i].r[2].r&0xf0000000) == KPTR) || ((gasm[i].r[2].r&0xf0000000) == KIMM) || ((gasm[i].r[2].r&0xf0000000) == KGLB)) | |
| { | |
| put1stfld(i,gasm[i].r[2]); //fld qword ptr [?] | |
| if (!(cputype&(1<<15))) //no CMOV | |
| { | |
| put2byte(0xe4d9); //ftst | |
| put2byte(0xe0df); //fnstsw ax | |
| put2byte(0xd8dd); //fstp st(0) | |
| put2byte(0xe480); put1byte(0x40); //and ah, 0x40 | |
| put2byte(0x840f); put4byte(gasm[i].r[1].r&0x0fffffff); //jz ? | |
| } | |
| else | |
| { | |
| put2byte(0xeed9); //fldz | |
| put2byte(0xf1df); //fcomip st(1) ;Requires >=PPRO | |
| put2byte(0xd8dd); //fstp st(0) | |
| put2byte(0x850f); put4byte(gasm[i].r[1].r&0x0fffffff); //jnz ? | |
| } | |
| } | |
| else | |
| { | |
| putsib(0x8b,0x00,gasm[i].r[2]); //mov eax, dword ptr [?] | |
| gasm[i].r[2].r += 4; | |
| putsib(0x0b,0x00,gasm[i].r[2]); //or eax, dword ptr [?+4] | |
| gasm[i].r[2].r -= 4; | |
| put2byte(0x850f); put4byte(gasm[i].r[1].r&0x0fffffff); //jnz ? | |
| } | |
| checklabs(jumpatnum+1); jumpat[jumpatnum++] = kasm87leng; | |
| if (putwrite) { checkjumpbacks(jumpbacknum+1); jumpback[jumpbacknum++].addr = (long)&compcode[kasm87leng-4]; } else ljumpbacknum++; | |
| break; | |
| case RND: //gvl[gasm[i].r[0]] = ((double)krand())/2^31; break; | |
| //31-bit RND | |
| put1byte(0xa1); put4byte((long)&kholdrand); //mov eax, kholdrand | |
| put2byte(0xc069); put4byte(214013*2); //imul eax, 214013*2 | |
| put1byte(0x05); put4byte(2531011*2); //add eax, 2531011*2 | |
| put2byte(0xe8d1); //shr eax, 1 | |
| put1byte(0xa3); put4byte((long)&kholdrand); //mov kholdrand, eax | |
| put2byte(0x05db); put4byte((long)&kholdrand); //fild dword ptr [kholdrand] | |
| put2byte(0x0dd8); put4byte((long)&oneover2_31); //fmul dword ptr [oneover2_31] | |
| break; | |
| case NRND: //gvl[gasm[i].r[0]] = nrnd(); break; | |
| put1byte(0x52); //push edx | |
| put1byte(0xb8); put4byte((long)nrnd); //mov eax, offset nrnd //use this to allow code dup | |
| put2byte(0xd0ff); //call eax | |
| //put1byte(0xe8); put4byte(((long)nrnd)-((long)&compcode[kasm87leng+4])); //call nrnd (relative) | |
| put1byte(0x5a); //pop edx | |
| break; | |
| case POW: //gvl[gasm[i].r[0]] = pow(gvl[gasm[i].r[1]],gvl[gasm[i].r[2]]); break; //POW: x ^ y = 2^(LOG2(x)*y) | |
| //inline: y=-1 y=-.5 y=0 y=.5 y=1 kpow: y=-1 y=-.5 y=0 y=.5 y=1 | |
| // x=- 1 4816x 4816 4816x 4816 4816x x=- 1 488 488 528 488 488 //number is cc | |
| // x=-.5 4816x 4816 4816x 4816 4816x x=-.5 424 381 528 381 424 //x means bad result! | |
| // x= 0 4684 4684 4816x 2936 2936 x= 0 97 97 104 92 92 | |
| // x= .5 372 357 472 357 372 x= .5 408 393 492 393 408 | |
| // x= 1 444 444 472 444 444 x= 1 468 468 492 468 468 | |
| put1stfld(i,gasm[i].r[2]); //fld qword ptr [?] | |
| putsib(0xdd,0x01,gasm[i].r[1]); //fld qword ptr [?] | |
| #if 0 | |
| //inline: returns incorrect values and very slow for x <= 0! | |
| put2byte(0xf1d9); //fyl2x (st1 *= log2(st0), pop st) | |
| put1byte(0xb8); put4byte(((long)kexptval)+8); //mov eax, offset kexptval[8] | |
| put2byte(0x10db); //fist dword ptr [eax] | |
| put2byte(0x20da); //fisub dword ptr [eax] | |
| put2byte(0x0081); put4byte(0x3fff); //add dword ptr [eax], 0x3fff | |
| put2byte(0xf0d9); //f2xm1 | |
| put2byte(0x05d8); put4byte((long)&posone); //fadd dword ptr [posone] | |
| put2byte(0x68db); put1byte(0xf8); //fld tbyte ptr [eax-8] | |
| put2byte(0xc9de); //fmulp st(1), st(0) | |
| #else | |
| //kpow: returns correct results, but a bit slower for x > 0 (more overhead) | |
| put2byte(0xec83); put1byte(16); //sub esp, 16 | |
| put1byte(0xdd); put2byte(0x241c); //fstp qword ptr [esp] | |
| put4byte(0x08245cdd); //fstp qword ptr [esp+8] | |
| put1byte(0xb8); put4byte((long)kpow); //mov eax, offset kpow //use this to allow code dup | |
| put2byte(0xd0ff); //call eax | |
| //put1byte(0xe8); put4byte(((long)kpow)-((long)&compcode[kasm87leng+4])); //call kpow | |
| put2byte(0xc483); put1byte(16); //add esp, 16 | |
| #endif | |
| break; | |
| case TIMES: //gvl[gasm[i].r[0]] = gvl[gasm[i].r[1]]*gvl[gasm[i].r[2]]; break; | |
| if (gasmeq(gasm[i].r[1],gasm[i].r[2])) | |
| { | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| put2byte(0xc8dc); //fmul st, st(0) | |
| //tr.r = KFST; putsib(0xdc,0x08,tr); //fmul st(0) | |
| } | |
| else | |
| { | |
| if (gasmeq(gasm[i-1].r[0],gasm[i].r[1])) j = 1; else j = 2; | |
| put1stfld(i,gasm[i].r[j]); //fld qword ptr [?] | |
| putsib(0xdc,0x09,gasm[i].r[3-j]); //fmul qword ptr [?] | |
| } | |
| break; | |
| case SLASH: //gvl[gasm[i].r[0]] = gvl[gasm[i].r[1]]/gvl[gasm[i].r[2]]; break; | |
| if (gasmeq(gasm[i-1].r[0],gasm[i].r[2])) | |
| { | |
| put1stfld(i,gasm[i].r[2]); //fld qword ptr [?] | |
| putsib(0xdc,0x39,gasm[i].r[1]); //fdivr qword ptr [?] | |
| } | |
| else | |
| { | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| putsib(0xdc,0x31,gasm[i].r[2]); //fdiv qword ptr [?] | |
| } | |
| break; | |
| case PERC: //gvl[gasm[i].r[0]] = gvl[gasm[i].r[1]]-floor(gvl[gasm[i].r[1]]/gvl[gasm[i].r[2]])*gvl[gasm[i].r[2]]; break; | |
| putsib(0xdd,0x00,gasm[i].r[2]); //fld qword ptr [?] | |
| putsib(0xdd,0x01,gasm[i].r[1]); //fld qword ptr [?] | |
| put2byte(0xf8d9); //fprem | |
| put2byte(0xe4d9); //ftst | |
| put2byte(0xe0df); //fnstsw ax | |
| put2byte(0xe480); put1byte(0x01); //and ah, 0x01 | |
| put1byte(0x74); put1byte(6); //jz short skip | |
| put2byte(0xc1d9); //fld st(1) | |
| put2byte(0xe1d9); //fabs | |
| put2byte(0xc1de); //faddp st(1), st | |
| put2byte(0xd9dd); //skip: fstp st(1) | |
| break; | |
| case PLUS: //gvl[gasm[i].r[0]] = gvl[gasm[i].r[1]]+gvl[gasm[i].r[2]]; break; | |
| case FADD: //no break intentional | |
| if (gasmeq(gasm[i].r[1],gasm[i].r[2])) | |
| { | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| put2byte(0xc0dc); //fadd st, st(0) | |
| //tr.r = KFST; putsib(0xdc,0x00,tr); //fadd st(0) | |
| } | |
| else | |
| { | |
| if (gasmeq(gasm[i-1].r[0],gasm[i].r[1])) j = 1; else j = 2; | |
| put1stfld(i,gasm[i].r[j]); //fld qword ptr [?] | |
| putsib(0xdc,0x01,gasm[i].r[3-j]); //fadd qword ptr [?] | |
| } | |
| break; | |
| case MINUS: //gvl[gasm[i].r[0]] = gvl[gasm[i].r[1]]-gvl[gasm[i].r[2]]; break; | |
| if (gasmeq(gasm[i-1].r[0],gasm[i].r[2])) | |
| { | |
| put1stfld(i,gasm[i].r[2]); //fld qword ptr [?] | |
| putsib(0xdc,0x29,gasm[i].r[1]); //fsubr qword ptr [?] | |
| } | |
| else | |
| { | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| putsib(0xdc,0x21,gasm[i].r[2]); //fsub qword ptr [?] | |
| } | |
| break; | |
| case FABS: //gvl[gasm[i].r[0]] = fabs(gvl[gasm[i].r[1]]); | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| put2byte(0xe1d9); //fabs st, st(0) | |
| break; | |
| case SGN: //if (gvl[gasm[i].r[1]] < 0) gvl[gasm[i].r[0]] =-1.0; else if (gvl[gasm[i].r[1]] > 0) gvl[gasm[i].r[0]] = 1.0; else gvl[gasm[i].r[0]] = 0.0; break; | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| put2byte(0xe4d9); //ftst | |
| put2byte(0xe0df); //fnstsw ax | |
| put2byte(0xd8dd); //fstp st(0) | |
| put4byte(0x0100a966); //test ax, 0x0100 | |
| put1byte(0x74); put1byte(8); //jz short skip1 | |
| put2byte(0x05d9); put4byte((long)&negone); //fld dword ptr [negone] | |
| put1byte(0xeb); put1byte(12); //jmp short endit | |
| put4byte(0x4000a966); //skip1: test ax, 0x4000 | |
| put1byte(0x74); put1byte(4); //jz short skip2 | |
| put2byte(0xeed9); //fldz | |
| put1byte(0xeb); put1byte(2); //jmp short endit | |
| put2byte(0xe8d9); //skip2: fld1 | |
| break; | |
| case UNIT: //if (gvl[gasm[i].r[1]] < 0) gvl[gasm[i].r[0]] = 0.0; else if (gvl[gasm[i].r[1]] > 0) gvl[gasm[i].r[0]] = 1.0; else gvl[gasm[i].r[0]] = 0.5; break; | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| put2byte(0xe4d9); //ftst | |
| put2byte(0xe0df); //fnstsw ax | |
| put2byte(0xd8dd); //fstp st(0) | |
| put4byte(0x0100a966); //test ax, 0x0100 | |
| put1byte(0x74); put1byte(4); //jz short skip1 | |
| put2byte(0xeed9); //fldz | |
| put1byte(0xeb); put1byte(16); //jmp short endit | |
| put4byte(0x4000a966); //skip1: test ax, 0x4000 | |
| put1byte(0x74); put1byte(8); //jz short skip2 | |
| put2byte(0x05d9); put4byte((long)&pointfive); //fld dword ptr [pointfive] | |
| put1byte(0xeb); put1byte(2); //jmp short endit | |
| put2byte(0xe8d9); //skip2: fld1 | |
| break; | |
| case FLOOR: //gvl[gasm[i].r[0]] = floor(gvl[gasm[i].r[1]]); break; | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| put2byte(0xec83); put1byte(8); //sub esp, 8 | |
| tr.r = KESP; | |
| putsib(0xd99b,0x38,tr); //fstcw word ptr [esp] | |
| putsib(0x8b66,0x00,tr); //mov ax, word ptr [esp] | |
| put4byte(0xf0ff2566); //and ax, 0xf0ff | |
| put4byte(0x07000d66); //or ax, 0x0700 | |
| putsib(0x8766,0x00,tr); //xchg word ptr [esp], ax | |
| putsib(0xd9,0x28,tr); //fldcw word ptr [esp] | |
| putsib(0xdf,0x38,tr); //fistp qword ptr [esp] | |
| putsib(0xdf,0x28,tr); //fild qword ptr [esp] | |
| putsib(0x8966,0x00,tr); //mov word ptr [esp], ax | |
| putsib(0xd9,0x28,tr); //fldcw word ptr [esp] | |
| put2byte(0xc483); put1byte(8); //add esp, 8 | |
| break; | |
| case CEIL: //gvl[gasm[i].r[0]] = ceil(gvl[gasm[i].r[1]]); break; | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| put2byte(0xe0d9); //fchs | |
| put2byte(0xec83); put1byte(8); //sub esp, 8 | |
| tr.r = KESP; | |
| putsib(0xd99b,0x38,tr); //fstcw word ptr [esp] | |
| putsib(0x8b66,0x00,tr); //mov ax, word ptr [esp] | |
| put4byte(0xf0ff2566); //and ax, 0xf0ff | |
| put4byte(0x07000d66); //or ax, 0x0700 | |
| putsib(0x8766,0x00,tr); //xchg word ptr [esp], ax | |
| putsib(0xd9,0x28,tr); //fldcw word ptr [esp] | |
| putsib(0xdf,0x38,tr); //fistp qword ptr [esp] | |
| putsib(0xdf,0x28,tr); //fild qword ptr [esp] | |
| putsib(0x8966,0x00,tr); //mov word ptr [esp], ax | |
| putsib(0xd9,0x28,tr); //fldcw word ptr [esp] | |
| put2byte(0xc483); put1byte(8); //add esp, 8 | |
| put2byte(0xe0d9); //fchs | |
| break; | |
| case ROUND0: case ROUND0_32: //gvl[gasm[i].r[0]] = round0(gvl[gasm[i].r[1]]); break; | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| if (!(cputype&(1<<27))) //no SSE3 | |
| { | |
| put2byte(0xec83); put1byte(8); //sub esp, 8 | |
| put2byte(0x1cdd); put1byte(0x24); //fstp qword ptr [esp] | |
| put4byte(0x0424448b); //mov eax, dword ptr [esp+4] | |
| put1byte(0x52); //push edx | |
| put2byte(0xd08b); //mov edx, eax | |
| put1byte(0x25); put4byte(0x7ff00000); //and eax, 0x7ff00000 | |
| put2byte(0xe8c1); put1byte(17); //shr eax, 17 | |
| put2byte(0x9023); put4byte(((long)round0msk)+4); //and edx, dword ptr round0msk[eax+4] | |
| put4byte(0x08245489); //mov dword ptr [esp+8], edx | |
| put4byte(0x0424548b); //mov edx, dword ptr [esp+4] | |
| put2byte(0x9023); put4byte((long)round0msk); //and edx, dword ptr round0msk[eax] | |
| put4byte(0x04245489); //mov dword ptr [esp+4], edx | |
| put1byte(0x5a); //pop edx | |
| put2byte(0x04dd); put1byte(0x24); //fld qword ptr [esp] | |
| put2byte(0xc483); put1byte(8); //add esp, 8 | |
| } | |
| else | |
| { | |
| if (gasm[i].f == ROUND0_32) //array indices only need 32-bit precision | |
| { | |
| put2byte(0xec83); put1byte(4); //sub esp, 4 | |
| put2byte(0x0cdb); put1byte(0x24); //fisttp dword ptr [esp] | |
| put2byte(0x04db); put1byte(0x24); //fild qword ptr [esp] | |
| put2byte(0xc483); put1byte(4); //add esp, 4 | |
| } | |
| else | |
| { | |
| put2byte(0xec83); put1byte(8); //sub esp, 8 | |
| put2byte(0x0cdd); put1byte(0x24); //fisttp qword ptr [esp] | |
| put2byte(0x2cdf); put1byte(0x24); //fild qword ptr [esp] | |
| put2byte(0xc483); put1byte(8); //add esp, 8 | |
| } | |
| } | |
| break; | |
| case MIN: //if (gvl[gasm[i].r[2]] < gvl[gasm[i].r[1]]) gvl[gasm[i].r[0]] = gvl[gasm[i].r[2]]; else gvl[gasm[i].r[0]] = gvl[gasm[i].r[1]]; break; | |
| putsib(0xdd,0x00,gasm[i].r[1]); //fld qword ptr [?] | |
| putsib(0xdc,0x19,gasm[i].r[2]); //fcomp qword ptr [?] | |
| put2byte(0xe0df); //fnstsw ax | |
| put2byte(0xe480); put1byte(0x41); //and ah, 0x41 | |
| put1byte(0x75); put1byte(putlen(gasm[i].r[2])+3); //jnz short skip | |
| putsib(0xdd,0x00,gasm[i].r[2]); //fld qword ptr [?] | |
| put1byte(0xeb); put1byte(putlen(gasm[i].r[1])+1); //jmp short endit | |
| putsib(0xdd,0x00,gasm[i].r[1]); //fld qword ptr [?] | |
| break; | |
| case MAX: //if (gvl[gasm[i].r[2]] > gvl[gasm[i].r[1]]) gvl[gasm[i].r[0]] = gvl[gasm[i].r[2]]; else gvl[gasm[i].r[0]] = gvl[gasm[i].r[1]]; break; | |
| putsib(0xdd,0x00,gasm[i].r[1]); //fld qword ptr [?] | |
| putsib(0xdc,0x19,gasm[i].r[2]); //fcomp qword ptr [?] | |
| put2byte(0xe0df); //fnstsw ax | |
| put2byte(0xe480); put1byte(0x41); //and ah, 0x41 | |
| put1byte(0x74); put1byte(putlen(gasm[i].r[2])+3); //jz short skip | |
| putsib(0xdd,0x00,gasm[i].r[2]); //fld qword ptr [?] | |
| put1byte(0xeb); put1byte(putlen(gasm[i].r[1])+1); //jmp short endit | |
| putsib(0xdd,0x00,gasm[i].r[1]); //fld qword ptr [?] | |
| break; | |
| case FMOD: //gvl[gasm[i].r[0]] = fmod(gvl[gasm[i].r[1]],gvl[gasm[i].r[2]]); break; | |
| put1stfld(i,gasm[i].r[2]); //fld qword ptr [?] | |
| putsib(0xdd,0x01,gasm[i].r[1]); //fld qword ptr [?] | |
| put2byte(0xf8d9); //fprem | |
| put2byte(0xd9dd); //skip: fstp st(1) | |
| break; | |
| case SIN: //gvl[gasm[i].r[0]] = sin(gvl[gasm[i].r[1]]); break; | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| put2byte(0xfed9); //fsin | |
| break; | |
| case COS: //gvl[gasm[i].r[0]] = cos(gvl[gasm[i].r[1]]); break; | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| put2byte(0xffd9); //fcos | |
| break; | |
| case TAN: //gvl[gasm[i].r[0]] = tan(gvl[gasm[i].r[1]]); break; | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| put2byte(0xf2d9); //fptan | |
| put2byte(0xd8dd); //fstp st(0) //discard the 1.0 | |
| break; | |
| case ASIN: //gvl[gasm[i].r[0]] = acos(gvl[gasm[i].r[1]]); break; | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| put2byte(0xe8d9); //fld1 | |
| put2byte(0xc1d9); //fld st(1) | |
| put2byte(0xcad8); //fmul st, st(2) | |
| put2byte(0xe9de); //fsubp (st0 = st1-st0) | |
| put2byte(0xfad9); //fsqrt | |
| put2byte(0xf3d9); //fpatan | |
| break; | |
| case ACOS: //gvl[gasm[i].r[0]] = asin(gvl[gasm[i].r[1]]); break; | |
| put2byte(0xe8d9); //fld1 | |
| putsib(0xdd,0x01,gasm[i].r[1]); //fld qword ptr [?] | |
| put2byte(0xc8dc); //fmul st, st | |
| put2byte(0xe9de); //fsubp (st0 = st1-st0) | |
| put2byte(0xfad9); //fsqrt | |
| putsib(0xdd,0x01,gasm[i].r[1]); //fld qword ptr [?] | |
| put2byte(0xf3d9); //fpatan | |
| break; | |
| case ATAN: //gvl[gasm[i].r[0]] = atan(gvl[gasm[i].r[1]]); break; | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| put2byte(0xe8d9); //fld1 | |
| put2byte(0xf3d9); //fpatan | |
| break; | |
| case ATAN2: //gvl[gasm[i].r[0]] = atan2(gvl[gasm[i].r[1]],gvl[gasm[i].r[2]]); break; | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| putsib(0xdd,0x01,gasm[i].r[2]); //fld qword ptr [?] | |
| put2byte(0xf3d9); //fpatan | |
| break; | |
| case SQRT: //gvl[gasm[i].r[0]] = sqrt(gvl[gasm[i].r[1]]); break; | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| put2byte(0xfad9); //fsqrt | |
| break; | |
| case EXP: //gvl[gasm[i].r[0]] = exp(gvl[gasm[i].r[1]]); break; | |
| put2byte(0xead9); //fldl2e | |
| putsib(0xdc,0x09,gasm[i].r[1]); //fmul qword ptr [?] | |
| #if 0 | |
| //This code is not thread-safe | |
| put1byte(0xb8); put4byte(((long)kexptval)+8); //mov eax, offset kexptval[8] | |
| put2byte(0x10db); //fist dword ptr [eax] | |
| put2byte(0x20da); //fisub dword ptr [eax] | |
| put2byte(0x0081); put4byte(0x3fff); //add dword ptr [eax], 0x3fff | |
| put2byte(0xf0d9); //f2xm1 | |
| put2byte(0x05d8); put4byte((long)&posone); //fadd dword ptr [posone] | |
| put2byte(0x68db); put1byte(0xf8); //fld tbyte ptr [eax-8] | |
| put2byte(0xc9de); //fmulp st(1), st(0) | |
| #else | |
| //Use this for Multithreaded code (~80cc slower than unsafe) | |
| put2byte(0xec83); put1byte(8); //sub esp, 8 | |
| put2byte(0x14db); put1byte(0x24); //fist dword ptr [esp] | |
| put2byte(0x24da); put1byte(0x24); //fisub dword ptr [esp] | |
| put2byte(0x0481); put1byte(0x24); put4byte(0x3fff); //add dword ptr [esp], 0x3fff | |
| put2byte(0xf0d9); //f2xm1 | |
| put2byte(0x05d8); put4byte((long)&posone); //fadd dword ptr [posone] | |
| put1byte(0x68); put4byte(0x80000000); //push 0x80000000 | |
| put2byte(0x006a); //push 0 | |
| put2byte(0x2cdb); put1byte(0x24); //fld tbyte ptr [esp] | |
| put2byte(0xc9de); //fmulp st(1), st(0) | |
| put2byte(0xc483); put1byte(16); //add esp, 16 | |
| #endif | |
| break; | |
| case FACT: //gvl[gasm[i].r[0]] = fact(gvl[gasm[i].r[1]]); break; | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| put2byte(0xec83); put1byte(8); //sub esp, 8 | |
| put1byte(0xdd); put2byte(0x241c); //fstp qword ptr [esp] | |
| put1byte(0xb8); put4byte((long)fact); //mov eax, offset fact //use this to allow code dup | |
| put2byte(0xd0ff); //call eax | |
| //put1byte(0xe8); put4byte(((long)fact)-((long)&compcode[kasm87leng+4])); //call fact | |
| put2byte(0xc483); put1byte(0x08); //add esp, 8 | |
| break; | |
| case LOG: //gvl[gasm[i].r[0]] = log(gvl[gasm[i].r[1]]); break; | |
| put2byte(0xedd9); //fldln2 (log(2)/log(10)) | |
| putsib(0xdd,0x01,gasm[i].r[1]); //fld qword ptr [?] | |
| put2byte(0xf1d9); //fyl2x (st1 *= log2(st0), pop st) | |
| break; | |
| case LOGB: //gvl[gasm[i].r[0]] = log(gvl[gasm[i].r[1]])/log(gvl[gasm[i].r[2]]); break; | |
| put2byte(0xe8d9); //fld1 | |
| putsib(0xdd,0x01,gasm[i].r[1]); //fld qword ptr [?] | |
| put2byte(0xf1d9); //fyl2x (st1 *= log2(st0), pop st) | |
| put2byte(0xe8d9); //fld1 | |
| putsib(0xdd,0x02,gasm[i].r[2]); //fld qword ptr [?] | |
| put2byte(0xf1d9); //fyl2x (st1 *= log2(st0), pop st) | |
| put2byte(0xf9de); //fdivp | |
| break; | |
| case PEEK: //gvl[gasm[i].r[0]] = *(double *)(((long)&gvl[gasm[i].r[1]])+gvl[gasm[i].r[2]]*8); break; | |
| put1stfld(i,gasm[i].r[2]); //fld qword ptr [?] | |
| if (cputype&(1<<27)) //SSE3 | |
| { | |
| put2byte(0xec83); put1byte(4); //sub esp, 4 | |
| put2byte(0x0cdb); put1byte(0x24); //fisttp dword ptr [esp] | |
| put1byte(0x58); //pop eax | |
| } | |
| else | |
| { | |
| //Round ninf (floor). Algo lifted from Agner Fog's optimizing_assembly.pdf | |
| put2byte(0xec83); put1byte(8); //sub esp, 8 | |
| put2byte(0x14db); put1byte(0x24); //fist dword ptr [esp] (rounded value) | |
| put2byte(0x24da); put1byte(0x24); //fisub dword ptr [esp] (sub rounded value) | |
| put4byte(0x04245cd9); //fstp dword ptr [esp+4] (diff) | |
| put1byte(0x58); //pop eax (rounded value) | |
| put1byte(0x59); //pop ecx (diff (float)) | |
| put2byte(0xc181); put4byte(0x7fffffff);//add ecx, 0x7fffffff (set CF if diff < -0) | |
| put2byte(0xd883); put1byte(0); //sbb eax, 0 (dec if x-round(x) < -0) | |
| } | |
| //quick&dirty bounds check | |
| j = newvar[gasm[i].r[1].nv].maxind; | |
| if ((j) && (!((j-1)&j))) //if 2^x, use "and" | |
| { | |
| if (j <= 128) { put2byte(0xe083); put1byte(j-1); } //and eax, imm8 | |
| else { put1byte(0x25); put4byte(j-1); } //and eax, imm32 | |
| } | |
| else | |
| { | |
| put1byte(0x3d); put4byte(j); //cmp eax, j | |
| put2byte(0x0272); //jc short skipzeroit | |
| put2byte(0xc033); //xor eax, eax | |
| } | |
| if (((gasm[i].r[1].r&0xf0000000) == KIMM) || ((gasm[i].r[1].r&0xf0000000) == KGLB)) | |
| { | |
| put2byte(0x04dd); put1byte(0xc5); //fld qword ptr [eax*8+imm32] | |
| tr = gasm[i].r[1]; | |
| if (putwrite) | |
| { | |
| checkpatch(patchnum+1); | |
| patch[patchnum].lptr = (long *)&compcode[kasm87leng]; | |
| patch[patchnum].ind = tr.r; | |
| patchnum++; | |
| } | |
| put4byte(tr.q*8); | |
| } | |
| else | |
| { | |
| putsib(0x8d,0x08,gasm[i].r[1]); //lea ecx, [?] | |
| //Warning: do not put any putsib calls between lea&fld because it can modify ecx | |
| put2byte(0x04dd); put1byte(0xc1); //fld qword ptr [eax*8+ecx] | |
| } | |
| break; | |
| case LES: //gvl[gasm[i].r[0]] = gvl[gasm[i].r[1]] < gvl[gasm[i].r[2]]; | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| putsib(0xdc,0x19,gasm[i].r[2]); //fcomp qword ptr [?] | |
| put2byte(0xe0df); //fnstsw ax | |
| put2byte(0xe480); put1byte(0x01); //and ah, 0x01 | |
| put1byte(0x74); put1byte(4); //jz short skip | |
| put2byte(0xe8d9); //fld1 | |
| put1byte(0xeb); put1byte(2); //jmp short endit | |
| put2byte(0xeed9); //fldz | |
| break; | |
| case LESEQ: //gvl[gasm[i].r[0]] = gvl[gasm[i].r[1]] <= gvl[gasm[i].r[2]]; | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| putsib(0xdc,0x19,gasm[i].r[2]); //fcomp qword ptr [?] | |
| put2byte(0xe0df); //fnstsw ax | |
| put2byte(0xe480); put1byte(0x41); //and ah, 0x41 | |
| put1byte(0x74); put1byte(4); //jz short skip | |
| put2byte(0xe8d9); //fld1 | |
| put1byte(0xeb); put1byte(2); //jmp short endit | |
| put2byte(0xeed9); //fldz | |
| break; | |
| case MOR: //gvl[gasm[i].r[0]] = gvl[gasm[i].r[1]] > gvl[gasm[i].r[2]]; | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| putsib(0xdc,0x19,gasm[i].r[2]); //fcomp qword ptr [?] | |
| put2byte(0xe0df); //fnstsw ax | |
| put2byte(0xe480); put1byte(0x41); //and ah, 0x41 | |
| put1byte(0x75); put1byte(4); //jnz short skip | |
| put2byte(0xe8d9); //fld1 | |
| put1byte(0xeb); put1byte(2); //jmp short endit | |
| put2byte(0xeed9); //fldz | |
| break; | |
| case MOREQ: //gvl[gasm[i].r[0]] = gvl[gasm[i].r[1]] >= gvl[gasm[i].r[2]]; | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| putsib(0xdc,0x19,gasm[i].r[2]); //fcomp qword ptr [?] | |
| put2byte(0xe0df); //fnstsw ax | |
| put2byte(0xe480); put1byte(0x01); //and ah, 0x01 | |
| put1byte(0x75); put1byte(4); //jnz short skip | |
| put2byte(0xe8d9); //fld1 | |
| put1byte(0xeb); put1byte(2); //jmp short endit | |
| put2byte(0xeed9); //fldz | |
| break; | |
| case EQU: //gvl[gasm[i].r[0]] = gvl[gasm[i].r[1]] == gvl[gasm[i].r[2]]; | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| putsib(0xdc,0x19,gasm[i].r[2]); //fcomp qword ptr [?] | |
| put2byte(0xe0df); //fnstsw ax | |
| put2byte(0xe480); put1byte(0x40); //and ah, 0x40 | |
| put1byte(0x74); put1byte(4); //jz short skip | |
| put2byte(0xe8d9); //fld1 | |
| put1byte(0xeb); put1byte(2); //jmp short endit | |
| put2byte(0xeed9); //fldz | |
| break; | |
| case NEQU: //gvl[gasm[i].r[0]] = gvl[gasm[i].r[1]] != gvl[gasm[i].r[2]]; | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| putsib(0xdc,0x19,gasm[i].r[2]); //fcomp qword ptr [?] | |
| put2byte(0xe0df); //fnstsw ax | |
| put2byte(0xe480); put1byte(0x40); //and ah, 0x40 | |
| put1byte(0x75); put1byte(4); //jnz short skip | |
| put2byte(0xe8d9); //fld1 | |
| put1byte(0xeb); put1byte(2); //jmp short endit | |
| put2byte(0xeed9); //fldz | |
| break; | |
| case LAND: //gvl[gasm[i].r[0]] = gvl[gasm[i].r[1]] && gvl[gasm[i].r[2]]; | |
| if (((gasm[i].r[1].r&0xf0000000) == KFST) || | |
| ((gasm[i].r[1].r&0xf0000000) == KPTR) || | |
| ((gasm[i].r[1].r&0xf0000000) == KIMM) || | |
| ((gasm[i].r[2].r&0xf0000000) == KFST) || | |
| ((gasm[i].r[2].r&0xf0000000) == KPTR) || | |
| ((gasm[i].r[2].r&0xf0000000) == KIMM) || | |
| ((gasm[i].r[2].r&0xf0000000) == KGLB)) | |
| { | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| put2byte(0xe4d9); //ftst | |
| put2byte(0xe0df); //fnstsw ax | |
| put2byte(0xd8dd); //fstp st(0) | |
| put2byte(0xe480); put1byte(0x40); //and ah, 0x40 | |
| put1byte(0x75); put1byte(putlen(gasm[i].r[2])+16); //jnz short ? | |
| putsib(0xdd,0x00,gasm[i].r[2]); //fld qword ptr [?] | |
| put2byte(0xe4d9); //ftst | |
| put2byte(0xe0df); //fnstsw ax | |
| put2byte(0xd8dd); //fstp st(0) | |
| put2byte(0xe480); put1byte(0x40); //and ah, 0x40 | |
| put1byte(0x75); put1byte(4); //jnz short ? | |
| } | |
| else | |
| { | |
| putsib(0x8b,0x00,gasm[i].r[1]); //mov eax, dword ptr [?] | |
| gasm[i].r[1].r += 4; | |
| putsib(0x0b,0x00,gasm[i].r[1]); //or eax, dword ptr [?+4] | |
| gasm[i].r[1].r -= 4; | |
| tr = gasm[i].r[2]; tr.r += 4; | |
| put1byte(0x74); put1byte(putlen(gasm[i].r[2])+putlen(tr)+8); //jz short skip | |
| putsib(0x8b,0x00,gasm[i].r[2]); //mov eax, dword ptr [?] | |
| putsib(0x0b,0x00,tr); //or eax, dword ptr [?+4] | |
| put1byte(0x74); put1byte(4); //jz short skip | |
| } | |
| put2byte(0xe8d9); //fld1 | |
| put1byte(0xeb); put1byte(2); //jmp short endit | |
| put2byte(0xeed9); //skip: fldz | |
| break; | |
| case LOR: //gvl[gasm[i].r[0]] = gvl[gasm[i].r[1]] || gvl[gasm[i].r[2]]; | |
| if (((gasm[i].r[1].r&0xf0000000) == KFST) || | |
| ((gasm[i].r[1].r&0xf0000000) == KPTR) || | |
| ((gasm[i].r[1].r&0xf0000000) == KIMM) || | |
| ((gasm[i].r[2].r&0xf0000000) == KFST) || | |
| ((gasm[i].r[2].r&0xf0000000) == KPTR) || | |
| ((gasm[i].r[2].r&0xf0000000) == KIMM) || | |
| ((gasm[i].r[2].r&0xf0000000) == KGLB)) | |
| { | |
| put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| put2byte(0xe4d9); //ftst | |
| put2byte(0xe0df); //fnstsw ax | |
| put2byte(0xd8dd); //fstp st(0) | |
| put2byte(0xe480); put1byte(0x40); //and ah, 0x40 | |
| put1byte(0x74); put1byte(putlen(gasm[i].r[2])+16); //jz short ? | |
| putsib(0xdd,0x00,gasm[i].r[2]); //fld qword ptr [?] | |
| put2byte(0xe4d9); //ftst | |
| put2byte(0xe0df); //fnstsw ax | |
| put2byte(0xd8dd); //fstp st(0) | |
| put2byte(0xe480); put1byte(0x40); //and ah, 0x40 | |
| put1byte(0x74); put1byte(4); //jz short ? | |
| } | |
| else | |
| { | |
| putsib(0x8b,0x00,gasm[i].r[1]); //mov eax, dword ptr [?] | |
| tr = gasm[i].r[1]; tr.r += 4; | |
| putsib(0x0b,0x00,tr); //or eax, dword ptr [?+4] | |
| putsib(0x0b,0x00,gasm[i].r[2]); //or eax, dword ptr [?] | |
| tr = gasm[i].r[2]; tr.r += 4; | |
| putsib(0x0b,0x00,tr); //or eax, dword ptr [?+4] | |
| put1byte(0x75); put1byte(4); //jnz short skip | |
| } | |
| put2byte(0xeed9); //fldz | |
| put1byte(0xeb); put1byte(2); //jmp short endit | |
| put2byte(0xe8d9); //skip: fld1 | |
| break; | |
| case POKE: //*(double *)(((long)&gvl[gasm[i].r[1]])+gvl[gasm[i].r[2]]*8) ?= gvl[gasm[i].r[3]]; break; | |
| case POKETIMES: case POKESLASH: case POKEPERC: case POKEPLUS: case POKEMINUS: | |
| put1stfld(i,gasm[i].r[2]); //fld qword ptr [?] | |
| if (cputype&(1<<27)) //SSE3 | |
| { | |
| put2byte(0xec83); put1byte(4); //sub esp, 4 | |
| put2byte(0x0cdb); put1byte(0x24); //fisttp dword ptr [esp] | |
| put1byte(0x58); //pop eax | |
| } | |
| else | |
| { | |
| //Round ninf (floor). Algo lifted from Agner Fog's optimizing_assembly.pdf | |
| put2byte(0xec83); put1byte(8); //sub esp, 8 | |
| put2byte(0x14db); put1byte(0x24); //fist dword ptr [esp] (rounded value) | |
| put2byte(0x24da); put1byte(0x24); //fisub dword ptr [esp] (sub rounded value) | |
| put4byte(0x04245cd9); //fstp dword ptr [esp+4] (diff) | |
| put1byte(0x58); //pop eax (rounded value) | |
| put1byte(0x59); //pop ecx (diff (float)) | |
| put2byte(0xc181); put4byte(0x7fffffff);//add ecx, 0x7fffffff (set CF if diff < -0) | |
| put2byte(0xd883); put1byte(0); //sbb eax, 0 (dec if x-round(x) < -0) | |
| } | |
| putsib(0xdd,0x00,rxi[gasm[i].rxi]); //fld qword ptr gasm[i].r[?3?] (3rd input parameter) | |
| //quick&dirty bounds check | |
| j = newvar[gasm[i].r[1].nv].maxind; | |
| if ((j) && (!((j-1)&j))) //if 2^x, use "and" | |
| { | |
| if (j <= 128) { put2byte(0xe083); put1byte(j-1); } //and eax, imm8 | |
| else { put1byte(0x25); put4byte(j-1); } //and eax, imm32 | |
| } | |
| else | |
| { | |
| put1byte(0x3d); put4byte(j); //cmp eax, j | |
| put2byte(0x0272); //jc short skipzeroit | |
| put2byte(0xc033); //xor eax, eax | |
| } | |
| if (((gasm[i].r[1].r&0xf0000000) == KIMM) || ((gasm[i].r[1].r&0xf0000000) == KGLB)) | |
| { | |
| if (gasm[i].f == POKEPERC) | |
| { | |
| put2byte(0x04dd); put1byte(0xc5); //fld qword ptr [eax*8+imm32] | |
| tr = gasm[i].r[1]; | |
| if (putwrite) | |
| { | |
| checkpatch(patchnum+1); | |
| patch[patchnum].lptr = (long *)&compcode[kasm87leng]; | |
| patch[patchnum].ind = tr.r; | |
| patchnum++; | |
| } | |
| put4byte(tr.q*8); | |
| put2byte(0xf8d9); //fprem | |
| put2byte(0xe4d9); //ftst | |
| put1byte(0x50); //push eax | |
| put2byte(0xe0df); //fnstsw ax | |
| put2byte(0xe480); put1byte(0x01); //and ah, 0x01 | |
| put1byte(0x58); //pop eax | |
| put1byte(0x74); put1byte(6); //jz short skip | |
| put2byte(0xc1d9); //fld st(1) | |
| put2byte(0xe1d9); //fabs | |
| put2byte(0xc1de); //faddp st(1), st | |
| put2byte(0xd9dd); //skip: fstp st(1) | |
| } | |
| else if (gasm[i].f != POKE) | |
| { | |
| switch(gasm[i].f) | |
| { | |
| case POKETIMES: put2byte(0x0cdc); put1byte(0xc5); break; //fmul qword ptr [eax*8+imm32] | |
| case POKESLASH: put2byte(0x3cdc); put1byte(0xc5); break; //fdivr qword ptr [eax*8+imm32] | |
| case POKEPLUS: put2byte(0x04dc); put1byte(0xc5); break; //fadd qword ptr [eax*8+imm32] | |
| case POKEMINUS: put2byte(0x2cdc); put1byte(0xc5); break; //fsubr qword ptr [eax*8+imm32] | |
| } | |
| tr = gasm[i].r[1]; | |
| if (putwrite) | |
| { | |
| checkpatch(patchnum+1); | |
| patch[patchnum].lptr = (long *)&compcode[kasm87leng]; | |
| patch[patchnum].ind = tr.r; | |
| patchnum++; | |
| } | |
| put4byte(tr.q*8); | |
| } | |
| put2byte(0x1cdd); put1byte(0xc5); //fstp qword ptr [eax*8+imm32] | |
| tr = gasm[i].r[1]; | |
| if (putwrite) | |
| { | |
| checkpatch(patchnum+1); | |
| patch[patchnum].lptr = (long *)&compcode[kasm87leng]; | |
| patch[patchnum].ind = tr.r; | |
| //patch[patchnum].ind = -1; | |
| //if ((tr.r&0xf0000000) == KIMM) patch[patchnum].ptr = gevalext[tr.r&0x0fffffff].ptr; | |
| // else patch[patchnum].ptr = (void *)(gstatmem + (tr.r&0x0fffffff)); | |
| patchnum++; | |
| } | |
| put4byte(tr.q*8); | |
| } | |
| else | |
| { | |
| putsib(0x8d,0x08,gasm[i].r[1]); //lea ecx, [?] | |
| //Warning: do not put any putsib calls between lea&fld because it can modify ecx | |
| if (gasm[i].f == POKEPERC) | |
| { | |
| put2byte(0x04dd); put1byte(0xc1); //fld qword ptr [eax*8+ecx] | |
| put2byte(0xf8d9); //fprem | |
| put2byte(0xe4d9); //ftst | |
| put1byte(0x50); //push eax | |
| put2byte(0xe0df); //fnstsw ax | |
| put2byte(0xe480); put1byte(0x01); //and ah, 0x01 | |
| put1byte(0x58); //pop eax | |
| put1byte(0x74); put1byte(6); //jz short skip | |
| put2byte(0xc1d9); //fld st(1) | |
| put2byte(0xe1d9); //fabs | |
| put2byte(0xc1de); //faddp st(1), st | |
| put2byte(0xd9dd); //skip: fstp st(1) | |
| } | |
| else if (gasm[i].f != POKE) | |
| { | |
| switch(gasm[i].f) | |
| { | |
| case POKETIMES: put2byte(0x0cdc); put1byte(0xc1); break; //fmul qword ptr [eax*8+ecx] | |
| case POKESLASH: put2byte(0x3cdc); put1byte(0xc1); break; //fdivr qword ptr [eax*8+ecx] | |
| case POKEPLUS: put2byte(0x04dc); put1byte(0xc1); break; //fadd qword ptr [eax*8+ecx] | |
| case POKEMINUS: put2byte(0x2cdc); put1byte(0xc1); break; //fsubr qword ptr [eax*8+ecx] | |
| } | |
| } | |
| put2byte(0x1cdd); put1byte(0xc1); //fstp qword ptr [eax*8+ecx] | |
| } | |
| break; | |
| case USERFUNC: | |
| //put1stfld(i,gasm[i].r[1]); //fld qword ptr [?] | |
| tbuf = &newvarnam[newvar[gasm[i].g].proti]; | |
| for(j=k=0;j<gasm[i].n;j++) | |
| { | |
| if (tbuf[j] == 'e') { k += (gasm[i].n-j)*8; break; } | |
| if (tbuf[j] >= 'a') k += 8; else k += 4; | |
| } | |
| put1byte(0x52); //push edx | |
| l = -((regnum<<3)+k); //add esp, i | |
| if (l >= -128) { put2byte(0xc483); put1byte(l); } else { put2byte(0xc481); put4byte(l); } | |
| got = 0; | |
| for(j=0,l=0;j<gasm[i].n;j++) | |
| { | |
| if (j < 2) tr = gasm[i].r[j+1]; else tr = rxi[gasm[i].rxi+j-2]; | |
| if (((tr.r&0xf0000000) == KESP) || ((tr.r&0xf0000000) == KPTR)) | |
| tr.r += (regnum<<3)+k+4; //hack for "add esp,i"&pushes | |
| if ((!got) && (tbuf[j] == 'e')) got = 1; | |
| if ((got) || (tbuf[j] >= 'a')) //'d' | |
| { | |
| putsib(0xdd,0x00,tr); //fld qword ptr [?] | |
| put1byte(0xdd); //fstp qword ptr [esp+l] | |
| if (!l) { put2byte(0x241c); } | |
| else if (l < 128) { put2byte(0x245c); put1byte(l); } | |
| else { put2byte(0x249c); put4byte(l); } | |
| l += 8; | |
| } | |
| else //'C','D' | |
| { | |
| if ((tr.r&0xf0000000) == KEDX) | |
| { | |
| if ((tr.r&0x0fffffff) < gccnt*8) | |
| { strcpy(kasm87err,"ERROR: pointer to constant"); return(0); } | |
| //Hack to protect EVALDRAW (passing filename string as a pointer in evalextyp) | |
| else if ((tbuf[j] == 'D') && ((tr.r&0x0fffffff) < gccnt*8+gstnum)) | |
| { strcpy(kasm87err,"ERROR: bad string"); return(0); } | |
| } | |
| if ((tr.r&0xf0000000) == KFST) tr.r = (tr.r&0x0fffffff)+k+KESP; | |
| putsib(0x8d,0x00,tr); //lea eax, qword ptr [?] | |
| put1byte(0x89); //mov dword ptr [esp+l], eax | |
| if (!l) { put2byte(0x2404); } | |
| else if (l < 128) { put2byte(0x2444); put1byte(l); } | |
| else { put2byte(0x2484); put4byte(l); } | |
| l += 4; | |
| } | |
| //printf("gasm[%2d].r[%d],%c,.r=%x+%2d,.q=%d,.nv=%d\n",i,j,tbuf[j],((unsigned)tr.r)>>28,tr.r&0x0fffffff,tr.q,tr.nv); | |
| } | |
| //push FP stack to ESP stack | |
| for(j=0;j<regnum;j++) | |
| { | |
| l = (j<<3)+k; put1byte(0xdd); //fstp qword ptr [esp+l] | |
| if (l < 128) { put2byte(0x245c); put1byte(l); } else { put2byte(0x249c); put4byte(l); } | |
| } | |
| if ((newvar[gasm[i].g].r&0xf0000000) == KESP) | |
| { | |
| tr.r = newvar[gasm[i].g].r + (regnum<<3)+k+4; | |
| putsib(0x8b,0x00,tr); //mov eax, [esp+?] //load passed user function address | |
| } | |
| else | |
| { | |
| put1byte(0xb8); | |
| if (putwrite) | |
| { | |
| checkpatch(patchnum+1); | |
| patch[patchnum].lptr = (long *)&compcode[kasm87leng]; | |
| patch[patchnum].ind = (newvar[gasm[i].g].r&0x0fffffff) + KIMM; | |
| patchnum++; | |
| } | |
| put4byte(0); //mov eax, ? //load static user function address | |
| } | |
| put2byte(0xd0ff); //call eax | |
| //pop FP stack from ESP stack | |
| for(j=regnum-1;j>=0;j--) | |
| { | |
| l = (j<<3)+k; put1byte(0xdd); //fld qword ptr [esp+l] | |
| if (l < 128) { put2byte(0x2444); put1byte(l); } else { put2byte(0x2484); put4byte(l); } | |
| } | |
| if (regnum) //move return value to top of stack (damn this is annoying! :/) | |
| { | |
| put2byte(0xc0d9+(regnum<<8)); //fld st(regnum) | |
| put2byte(0xc0dd+((regnum+1)<<8)); //ffree st(regnum+1) | |
| } | |
| l = -((regnum<<3)+k); //sub esp, l | |
| if (l >= -128) { put2byte(0xec83); put1byte(l); } else { put2byte(0xec81); put4byte(l); } | |
| put1byte(0x5a); //pop edx | |
| break; | |
| } | |
| if ((gasm[i].f != NUL) && (gasm[i].r[0].r != KUNUSED)) | |
| putsib(0xdd,0x19,gasm[i].r[0]); //fstp qword ptr [?] | |
| } | |
| //NOTE:Can't skip return if last line is GOTO: would cause jumpback hack to roll into UD1! | |
| if (gasm[gecnt-1].f != RETURN) | |
| { | |
| if (((gasm[gecnt-1].r[0].r&0xf0000000) != KIMM) && | |
| ((gasm[gecnt-1].r[0].r&0xf0000000) != KGLB) && | |
| ((gasm[gecnt-1].r[0].r&0xf0000000) != KEDX) && | |
| ((gasm[gecnt-1].r[0].r&0xf0000000) != KPTR) && (gasm[gecnt-1].r[0].r != KUNUSED) && | |
| (gasm[gecnt-1].f != NUL)) | |
| { | |
| long j = (putlen(gasm[gecnt-1].r[0])+1); | |
| if ((patchnum > 0) && (patch[patchnum-1].lptr >= (long *)&compcode[kasm87leng-j]) && | |
| (putwrite) && (patch[patchnum-1].lptr <= (long *)&compcode[kasm87leng-4])) patchnum--; | |
| kasm87leng -= j; //remove previous "fstp qword ptr [?]" | |
| } | |
| else | |
| put2byte(0xeed9); //fldz (add fake return value if last line isn't expression) | |
| //Ensure stack is empty (except for return value which is st(0)) | |
| //WARNING: fcompp trick only works properly if BOTH regs are known to be valid (not empty) | |
| //for(j=regnum;j>0;j--) put2byte(0xd9dd); //fstp st(1) | |
| for(j=regnum;j>0;j--) /*if (fpustat&(1<<(j-1)))*/ put2byte(0xc0dd+(j<<8)); //ffree st(j) (ffree faster on P4: less dependency?) | |
| if (memnum) | |
| { | |
| if ((memnum<<3)+4 <= 128) { put2byte(0xec83); put1byte(-((memnum<<3)+4)); } //sub esp, -((memnum<<3)+4) | |
| else { put2byte(0xec81); put4byte(-((memnum<<3)+4)); } //sub esp, -((memnum<<3)+4) | |
| } | |
| put1byte(0xc3); //ret | |
| } | |
| put2byte(0x0b0f); //UD1 (undefined opcode): Prevents fetcher from crashing or getting very slow! | |
| while (kasm87leng&15) put1byte(0x90); //ALIGN 16 for code blocks | |
| if (putwrite) | |
| { | |
| //Code block: | |
| *(long *)&compcode[ 0-FUNCBYTEOFFS] = 0; | |
| *(long *)&compcode[ 4-FUNCBYTEOFFS] = kasm87leng; | |
| //Data block: | |
| *(long *)&compcode[ 8-FUNCBYTEOFFS] = kasm87leng; | |
| *(long *)&compcode[12-FUNCBYTEOFFS] = gccnt*8 + gstnum + arrnum+(arrnum&7); | |
| } | |
| if ((gccnt) || (gstnum) || (arrnum)) //transplant constant table to after code | |
| { | |
| for(i=0;i<gccnt;i++) { if (putwrite) *(double *)&compcode[kasm87leng] = globval[i]; kasm87leng += 8; } | |
| //Copy string tables | |
| for(i=0;i<gstnum;i++) put1byte(gstring[i]); | |
| //Fill static variables&arrays with assigned values or 0's | |
| if (!putwrite) kasm87leng += arrnum; | |
| else | |
| { | |
| for(i=j=0;i<arrnum;i+=8,kasm87leng+=8) | |
| { | |
| if ((j < ginitvalnum) && (i == ginitval[j].i)) | |
| { *(double *)&compcode[kasm87leng] = ginitval[j].v; j++; } | |
| else *(double *)&compcode[kasm87leng] = 0; | |
| } | |
| for(i=arrnum&7;i>0;i--) put1byte(0); //Align 8 for DATA blocks (shouldn't happen unless I implement non-double types) | |
| } | |
| } | |
| if (!putwrite) | |
| { | |
| //Note: jumpback table is copied after returning to kasm87() | |
| //Reserve the space in case it's a single function script | |
| compcode = (unsigned char *)malloc(FUNCBYTEOFFS + kasm87leng + ljumpbacknum*sizeof(jumpback_t)); | |
| if (!compcode) { strcpy(kasm87err,"ERROR: malloc failed"); return(0); } | |
| compcode += FUNCBYTEOFFS; | |
| } | |
| else | |
| { | |
| //Fix jump table addresses | |
| for(i=0;i<jumpatnum;i++) | |
| (*(long *)&compcode[jumpat[i]-4]) = labpat[*(long *)&compcode[jumpat[i]-4]]-jumpat[i]; | |
| } | |
| } | |
| return(compcode); | |
| #endif | |
| } | |
| //Finds index to '(' of 1st function. -1 if simple form (no function blocks) | |
| long kasm87_findfirstfuncparen (char *st) | |
| { | |
| long i, got, bcnt, scnt, inquotes; | |
| char och; | |
| //Compact white space, remove comments, handle quotes & char constants | |
| got = 0; inquotes = 0; scnt = 0; bcnt = 0; och = 255; | |
| for(i=0;st[i];i++) | |
| { | |
| if (!inquotes) | |
| { | |
| if ((st[i] == 32) || (st[i] == 9)) continue; //strip Space/Tab | |
| if ((st[i] == 10) || (st[i] == 13)) { if (got == 1) got = 0; continue; } //strip CR,LF | |
| if ((st[i] == '/') && (st[i+1] == '/') && (!got)) got = 1; | |
| else if ((st[i] == '/') && (st[i+1] == '*') && (!got)) got = 2; | |
| else if ((st[i] == '*') && (st[i+1] == '/') && (got == 2)) { got = 0; i++; continue; } | |
| if (got) continue; | |
| } | |
| if ((st[i] == '\"') && (och != '\\')) inquotes ^= 1; | |
| if (inquotes) continue; | |
| if ((st[i] == '\'') && (st[i+1] >= 32) && (st[i+2] == '\'')) i += 2; //Skip character numbers: ' ', '$', 'a', etc... | |
| else if (st[i] == '{') bcnt++; | |
| else if (st[i] == '}') bcnt--; | |
| else if (st[i] == '[') scnt++; | |
| else if (st[i] == ']') scnt--; | |
| else if ((st[i] == '(') && (!(bcnt|scnt))) //Found first '(' not inside comment, "", {}, [] | |
| { | |
| if (isvarchar(och)) break; | |
| return(i); | |
| } | |
| och = st[i]; //cache previous char not inside comment or "". Don't want white space either. | |
| } | |
| return(-1); //Simple script | |
| } | |
| //hello how are | |
| //111111000001111000111 | |
| //hello how are | |
| static void setecurs (long i0, long i1) | |
| { | |
| long i; | |
| for(i=0;(i0 > 0) && (i < texttransn);i++) { if (texttrans[i>>5]&(1<<i)) i0--; } kasm87err0 = i; //WARNING:Uses 32-bit x86 shift trick | |
| for(i=0;(i1 > 0) && (i < texttransn);i++) { if (texttrans[i>>5]&(1<<i)) i1--; } kasm87err1 = i; //WARNING:Uses 32-bit x86 shift trick | |
| } | |
| void *kasm87 (char *bakz) | |
| { | |
| long i, j, k, l, z, oi, got, pcnt, bcnt, scnt, whitespc, funcnt, inquotes, funptr, *lptr; | |
| long codebytes, databytes, ogevalextnum, globenumcharplc, globenumnum, globnewvarplc, globnewvarnum; | |
| evalextyp *ogevalext; | |
| char ch, *tbuf, *tbufmal, *cptr; | |
| void *v; | |
| if (!cpuinited) | |
| { | |
| cpuinited = 1; cputype = getcputype(); | |
| for(i=0;i<2048;i++) | |
| { | |
| if (i < 1044) round0msk[i][0] = 0; | |
| else if (i >=1075) round0msk[i][0] = -1; | |
| else round0msk[i][0] = -(1<<(1075-i)); | |
| if (i < 1023) round0msk[i][1] = 0; | |
| else if (i >=1043) round0msk[i][1] = -1; | |
| else round0msk[i][1] = (-(1<<(1043-i)))|0xfff00000; | |
| } | |
| } | |
| kasm87err0 = -1; kasm87err1 = -1; | |
| i = strlen(bakz)+1+2; | |
| texttransn = (((i+31)>>5)<<2); //# bytes for texttrans bit buf, aligned to 32-bits | |
| if (texttransn > texttransmal) | |
| { | |
| if (texttrans) free((void *)texttrans); | |
| texttransmal = max(max(texttransn,1024),texttransmal<<1); | |
| texttrans = (long *)malloc(texttransmal); | |
| } | |
| if (!texttrans) { strcpy(kasm87err,"ERROR: malloc failed"); return(0); } | |
| memset(texttrans,0,texttransn); | |
| tbufmal = (char *)malloc(i); //2 more to store "()" if necessary | |
| if (!tbufmal) { strcpy(kasm87err,"ERROR: malloc failed"); return(0); } | |
| tbufmal[0] = '('; tbufmal[1] = ')'; tbuf = tbufmal+2; | |
| //Compact white space, remove comments, handle quotes & char constants | |
| l = 0; got = 0; whitespc = 0; inquotes = 0; | |
| for(i=0;bakz[i];i++) | |
| { | |
| if (!inquotes) | |
| { | |
| if ((bakz[i] == 32) || (bakz[i] == 9)) { whitespc = 1; continue; } //strip Space/Tab | |
| if ((bakz[i] == 10) || (bakz[i] == 13)) { whitespc = 1; if (got == 1) got = 0; continue; } //strip CR,LF | |
| if ((bakz[i] == '/') && (bakz[i+1] == '/') && (!got)) got = 1; | |
| if ((bakz[i] == '/') && (bakz[i+1] == '*') && (!got)) { whitespc = 1; got = 2; } | |
| if ((bakz[i] == '*') && (bakz[i+1] == '/') && (got == 2)) { got = 0; i++; continue; } | |
| if (got) continue; | |
| if (whitespc) | |
| { | |
| tbuf[l++] = ' '; whitespc = 0; | |
| texttrans[(i-1)>>5] |= (1<<(i-1)); //WARNING:Uses 32-bit x86 shift trick | |
| } | |
| //Skip "#opt(..)" | |
| if ((bakz[i] == '#') && (bakz[i+1] == 'o') && (bakz[i+2] == 'p') && (bakz[i+3] == 't') && (bakz[i+4] == '(')) | |
| { for(i+=5;(bakz[i]) && (bakz[i] != ')');i++); continue; } | |
| ch = bakz[i]; if ((ch >= 'a') && (ch <= 'z')) ch -= 32; | |
| } else ch = bakz[i]; | |
| tbuf[l++] = ch; | |
| texttrans[i>>5] |= (1<<i); //WARNING:Uses 32-bit x86 shift trick | |
| if ((ch == '\"') && ((l < 2) || (tbuf[l-2] != '\\'))) inquotes ^= 1; | |
| if (inquotes) continue; | |
| if (ch == '\'') //Convert character numbers: ' ', '$', 'a', etc... | |
| { | |
| if ((bakz[i+1] < 32) || (bakz[i+2] != '\'')) { sprintf(kasm87err,"ERROR: ' used wrong"); kasm87err0 = i; kasm87err1 = i+2; free(tbufmal); return(0); } | |
| j = sprintf(&tbuf[l-1],"%d",bakz[i+1]); | |
| switch (j) | |
| { | |
| case 3: texttrans[(i+2)>>5] |= (1<<(i+2)); //no break intentinoal //WARNING:Uses 32-bit x86 shift trick | |
| case 2: texttrans[(i+1)>>5] |= (1<<(i+1)); //no break intentinoal //WARNING:Uses 32-bit x86 shift trick | |
| case 1: break; | |
| } | |
| l += j-1; i += 2; continue; | |
| } | |
| } | |
| tbuf[l] = 0; | |
| //Split function definitions & global sections. Algo: | |
| // * find first '(' not inside "", {}, [] | |
| // * extract function name by searching backwards like this: !isvarchar whitespc name whitespc ( | |
| // * if char before name isn't ';', '}' or start of buffer, then: | |
| // Whole script is treated as single function; add "()" at beginning. | |
| // Note: This case only valid when finding the first function. If detected later, report an error. | |
| // * else if name is blank, then it becomes the main function: everything before it is treated as global. | |
| pcnt = 0; scnt = 0; bcnt = 0; funcnt = 0; inquotes = 0; oi = 0; got = 0; | |
| globi = 0; if (!maxfuncst) { checkfuncst(0); if (globi < 0) { free(tbufmal); return(0); } } | |
| for(i=0;i<l;i++) | |
| { | |
| ch = tbuf[i]; | |
| if ((ch == '\"') && (!((i) && (tbuf[i-1] == '\\')))) inquotes ^= 1; | |
| if (inquotes) continue; | |
| if (ch == '{') { bcnt++; continue; } | |
| if (ch == '}') { bcnt--; if (bcnt < 0) { strcpy(kasm87err,"ERROR: too many }"); /*setecurs(i-1,i+1);*/ free(tbufmal); return(0); } | |
| if (pcnt ) { strcpy(kasm87err,"ERROR: missing )"); /*setecurs(i-1,i+1);*/ free(tbufmal); return(0); } | |
| continue; } | |
| if (ch == '[') { scnt++; continue; } | |
| if (ch == ']') { scnt--; if (scnt < 0) { strcpy(kasm87err,"ERROR: too many ]"); /*setecurs(i-1,i+1);*/ free(tbufmal); return(0); } | |
| continue; } | |
| if ((ch == '(') && (!bcnt) && (!scnt)) //Found first '(' not inside "", {}, [] | |
| { | |
| checkfuncst((funcnt+2)*4); if (globi < 0) { /*setecurs(i-1,i+1);*/ free(tbufmal); return(0); } | |
| //check if it's a function. Extract function name by searching backwards like this: !isvarchar whitespc name whitespc ( | |
| k = i; | |
| if ((k > oi) && (tbuf[k-1] == ' ')) k--; //skip whitespc | |
| for(j=k;(k) && (isvarchar(tbuf[k-1]));k--); //find string: &tbuf[k<=?<j] | |
| //insert global section | |
| if (oi < k-1) { funcst[funcnt*4+0] = -1; funcst[funcnt*4+1] = 0; funcst[funcnt*4+2] = oi; funcst[funcnt*4+3] = k; funcnt++; } | |
| if (j == k) { funcst[funcnt*4+0] = i; } //no name; ltrim space | |
| else { funcst[funcnt*4+0] = k; if (!got) break;/*Hack for simple cases like "cos(3)+4"*/ } | |
| funcst[funcnt*4+1] = i; | |
| if ((k > oi) && (tbuf[k-1] == ' ')) k--; //m = char before string (skipping whitespc) | |
| if ((k > oi) && (tbuf[k-1] != ';') && (tbuf[k-1] != '}')) | |
| { | |
| if (got) { strcpy(kasm87err,"ERROR: global definition missing ; or }"); /*setecurs(i-1,i+1);*/ free(tbufmal); return(0); } | |
| break; //Simple script | |
| } | |
| got = 1; | |
| //Find ')' (end of function parameter list) | |
| for(pcnt=1,i++;i<l;i++) | |
| { | |
| if (tbuf[i] == '(') { pcnt++; continue; } | |
| if (tbuf[i] == ')') { pcnt--; if (!pcnt) { funcst[funcnt*4+2] = i+1; break; } } | |
| } | |
| if (i >= l) { strcpy(kasm87err,"ERROR: function definition missing )"); /*setecurs(i-1,i+1);*/ free(tbufmal); return(0); } | |
| //Find '}' (end of function body) | |
| i++; if (tbuf[i] == 32) i++; | |
| if (tbuf[i] != '{') { funcst[funcnt*4+3] = l; funcnt++; oi = l; break; } //Hack to support a single function without {} around body | |
| for(bcnt=1,i++;i<l;i++) | |
| { | |
| if (tbuf[i] == '{') { bcnt++; continue; } | |
| if (tbuf[i] == '}') { bcnt--; if (!bcnt) { funcst[funcnt*4+3] = i+1; oi = i+1; funcnt++; break; } } | |
| } | |
| if (i >= l) { strcpy(kasm87err,"ERROR: function missing } at end"); /*setecurs(k,i);*/ free(tbufmal); return(0); } | |
| } | |
| } | |
| //Simple script | |
| if (!got) { tbuf -= 2; l += 2; funcst[0*4+0] = 0; funcst[0*4+1] = 0; funcst[0*4+2] = 2; funcst[0*4+3] = l; funcnt = 1; } | |
| else | |
| { | |
| //insert global section | |
| if (oi < l-1) { funcst[funcnt*4+0] = -1; funcst[funcnt*4+1] = 0; funcst[funcnt*4+2] = oi; funcst[funcnt*4+3] = l; funcnt++; } | |
| } | |
| ogevalext = gevalext; ogevalextnum = gevalextnum; | |
| gevalext = (evalextyp *)malloc((gevalextnum+funcnt)*sizeof(evalextyp)); | |
| if (!gevalext) { strcpy(kasm87err,"ERROR: malloc failed"); free(tbufmal); return(0); } | |
| #if (COMPILE != 0) | |
| patchnum = 0; | |
| #endif | |
| #if 0 | |
| //funcst[?*4+{0...1....2......3}] | |
| // |func(x,y){x^2+y}| | |
| // |func(x,y)| | |
| // |(x,y){x^2+y}| | |
| for(i=0;i<funcnt;i++) | |
| { | |
| if (funcst[i*4+0] < 0) | |
| { | |
| ch = tbuf[funcst[i*4+3]]; tbuf[funcst[i*4+3]] = 0; printf("FUNC%d:glob parse sees:|%s|\n",i,&tbuf[funcst[i*4+2]]); tbuf[funcst[i*4+3]] = ch; | |
| } | |
| else | |
| { | |
| ch = tbuf[funcst[i*4+2]]; tbuf[funcst[i*4+2]] = 0; printf("FUNC%d: gevalext proto:|%s|\n",i,&tbuf[funcst[i*4+0]]); tbuf[funcst[i*4+2]] = ch; | |
| ch = tbuf[funcst[i*4+3]]; tbuf[funcst[i*4+3]] = 0; printf("FUNC%d:kasm87comp sees:|%s|\n",i,&tbuf[funcst[i*4+1]]); tbuf[funcst[i*4+3]] = ch; | |
| } | |
| } | |
| #endif | |
| globi = 0; arrnum = 0; | |
| if (!maxops) | |
| { | |
| if (oprio[0] != 255) //Initialize operator precedence LUT | |
| { | |
| memset(oprio,255,sizeof(oprio)); | |
| oprio[POW] = 0; | |
| oprio[TIMES] = oprio[SLASH] = oprio[PERC] = 1; | |
| oprio[PLUS] = oprio[MINUS] = 2; | |
| oprio[LES] = oprio[LESEQ] = oprio[MOR] = oprio[MOREQ] = 3; | |
| oprio[EQU] = oprio[NEQU] = 4; | |
| oprio[LAND] = 5; | |
| oprio[LOR] = 6; | |
| } | |
| checkops(0); checkstrings(0); checkinitvals(0); checkrxi(0); | |
| checkenum(0); checkenumchars(0); | |
| checkvars(0); checkvarchars(0); | |
| checklabs(0); checklabchars(0); | |
| checkjumpbacks(0); | |
| #if (COMPILE != 0) | |
| checkpatch(0); | |
| #endif | |
| if (globi < 0) | |
| { | |
| free(gevalext); gevalext = ogevalext; gevalextnum = ogevalextnum; free(tbufmal); | |
| strcpy(kasm87err,"ERROR: malloc failed"); return(0); | |
| } | |
| } | |
| //Init for global parse_static() -> parse_dimensions() -> kasmoptimizations() | |
| gecnt = 0; gccnt = 0; gstnum = 0; ginitvalnum = 0; | |
| //Init for parse_static() | |
| memset(newvarhash,-1,sizeof(newvarhash)); | |
| newvarnam[0] = 0; newvarplc = 0; newvarnum = 0; | |
| //parse global enum&static declarations | |
| enumcharplc = 0; enumnum = 0; | |
| for(i=j=0;i<funcnt;i++) | |
| { | |
| if (funcst[i*4+0] < 0) | |
| { | |
| ch = tbuf[funcst[i*4+3]]; tbuf[funcst[i*4+3]] = 0; | |
| for(z=funcst[i*4+2];tbuf[z];z++) | |
| { | |
| if ((!strncmp(&tbuf[z],"ENUM" ,4)) && (!isvarchar(tbuf[z+4]))) z = parse_enum(tbuf,z); | |
| else if ((!strncmp(&tbuf[z],"STATIC",6)) && (!isvarchar(tbuf[z+6]))) z = parse_static(tbuf,z); | |
| else continue; | |
| if (globi < 0) { free(gevalext); gevalext = ogevalext; gevalextnum = ogevalextnum; free(tbufmal); return(0); } | |
| if (!tbuf[z]) break; | |
| } | |
| tbuf[funcst[i*4+3]] = ch; | |
| } | |
| else | |
| { | |
| funcst[j*4+0] = funcst[i*4+0]; | |
| funcst[j*4+1] = funcst[i*4+1]; | |
| funcst[j*4+2] = funcst[i*4+2]; | |
| funcst[j*4+3] = funcst[i*4+3]; | |
| j++; | |
| } | |
| } | |
| funcnt = j; | |
| globenumnum = enumnum; globenumcharplc = enumcharplc; | |
| memcpy(newvarhash_glob,newvarhash,sizeof(newvarhash)); | |
| globnewvarplc = newvarplc; globnewvarnum = newvarnum; | |
| if (arrnum) //allocate gstatmem block & copy ginitval to it before kasm87comp destroys ginitval | |
| { | |
| gstatmem = (long)malloc(arrnum); | |
| if (!gstatmem) | |
| { | |
| free(gevalext); gevalext = ogevalext; gevalextnum = ogevalextnum; free(tbufmal); | |
| strcpy(kasm87err,"ERROR: malloc failed"); return(0); | |
| } | |
| //Fill gstatmem with assigned values or 0's | |
| k = (long)gstatmem; | |
| for(i=j=0;i<arrnum;i+=8,k+=8) | |
| { | |
| if ((j < ginitvalnum) && (i == ginitval[j].i)) | |
| { *(double *)k = ginitval[j].v; j++; } | |
| else *(double *)k = 0; | |
| } | |
| } else gstatmem = 0; | |
| for(i=0;i<globnewvarnum;i++) | |
| if ((newvar[i].r&0xf0000000) == KARR) | |
| newvar[i].r = (newvar[i].r&0x0fffffff)+KGLB; | |
| jumpbacknum = 0; | |
| memcpy(gevalext,ogevalext,ogevalextnum*sizeof(evalextyp)); | |
| gevalextnum += funcnt-1; | |
| //Main can't be called since it doesn't have a name | |
| for(i=funcnt-1;i>0;i--) | |
| { | |
| j = gevalextnum-i; | |
| ch = tbuf[funcst[i*4+2]]; tbuf[funcst[i*4+2]] = 0; | |
| gevalext[j].nam = strdup(&tbuf[funcst[i*4+0]]); | |
| if (!gevalext[j].nam) | |
| { | |
| for(j--;j>=ogevalextnum;j--) free(gevalext[j].nam); | |
| if (gstatmem) { free((void *)gstatmem); gstatmem = 0; } | |
| free(gevalext); gevalext = ogevalext; gevalextnum = ogevalextnum; | |
| free(tbufmal); strcpy(kasm87err,"ERROR: strdup failed"); return(0); | |
| } | |
| //printf("ext_proto:|%s|\n",gevalext[j].nam); | |
| tbuf[funcst[i*4+2]] = ch; | |
| } | |
| codebytes = databytes = 0; | |
| for(i=funcnt-1;i>=0;i--) | |
| { | |
| j = gevalextnum-i; | |
| enumnum = globenumnum; enumcharplc = globenumcharplc; | |
| memcpy(newvarhash,newvarhash_glob,sizeof(newvarhash)); | |
| newvarplc = globnewvarplc; newvarnum = globnewvarnum; | |
| tbuf[funcst[i*4+3]] = 0; | |
| //printf("compiling:|%s|\n",&tbuf[funcst[i*4+1]]); | |
| gevalext[j].ptr = (EVALFUNC)kasm87comp(&tbuf[funcst[i*4+1]]); | |
| #if 0 | |
| { //DEBUG ONLY! | |
| char debuf[65536]; | |
| extern char *kdisasm (char *, long, char *, long, long *); | |
| kasm87_showdebug(1,debuf,sizeof(debuf)); printf("\n\n%d:\n%s",i,debuf); | |
| debuf[0] = debuf[sizeof(debuf)-1] = 0; | |
| l = 0; kdisasm((char *)compcode,kasm87leng,debuf,sizeof(debuf)-1,&l); | |
| printf("\n%s",debuf); | |
| } | |
| #endif | |
| if (!gevalext[j].ptr) | |
| { | |
| for(j--;j>=ogevalextnum;j--) free((void *)(((long)gevalext[j].ptr)-FUNCBYTEOFFS)); | |
| for(j=gevalextnum-1;j>=ogevalextnum;j--) free(gevalext[j].nam); | |
| if (gstatmem) { free((void *)gstatmem); gstatmem = 0; } | |
| free(gevalext); gevalext = ogevalext; gevalextnum = ogevalextnum; | |
| free(tbufmal); return(0); | |
| } | |
| funptr = (long)gevalext[j].ptr; lptr = (long *)(funptr-FUNCBYTEOFFS); | |
| codebytes += lptr[1]; | |
| databytes += lptr[3]; | |
| } | |
| v = (void *)gevalext[gevalextnum].ptr; | |
| #if (COMPILE != 0) | |
| //merge all functions together | |
| kasm87leng = codebytes + databytes; if (databytes) kasm87leng += CODEDATADIST; | |
| v = malloc(FUNCBYTEOFFS + kasm87leng + jumpbacknum*sizeof(jumpback_t)); | |
| if (!v) | |
| { | |
| for(i=0;i<funcnt;i++) { j = gevalextnum-i; free((void *)(((long)gevalext[j].ptr)-FUNCBYTEOFFS)); } | |
| for(j=gevalextnum-1;j>=ogevalextnum;j--) free(gevalext[j].nam); | |
| if (gstatmem) { free((void *)gstatmem); gstatmem = 0; } | |
| free(gevalext); gevalext = ogevalext; gevalextnum = ogevalextnum; | |
| free(tbufmal); return(0); | |
| } | |
| v = (void *)(((long)v)+FUNCBYTEOFFS); | |
| cptr = (char *)v; l = patchnum-1; k = jumpbacknum-1; | |
| for(i=0;i<funcnt;i++) | |
| { | |
| j = gevalextnum-i; funptr = (long)gevalext[j].ptr; lptr = (long *)(funptr-FUNCBYTEOFFS); if (!lptr[1]) continue; | |
| //Relocate code | |
| memcpy(cptr,(void *)funptr,lptr[1]); | |
| lptr[0] = ((long)cptr); //lptr[0] now holds code pointer so data pointer can be plugged in later | |
| //Relocate patch pointers | |
| while ((l >= 0) && (((unsigned long)patch[l].lptr)-((unsigned long)funptr) < (unsigned long)lptr[1])) | |
| { patch[l].lptr = (long *)(((long)patch[l].lptr) + ((long)cptr) - funptr); l--; } | |
| //Relocate jumpback list | |
| while ((k >= 0) && (((unsigned long)jumpback[k].addr)-((unsigned long)funptr) < (unsigned long)lptr[1])) | |
| { jumpback[k].addr += ((long)cptr) - funptr; k--; } | |
| cptr += lptr[1]; | |
| } | |
| if (databytes) { memset(cptr,0x90,CODEDATADIST); cptr += CODEDATADIST; } | |
| for(i=0;i<funcnt;i++) | |
| { | |
| j = gevalextnum-i; funptr = (long)gevalext[j].ptr; lptr = (long *)(funptr-FUNCBYTEOFFS); if (!lptr[3]) continue; | |
| //Relocate data | |
| memcpy(cptr,(void *)(funptr+lptr[2]),lptr[3]); | |
| //Relocate consts&array (KEDX) | |
| if (*(unsigned char *)(lptr[0]) == 0xba) *(long *)(lptr[0]+1) = ((long)cptr); //Note: condition should always be true | |
| cptr += lptr[3]; | |
| } | |
| for(i=0;i<funcnt;i++) | |
| { | |
| j = gevalextnum-i; funptr = (long)gevalext[j].ptr; lptr = (long *)(funptr-FUNCBYTEOFFS); | |
| gevalext[j].ptr = (long *)lptr[0]; //Put the new function pointer on global list | |
| free((void *)lptr); //..and free the original function which was just copied | |
| } | |
| for(j=patchnum-1;j>=0;j--) | |
| { | |
| if ((patch[j].ind&0xf0000000) == KIMM) patch[j].lptr[0] += ((long)gevalext[patch[j].ind&0x0fffffff].ptr); | |
| else patch[j].lptr[0] += gstatmem + (patch[j].ind&0x0fffffff); | |
| } | |
| for(j=jumpbacknum-1;j>=0;j--) jumpback[j].val = *(long *)(jumpback[j].addr); //backup original jumpback values | |
| compcode = (unsigned char *)v; //make sure kasm87_showdebug has a valid pointer | |
| #else | |
| ??? not implemented .. need to fix .. sorry :/ | |
| #endif | |
| //Copy jumpback table to script's malloced array so kasm87jumpback() can support multiple scripts | |
| *(long *)(((long)v)-FUNCBYTEOFFS) = jumpbacknum; | |
| *(long *)(((long)v)-FUNCBYTEOFFS+4) = kasm87leng; | |
| *(long *)(((long)v)-FUNCBYTEOFFS+8) = gstatmem; | |
| memcpy((void *)(((long)v)+kasm87leng),jumpback,jumpbacknum*sizeof(jumpback_t)); | |
| for(j=gevalextnum-1;j>=ogevalextnum;j--) free(gevalext[j].nam); | |
| free(gevalext); gevalext = ogevalext; gevalextnum = ogevalextnum; | |
| free(tbufmal); return(v); | |
| } | |
| //------------------------------------------ KASM87 ENDS ------------------------------------------ | |
| #ifdef EVALTEST | |
| #if defined(_MSC_VER) && defined(_M_IX86) | |
| static __forceinline __int64 rdtsc64 () { _asm rdtsc } | |
| #elif defined(__GNUC__) && defined(__i386__) | |
| static _inline __int64 rdtsc64 () | |
| { | |
| unsigned long long q; | |
| __asm__ __volatile__ ("rdtsc\n" : "=A" (q) : : "memory"); | |
| return(q); | |
| } | |
| #elif defined(powerc) || defined(__POWERC__) || defined(__ppc__) || defined(ppc) | |
| static _inline __int64 rdtsc64 () | |
| { | |
| //register unsigned long t; __asm__ __volatile__ ("mftb %0":"=r"(t)); return((__int64)t); //32-bit only | |
| //Code from: http://ozlabs.org/pipermail/linuxppc-dev/1999-October/003889.html | |
| //TimeBase resolution is 41.5Mhz on a 1.412Mhz MacMini | |
| unsigned long long q; | |
| unsigned long t; | |
| __asm__ __volatile__ ("\n\ | |
| 1: mftbu %1\n\ | |
| mftb %L0\n\ | |
| mftbu %0\n\ | |
| cmpw %0,%1\n\ | |
| bne 1b" | |
| : "=r" (q), "=r" (t)); | |
| return(q); | |
| } | |
| #else | |
| static __int64 rdtsc64 () { return(LL(0)); } | |
| #endif | |
| //This function is useful for debugging FP stack overflows/underflows/leaks | |
| void dumpfp () | |
| { | |
| #ifdef _MSC_VER | |
| long i, j, fpreg[8]; | |
| _asm fnstenv fpreg | |
| if ((fpreg[1]&64) || ((fpreg[2]&0xffff) != 0xffff)) | |
| { | |
| printf("\n\nERROR: fp stack corrupt! (st() regs should be: ---)"); | |
| j = (fpreg[1]>>11)&7; | |
| printf("\n "); for(i=0;i<8;i++) printf("st%d ",(i-j)&7); | |
| printf("\nTag: "); | |
| for(i=0;i<8;i++) | |
| { | |
| switch((fpreg[2]>>((i&7)<<1))&3) | |
| { | |
| case 0: printf(" # "); break; | |
| case 1: printf(" 0 "); break; | |
| case 2: printf("NaN "); break; | |
| case 3: printf("--- "); break; | |
| } | |
| } | |
| if (fpreg[1]&64) | |
| { | |
| if (fpreg[1]&512) printf("\nFP Stack overflow: too many fld!"); | |
| else printf("\nFP Stack underflow: too many fst!"); | |
| } | |
| _asm //This code ruins "sticky" FP exception flag, so make sure it's after fnstenv | |
| { | |
| mov eax, 28 | |
| begfpstk:fdecstp | |
| fst dword ptr fpreg[eax] | |
| sub eax, 4 | |
| jge short begfpstk | |
| fninit | |
| } | |
| for(i=0;i<8;i++) printf("\nst(%d) = 0x%08x, %f",i,fpreg[i],*(float *)&fpreg[i]); | |
| printf("\n\n"); | |
| } | |
| #endif | |
| } | |
| static char debuf[16384]; | |
| #if 1 | |
| #include "kdisasm.c" | |
| void showasm () | |
| { | |
| long i; | |
| debuf[0] = debuf[sizeof(debuf)-1] = 0; | |
| i = 0; kdisasm((char *)compcode,kasm87leng,debuf,sizeof(debuf)-1,&i); | |
| printf("\n%s",debuf); | |
| } | |
| #endif | |
| //NOTE: Non-VC compilers only support up to 3 params - else crash. | |
| void testcode (char *st, double *v, long vnum) | |
| { | |
| //double (__cdecl *fptr)(double, ...); | |
| EVALFUNC fptr; | |
| double d; | |
| __int64 q0, q1, q2; | |
| long i, j, k; | |
| printf("%s =\n",st); | |
| ksrand(17); snormstat = 0; | |
| //fptr = (double (__cdecl *)(double, ...))kasm87(st); | |
| fptr = (EVALFUNC)kasm87(st); | |
| if (fptr) { kasm87_showdebug(3,debuf,sizeof(debuf)); printf("%s",debuf); } | |
| else { puts(kasm87err); return; } | |
| printf("value:"); | |
| #ifndef _MSC_VER | |
| d = fptr(v[0],v[1],v[2]); | |
| #else | |
| _asm | |
| { | |
| mov eax, vnum | |
| mov edx, v | |
| beg: push dword ptr [edx+eax*8-4] | |
| push dword ptr [edx+eax*8-8] | |
| sub eax, 1 | |
| jg short beg | |
| call dword ptr fptr | |
| mov eax, vnum | |
| lea esp, [esp+eax*8] | |
| fstp qword ptr d | |
| } | |
| #endif | |
| printf(" %.25g",d); | |
| dumpfp(); | |
| q2 = LL(0x7fffffffffffffff); | |
| for(j=16;j;j--) | |
| { | |
| q0 = rdtsc64(); | |
| for(i=256;i;i--) | |
| { | |
| #ifndef _MSC_VER | |
| fptr(v[0],v[1],v[2]); | |
| #else | |
| _asm | |
| { | |
| mov eax, vnum | |
| mov edx, v | |
| beg2: push dword ptr [edx+eax*8-4] | |
| push dword ptr [edx+eax*8-8] | |
| sub eax, 1 | |
| jg short beg2 | |
| call dword ptr fptr | |
| mov eax, vnum | |
| lea esp, [esp+eax*8] | |
| fstp qword ptr d | |
| } | |
| #endif | |
| } | |
| q1 = rdtsc64(); | |
| if (q1-q0 < q2) q2 = q1-q0; | |
| } | |
| printf(" %10" PRINTF64 " cc\n",q2>>8); | |
| kasm87free((void *)fptr); | |
| } | |
| static double pilut[] = {3,1,4,1,5,9,2,6,5,3,5,8,9,7,9,3,2,3,8,4,6,2,6,4}; | |
| double __cdecl getdigpi (double d) | |
| { | |
| if ((d < 0) || (d >= (sizeof(pilut)/sizeof(pilut[0])))) return(0.0); | |
| return(pilut[(long)d]); | |
| } | |
| static double goldratlut[] = {1,6,1,8,0,3,3,9,8,8,7,4,9,8,9,5}; | |
| double __cdecl getdiggoldrat (double d) | |
| { | |
| if ((d < 0) || (d >= (sizeof(goldratlut)/sizeof(goldratlut[0])))) return(0.0); | |
| return(goldratlut[(long)d]); | |
| } | |
| double __cdecl getangle360 (double x, double y) | |
| { | |
| double d; | |
| d = atan2(y,x)*180/PI; | |
| if (d < 0) d += 360; | |
| return(d); | |
| } | |
| double __cdecl func4d (double a, double b, double c, double d) { return(((a*10+b)*10+c)*10+d); } | |
| double __cdecl sndfunc (double x) | |
| { | |
| return(x); | |
| } | |
| double __cdecl mysrand (double val) { kholdrand = (long)val; return(0); } | |
| double __cdecl printnum (double n) { printf("%g ",n); return(0); } | |
| static double buf8[8], buf11[11], buf3_2[3][2] = {2,3,5,7,11,13}; | |
| //C += A x B | |
| double __cdecl crossprod (double Ax, double Ay, double Az, | |
| double Bx, double By, double Bz, | |
| double *Cx, double *Cy, double *Cz) | |
| { | |
| (*Cx) += Ay*Bz - Az*By; | |
| (*Cy) += Az*Bx - Ax*Bz; | |
| (*Cz) += Ax*By - Ay*Bx; | |
| return(0); | |
| } | |
| double __cdecl printst (double d, char *st) | |
| { | |
| long i; | |
| for(i=(long)d;i>0;i--) printf("\n|%s|",st); | |
| return(0); | |
| } | |
| int main (int argc, char **argv) | |
| { | |
| double v[256]; | |
| long i, j, nparams; | |
| memset(v,0,sizeof(v)); | |
| if (argc >= 2) | |
| { | |
| for(i=argc-2-1;i>=0;i--) v[i] = atof(argv[i+2]); | |
| testcode(argv[1],v,argc-2); | |
| return(0); | |
| } | |
| v[0] = 0.5; v[1] = 0.8; testcode("(x,y)cos(max(x,y)*PI)^2+sin(max(y,x)*PI)^2",v,2); //=1.0! | |
| puts(""); | |
| v[0] = 2.0/3.0; testcode("(x)y=PI/2*x;z=y^2;(z/fact(5)-1/fact(3))*z*y+y",v,1); //~fcos | |
| puts(""); | |
| //Simple example #1: Celsius to Fahrenheit converter | |
| { | |
| //double (__cdecl *c2f)(double, ...) = (double (__cdecl *)(double, ...))kasm87("(x)(x*(9/5))+32"); | |
| EVALFUNC c2f = (EVALFUNC)kasm87("(x)(x*(9/5))+32"); | |
| if (c2f) | |
| { | |
| printf("%gøC = %gøF\n",20.0,c2f(20.0)); | |
| kasm87free(c2f); | |
| } | |
| } | |
| //Simple example #2: Hypotenuse calculator | |
| { | |
| EVALFUNC hypot = (EVALFUNC)kasm87("(cat,dog)cat*=cat;bozo=dog^2;sqrt(cat+bozo)"); | |
| if (hypot) | |
| { | |
| #ifndef _MSC_VER | |
| printf("hypot(%g,%g) = %g\n",3.0,4.0,hypot(3.0,4.0)); | |
| #else | |
| v[0] = 3.0; v[1] = 4.0; //v[2] = hypot(v[0],v[1]); | |
| _asm //Show how to call kasm87 code in ASM | |
| { | |
| push dword ptr [v+12] | |
| push dword ptr [v+8] | |
| push dword ptr [v+4] | |
| push dword ptr [v] | |
| call dword ptr [hypot] | |
| fstp qword ptr [v+16] | |
| add esp, 16 | |
| } | |
| printf("hypot(%g,%g) = %g\n",v[0],v[1],v[2]); | |
| #endif | |
| kasm87free(hypot); | |
| } | |
| } | |
| //Example #3: Passing user functions by pointer | |
| { | |
| EVALFUNC passfunc = (EVALFUNC)kasm87("(x,pifunc())pifunc(x)*10+pifunc(x+1)"); | |
| if (passfunc) | |
| { | |
| double d; | |
| kasm87_showdebug(1,debuf,sizeof(debuf)); printf("\n%s",debuf); | |
| for(d=.5;d<5;d++) { printf("sillypifunc(%g) = %g\n",d,passfunc(d,getdigpi)); } | |
| kasm87free(passfunc); | |
| } else puts(kasm87err); | |
| } | |
| //Example #4: Passing 2 user functions by pointer | |
| { | |
| EVALFUNC passfunc = (EVALFUNC)kasm87("(x,goldfunc(),pifunc())pifunc(x)*1000+goldfunc(x)*100+pifunc(x)*10+goldfunc(x)"); | |
| if (passfunc) | |
| { | |
| double d; | |
| kasm87_showdebug(1,debuf,sizeof(debuf)); printf("\n%s",debuf); | |
| for(d=.5;d<5;d++) { printf("sillydualfunc(%g) = %g\n",d,passfunc(d,getdiggoldrat,getdigpi)); } | |
| kasm87free(passfunc); | |
| } else puts(kasm87err); | |
| } | |
| //Example #5: Passing user functions with 2 variables by pointer | |
| { | |
| EVALFUNC passfunc = (EVALFUNC)kasm87("(x,y,ang2vec(,))ang2vec(x,y)"); | |
| if (passfunc) | |
| { | |
| double x, y; | |
| puts(""); | |
| for(i=1;i>=-1;i-=2) | |
| { | |
| x = 1; y = (double)i; | |
| printf("dumbanglefunc(%g,%g) = %g (should be %g)\n",x,y,passfunc(x,y,getangle360),getangle360(x,y)); | |
| } | |
| kasm87free(passfunc); | |
| } else puts(kasm87err); | |
| } | |
| //Example #6: Passing variables by pointer | |
| { | |
| EVALFUNC passfunc = (EVALFUNC)kasm87("(ang,&x,&y)ang*=PI/180;x=cos(ang);y=sin(ang);"); | |
| if (passfunc) | |
| { | |
| double a, x, y; | |
| kasm87_showdebug(1,debuf,sizeof(debuf)); printf("\n%s",debuf); | |
| a = 30; x = y = -17.0; passfunc(a,&x,&y); printf("getunitvector(%g) = %g,%g\n",a,x,y); | |
| printf(" [Should be: %g,%g]\n",cos(a*PI/180),sin(a*PI/180)); | |
| kasm87free(passfunc); | |
| } else puts(kasm87err); | |
| } | |
| //Example #7: More with pointers | |
| { | |
| EVALFUNC passfunc = (EVALFUNC)kasm87("(x,y,&r,&g,&b)r=x+y;g=x*y;b=x/y;"); | |
| if (passfunc) | |
| { | |
| double x, y, r, g, b; | |
| kasm87_showdebug(1,debuf,sizeof(debuf)); printf("\n%s",debuf); | |
| x = 4; y = 3; r = g = b = -17.0; passfunc(x,y,&r,&g,&b); printf("getcol(%g,%g) = %g,%g,%g\n",x,y,r,g,b); | |
| printf(" [Should be: %g,%g,%g]\n",x+y,x*y,x/y); | |
| kasm87free(passfunc); | |
| } else puts(kasm87err); | |
| } | |
| //Example #8: Passing arrays by pointers (indices must be hardcoded) | |
| { | |
| EVALFUNCP passfunc = (EVALFUNCP)kasm87("(a[3])a[0]+=a[1];a[1]+=a[2];"); | |
| if (passfunc) | |
| { | |
| double a[3]; | |
| kasm87_showdebug(1,debuf,sizeof(debuf)); printf("\n%s",debuf); | |
| a[0] = 1; a[1] = 2; a[2] = 3; | |
| printf("%g,%g,%g <-Should be: 1 2 3\n",a[0],a[1],a[2]); passfunc(a); | |
| printf("%g,%g,%g <- 3 5 3\n",a[0],a[1],a[2]); passfunc(a); | |
| printf("%g,%g,%g <- 8 8 3\n",a[0],a[1],a[2]); | |
| kasm87free(passfunc); | |
| } else puts(kasm87err); | |
| } | |
| //Example #9: Compile with external library | |
| { | |
| double d; | |
| evalextyp myext[] = | |
| { | |
| {"LOCVAR" ,&d }, | |
| {"PILUT[24]" ,pilut }, | |
| {"GETDIGPI()" ,getdigpi }, | |
| {"GETANG(,)" ,getangle360}, | |
| {"FUNC4D(,,,)",func4d }, | |
| }; | |
| kasm87addext(myext,sizeof(myext)/sizeof(myext[0])); | |
| { | |
| EVALFUNC libfunc = (EVALFUNC)kasm87("(x)func4d(pilut[0],getdigpi(1),getdigpi(2),pilut[3])+x*10000"); | |
| if (libfunc) | |
| { | |
| puts(""); | |
| kasm87_showdebug(1,debuf,sizeof(debuf)); printf("\n%s",debuf); | |
| for(i=0;i<5;i++) | |
| { | |
| d = ((double)(((rand()&32767)*10)>>15)); | |
| printf("%g: = ",d); | |
| printf("%5g ",libfunc(d)); | |
| printf("\n"); | |
| } | |
| //puts(""); for(d=0;d<(sizeof(pilut)/sizeof(pilut[0]));d++) printf("%g",libfunc(d)); | |
| kasm87free(libfunc); | |
| } else puts(kasm87err); | |
| } | |
| kasm87addext(0,0); | |
| } | |
| #if 0 | |
| //Example #10: Extended variable names in quotes (put any "snd0.wav"&"snd1.wav" in directory) | |
| { | |
| double d; | |
| evalextyp myext[] = | |
| { | |
| {"\"snd0.wav\"()",sndfunc}, | |
| {"\"snd1.wav\"()",sndfunc}, | |
| }; | |
| kasm87addext(myext,sizeof(myext)/sizeof(myext[0])); | |
| { | |
| EVALFUNC libfunc = (EVALFUNC)kasm87("(x)\"snd0.wav\"(x)+\"snd1.wav\"(x)"); | |
| if (libfunc) | |
| { | |
| kasm87_showdebug(1,debuf,sizeof(debuf)); printf("\n%s",debuf); | |
| for(i=0;i<5;i++) | |
| { | |
| d = ((double)(((rand()&32767)*10)>>15)); | |
| printf("%g: = ",d); | |
| printf("%5g ",libfunc(d)); | |
| printf("\n"); | |
| } | |
| kasm87free(libfunc); | |
| } else puts(kasm87err); | |
| } | |
| kasm87addext(0,0); | |
| } | |
| #endif | |
| //Example #11: Lookup table | |
| { | |
| double d, crc32[256]; | |
| long j, k; | |
| for(i=255;i>=0;i--) | |
| { | |
| k = i; for(j=8;j;j--) k = ((unsigned long)k>>1)^((-(k&1))&0xedb88320); | |
| crc32[i] = (double)k; | |
| } | |
| { | |
| EVALFUNC lutfunc = (EVALFUNC)kasm87("(x,crc32[256])crc32[x]"); | |
| if (lutfunc) | |
| { | |
| kasm87_showdebug(1,debuf,sizeof(debuf)); printf("\n%s",debuf); | |
| for(i=0;i<256;i+=85) | |
| { | |
| printf("crc32[%3d]: ",i); | |
| printf("%11.1f ",lutfunc((double)i,crc32)); | |
| printf("%10d\n",(long)crc32[i]); | |
| } | |
| kasm87free(lutfunc); | |
| } else puts(kasm87err); | |
| } | |
| } | |
| //Example #12: Write buffer | |
| { | |
| double d, sq[8]; | |
| { | |
| //EVALFUNC lutfunc = (EVALFUNC)kasm87("(sc,buf[8])i=7;do{buf[i]=i*i*sc;i--;}while(i>=0);0"); | |
| EVALFUNC lutfunc = (EVALFUNC)kasm87("(sc,buf[8])for(i=0;i<8;i++)buf[i]=i*i*sc;0"); | |
| if (lutfunc) | |
| { | |
| kasm87_showdebug(1,debuf,sizeof(debuf)); printf("\n%s",debuf); | |
| memset(sq,0,sizeof(double)*8); lutfunc(3.0,sq); for(i=0;i<8;i++) printf("%g ",sq[i]); printf("\n"); | |
| printf("0 3 12 27 48 75 108 147 <- Should be\n"); | |
| kasm87free(lutfunc); | |
| } else puts(kasm87err); | |
| } | |
| } | |
| //Example #13: User static buffer | |
| { | |
| double d; | |
| evalextyp myext[] = {"PRINTNUM()",printnum,"SRAND()",mysrand}; | |
| kasm87addext(myext,sizeof(myext)/sizeof(myext[0])); | |
| { | |
| EVALFUNC arrfunc = (EVALFUNC)kasm87( | |
| "(dum) static buf[28]; srand(0);" | |
| "for(i=0;i<28;i++) buf[i] = i;" | |
| "for(i=28;i>1;i--) { j = int(i*rnd); z = buf[j]; buf[j] = buf[i-1]; buf[i-1] = z; }" | |
| "for(i=0;i<28;i++) printnum(buf[i]);" | |
| "0"); | |
| if (arrfunc) | |
| { | |
| printf("\n"); | |
| //kasm87_showdebug(1,debuf,sizeof(debuf)); printf("\n%s",debuf); | |
| arrfunc(0.0); printf("\n"); | |
| kasm87free(arrfunc); | |
| } else puts(kasm87err); | |
| } | |
| } | |
| //Example #14: Test behavior of array index out of bounds | |
| { | |
| evalextyp myext[] = {"BUF8[8]",buf8,"BUF11[11]",buf11}; | |
| kasm87addext(myext,sizeof(myext)/sizeof(myext[0])); | |
| { | |
| EVALFUNC boundtst = (EVALFUNC)kasm87("(dum) for(i=0;i<13;i++) { buf8[i] = i; buf11[i] = i; } 0"); | |
| if (boundtst) | |
| { | |
| printf("\n"); | |
| //kasm87_showdebug(1,debuf,sizeof(debuf)); printf("\n%s",debuf); | |
| for(i=0;i<8;i++) buf8[i] = -1.0; | |
| for(i=0;i<11;i++) buf11[i] = -1.0; | |
| boundtst(0.0); | |
| for(i=0;i<8;i++) printf("%g,",buf8[i]); printf(" "); | |
| for(i=0;i<11;i++) printf("%g,",buf11[i]); printf("\n"); | |
| printf("8,9,10,11,12,5,6,7, 12,1,2,3,4,5,6,7,8,9,10 <- Should be\n"); | |
| kasm87free(boundtst); | |
| } else puts(kasm87err); | |
| } | |
| kasm87addext(0,0); | |
| } | |
| //Example #15: External function passing by pointer | |
| { | |
| evalextyp myext[] = | |
| { | |
| {"CROSSPROD(,,,,,,&,&,&)",crossprod}, | |
| }; | |
| kasm87addext(myext,sizeof(myext)/sizeof(myext[0])); | |
| { | |
| EVALFUNCP makevec = (EVALFUNCP)kasm87("(&x,&y,&z)crossprod(y,z,x,z,x,y,&x,&y,&z);x*=10;y*=10;z*=10;"); | |
| if (makevec) | |
| { | |
| puts(""); | |
| kasm87_showdebug(1,debuf,sizeof(debuf)); printf("\n%s",debuf); | |
| v[0] = 2; v[1] = 3; v[2] = 5; makevec(&v[0],&v[1],&v[2]); | |
| printf("Should be:[130,40,-140]\n"); | |
| printf("Result is: %g,%g,%g\n",v[0],v[1],v[2]); | |
| kasm87free(makevec); | |
| } else puts(kasm87err); | |
| } | |
| kasm87addext(0,0); | |
| } | |
| //Example #16: Eval function calling another Eval function | |
| { | |
| EVALFUNC rot2d, doubrot; | |
| rot2d = (EVALFUNC)kasm87("(a,&x,&y)a*=PI/180;c=cos(a);s=sin(a);ox=x;x=x*c-y*s;y=y*c+ox*s;"); | |
| if (rot2d) | |
| { | |
| evalextyp myext[] = {{"ROT2D(,&,&)",rot2d}}; | |
| //kasm87_showdebug(1,debuf,sizeof(debuf)); printf("\n%s",debuf); | |
| kasm87addext(myext,sizeof(myext)/sizeof(myext[0])); | |
| doubrot = (EVALFUNC)kasm87("(x,y,&x2,&y2)x2=x;y2=y;rot2d(45,&x2,&y2);x2++;rot2d(-45,&x2,&y2);"); | |
| if (doubrot) | |
| { | |
| //kasm87_showdebug(1,debuf,sizeof(debuf)); printf("\n%s",debuf); | |
| doubrot(1.0,0.0,&v[0],&v[1]); printf("\nShould be:[1.70711,-0.707107]\nResult is: %g,%g\n",v[0],v[1]); | |
| } else puts(kasm87err); | |
| } else puts(kasm87err); | |
| kasm87free(doubrot); | |
| kasm87free(rot2d); | |
| kasm87addext(0,0); | |
| } | |
| //Example #17: Eval function calling another Eval function | |
| { | |
| EVALFUNC cubesum = (EVALFUNC)kasm87("(x,y) { return(cube(x)+cube(y)); } cube(x) { return(x*x*x); }"); | |
| if (cubesum) { printf("\nShould be:[35]\nResult is: %g\n",cubesum(2.0,3.0)); } else puts(kasm87err); | |
| kasm87free(cubesum); | |
| kasm87addext(0,0); | |
| } | |
| //Example #18: Passing string pointer (07/18/2006) | |
| { | |
| evalextyp myext[] = {{"PRINTST(,$)",printst}}; | |
| kasm87addext(myext,sizeof(myext)/sizeof(myext[0])); | |
| { | |
| EVALFUNC printme = (EVALFUNC)kasm87("(d,$st)printst(d+1,st);printst(d-1,\"[It, works! ( : }\");"); | |
| //kasm87_showdebug(1,debuf,sizeof(debuf)); printf("\n%s",debuf); | |
| //showasm(); | |
| if (printme) printme(3,"Hello :)"); else puts(kasm87err); | |
| kasm87free(printme); | |
| } | |
| kasm87addext(0,0); | |
| } | |
| //Example #19: Passing multidimensional array (03/01/2007) | |
| { | |
| evalextyp myext[] = {{"BUF[3][2]",buf3_2}}; | |
| kasm87addext(myext,sizeof(myext)/sizeof(myext[0])); | |
| { | |
| EVALFUNC multidim = (EVALFUNC)kasm87("(x)buf[3]+buf[1][0]+buf[2][1]+x;"); | |
| //kasm87_showdebug(1,debuf,sizeof(debuf)); printf("\n%s",debuf); | |
| //showasm(); | |
| if (multidim) printf("\n\nEx.19: Value is:%g\n [Should be:28.4]",multidim(3.4)); else puts(kasm87err); | |
| kasm87free(multidim); | |
| } | |
| kasm87addext(0,0); | |
| } | |
| } | |
| #endif | |
| #if 0 | |
| !endif | |
| #endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment