/* bool: argindex(index, value[], maxlength=sizeof value, bool:pack=false) * returns true if the option was found and false on error or if the parameter "index" is out of range */ static cell AMX_NATIVE_CALL n_argindex(AMX *amx, const cell *params) { const TCHAR *cmdline = rawcmdline(); const TCHAR *option; int length, max; TCHAR *str; cell *cptr; max = (int)params[3]; if (max <= 0) return 0; cptr = amx_Address(amx, params[2]); if ((option = tokenize(cmdline, params[1], &length)) == NULL) { /* option not found, return an empty string */ *cptr = 0; return 0; } /* if */ if (params[4]) max *= sizeof(cell); if (max > length + 1) max = length + 1; str = (TCHAR *)alloca(max*sizeof(TCHAR)); if (str == NULL) { amx_RaiseError(amx, AMX_ERR_NATIVE); return 0; } /* if */ memcpy(str, option, (max - 1) * sizeof(TCHAR)); str[max - 1] = __T('\0'); amx_SetString(cptr, (char*)str, (int)params[4], sizeof(TCHAR)>1, max); return 1; }
/* Fixed:fsqroot(Fixed:value) */ static cell AMX_NATIVE_CALL n_fsqroot(AMX *amx,const cell *params) { cell value=params[1]; cell low=0; cell high=value; cell mid[3]={8,0,0}; if (value<0) { amx_RaiseError(amx, AMX_ERR_DOMAIN); return 0; } /* if */ while (high-low > 1) { mid[1]=mid[2]=(low+high)/2; if (n_fmul(amx,mid) < value) low=mid[1]; else high=mid[1]; } /* while */ /* check whether low or high mark comes closest */ if (low!=high) { cell deltalow, deltahigh; mid[1]=mid[2]=low; deltalow=value-n_fmul(amx,mid); assert(deltalow>=0); mid[1]=mid[2]=high; deltahigh=n_fmul(amx,mid)-value; assert(deltahigh>=0); if (deltahigh<=deltalow) low=high; /* return "high" mark (comes closer to the answer) */ } /* if */ return low; }
static void *fillarray(AMX *amx, PARAM *param, cell *cptr) { int i; void *vptr; vptr = malloc(param->range * (param->size / 8)); if (vptr == NULL) { amx_RaiseError(amx, AMX_ERR_NATIVE); return NULL; } /* if */ assert(param->range > 1); if (param->size == 8) { unsigned char *ptr = (unsigned char *)vptr; for (i = 0; i < param->range; i++) *ptr++ = (unsigned char)*cptr++; } else if (param->size == 16) { unsigned short *ptr = (unsigned short *)vptr; for (i = 0; i < param->range; i++) *ptr++ = (unsigned short)*cptr++; } else { unsigned long *ptr = (unsigned long *)vptr; for (i = 0; i < param->range; i++) *ptr++ = (unsigned long)*cptr++; } /* for */ return vptr; }
/* bool: argstr(index=0, const option[]="", value[]="", maxlength=sizeof value, bool:pack=false) * returns true if the option was found and false otherwise */ static cell AMX_NATIVE_CALL n_argstr(AMX *amx, const cell *params) { const TCHAR *option, *key; int length, max; TCHAR *str; cell *cptr; max = (int)params[4]; if (max <= 0) return 0; amx_StrParam(amx, params[2], key); amx_GetAddr(amx, params[3], &cptr); if (cptr == NULL) { amx_RaiseError(amx, AMX_ERR_NATIVE); return 0; } /* if */ option = matcharg(key, (int)params[1], &length); if (option == NULL) return 0; /* option not found */ /* check whether we must write the value of the option at all; in case the * size is one cell and that cell is already zero, we do not write anything * back */ assert(params[4] > 0); if (params[4] > 1 || *cptr != 0) { if (params[5]) max *= sizeof(cell); if (max > length + 1) max = length + 1; str = (TCHAR *)alloca(max*sizeof(TCHAR)); if (str == NULL) { amx_RaiseError(amx, AMX_ERR_NATIVE); return 0; } /* if */ memcpy(str, option, (max - 1) * sizeof(TCHAR)); str[max - 1] = __T('\0'); amx_SetString(cptr, (char*)str, (int)params[5], sizeof(TCHAR)>1, max); } /* if */ return 1; }
static cell AMX_NATIVE_CALL sm_lightning(AMX *amx, cell *params) { if(gamestate != GS_LEVEL) { amx_RaiseError(amx, SC_ERR_GAMEMODE | SC_ERR_MASK); return -1; } P_ForceLightning(); return 0; }
static cell AMX_NATIVE_CALL n_floatsqroot(AMX *amx,const cell *params) { /* * params[0] = number of bytes * params[1] = float operand */ REAL fA = amx_ctof(params[1]); fA = (REAL)sqrt(fA); if (fA < 0) return amx_RaiseError(amx, AMX_ERR_DOMAIN); return amx_ftoc(fA); }
static cell AMX_NATIVE_CALL n_strfixed(AMX *amx,const cell *params) { char str[50],*ptr; cell *cstr,intpart,decimals; long multiplier,divisor; int len,sign=1; cstr=amx_Address(amx,params[1]); amx_StrLen(cstr,&len); if (len>=50) { amx_RaiseError(amx,AMX_ERR_NATIVE); return 0; } /* if */ amx_GetString(str,cstr,0,UNLIMITED); ptr=str; intpart=0; decimals=0; multiplier=MULTIPLIER; divisor=1; while (*ptr!='\0' && *ptr<=' ') ptr++; /* skip whitespace */ if (*ptr=='-') { /* handle sign */ sign=-1; ptr++; } else if (*ptr=='+') { ptr++; } /* if */ while (isdigit(*ptr)) { intpart=intpart*10 + (*ptr-'0'); ptr++; } /* while */ if (*ptr=='.') { ptr++; len=0; while (isdigit(*ptr) && len<8) { decimals=decimals*10 + (*ptr-'0'); if (multiplier>1) multiplier/=10; else divisor*=10; ptr++; len++; } /* while */ } /* if */ return ((intpart*MULTIPLIER) + (decimals*multiplier+(divisor/2))/divisor) * sign; }
static cell AMX_NATIVE_CALL n_floatlog(AMX *amx,const cell *params) { /* * params[0] = number of bytes * params[1] = float operand 1 (value) * params[2] = float operand 2 (base) */ REAL fValue = amx_ctof(params[1]); REAL fBase = amx_ctof(params[2]); (void)amx; if (fValue <= 0.0 || fBase <= 0) return amx_RaiseError(amx, AMX_ERR_DOMAIN); if (fBase == 10.0) // ??? epsilon fValue = (REAL)log10(fValue); else fValue = (REAL)(log(fValue) / log(fBase)); return amx_ftoc(fValue); }
static cell AMX_NATIVE_CALL funcidx(AMX *amx,cell *params) { char name[64]; cell *cstr; int index,err; amx_GetAddr(amx,params[1],&cstr); #if 0 /* if you are paranoia */ amx_StrLen(cstr,&len); if (len>=64) { amx_RaiseError(amx,AMX_ERR_NATIVE); return 0; } /* if */ #endif amx_GetString(name,cstr); err=amx_FindPublic(amx,name,&index); if (err!=AMX_ERR_NONE) index=-1; /* this is not considered a fatal error */ return index; }
/* bool: argvalue(index=0, const option[]="", &value=cellmin) * returns true if the option was found and false otherwise */ static cell AMX_NATIVE_CALL n_argvalue(AMX *amx, const cell *params) { const TCHAR *option, *key; int length; cell *cptr; amx_StrParam(amx, params[2], key); amx_GetAddr(amx, params[3], &cptr); if (cptr == NULL) { amx_RaiseError(amx, AMX_ERR_NATIVE); return 0; } /* if */ option = matcharg(key, (int)params[1], &length); if (option == NULL) return 0; /* check whether we must write the value of the option at all */ if (length > 0 && (_istdigit(*option) || *option == __T('-'))) *cptr = _tcstol(option, NULL, 10); return 1; }
/* PID: procexec(const commandline[]) * Executes a program. Returns an "id" representing the new process (or 0 on * failure). */ static cell AMX_NATIVE_CALL n_procexec(AMX *amx, const cell *params) { TCHAR *pgmname; #if defined __WIN32__ || defined _WIN32 || defined WIN32 BOOL IsWinNT; OSVERSIONINFO VerInfo; STARTUPINFO si; SECURITY_ATTRIBUTES sa; SECURITY_DESCRIPTOR sd; PROCESS_INFORMATION pi; #elif defined _Windows HINSTANCE hinst; #elif defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ pid_t pid; #endif amx_StrParam(amx,params[1],pgmname); #if defined __WIN32__ || defined _WIN32 || defined WIN32 /* most of this code comes from a "Borland Network" article, combined * with some knowledge gained from a CodeProject article */ closepipe(); VerInfo.dwOSVersionInfoSize=sizeof(OSVERSIONINFO); GetVersionEx(&VerInfo); IsWinNT = VerInfo.dwPlatformId==VER_PLATFORM_WIN32_NT; if (IsWinNT) { //initialize security descriptor (Windows NT) InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION); SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE); sa.lpSecurityDescriptor = &sd; } else { sa.lpSecurityDescriptor = NULL; } /* if */ sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = TRUE; //allow inheritable handles if (!CreatePipe(&newstdin,&write_stdin,&sa,0)) { //create stdin pipe amx_RaiseError(amx, AMX_ERR_NATIVE); return 0; } /* if */ if (!CreatePipe(&read_stdout,&newstdout,&sa,0)) { //create stdout pipe closepipe(); amx_RaiseError(amx, AMX_ERR_NATIVE); return 0; } /* if */ GetStartupInfo(&si); //set startupinfo for the spawned process si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; si.wShowWindow = SW_SHOWNORMAL; si.hStdOutput = newstdout; si.hStdError = newstdout; //set the new handles for the child process si.hStdInput = newstdin; /* spawn the child process */ if (!CreateProcess(NULL,(TCHAR*)pgmname,NULL,NULL,TRUE,CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi)) { closepipe(); return 0; } /* if */ CloseHandle(pi.hThread); CloseHandle(pi.hProcess); Sleep(100); return pi.dwProcessId; #elif defined _Windows hinst=WinExec(pgmname,SW_SHOW); if (hinst<=32) hinst=0; return (cell)hinst; #elif defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ /* set up communication pipes first */ closepipe(); if (pipe(pipe_to)!=0 || pipe(pipe_from)!=0) { closepipe(); amx_RaiseError(amx, AMX_ERR_NATIVE); return 0; } /* if */ /* attempt to fork */ if ((pid=fork())<0) { closepipe(); amx_RaiseError(amx, AMX_ERR_NATIVE); return 0; } /* if */ if (pid==0) { /* this is the child process */ #define MAX_ARGS 10 TCHAR *args[MAX_ARGS]; int i; dup2(pipe_to[0],STDIN_FILENO); /* replace stdin with the in side of the pipe */ dup2(pipe_from[1],STDOUT_FILENO); /* replace stdout with the out side of the pipe */ close(pipe_to[0]); /* the pipes are no longer needed */ close(pipe_to[1]); close(pipe_from[0]); close(pipe_from[1]); pipe_to[0]=-1; pipe_to[1]=-1; pipe_from[0]=-1; pipe_from[1]=-1; /* split off the option(s) */ assert(MAX_ARGS>=2); /* args[0] is reserved */ memset(args,0,MAX_ARGS*sizeof(TCHAR*)); args[0]=pgmname; for (i=1; i<MAX_ARGS && args[i-1]!=NULL; i++) { if ((args[i]=strchr(args[i-1],' '))!=NULL) { args[i][0]='\0'; args[i]+=1; } /* if */ } /* for */ /* replace the child fork with a new process */ if(execvp(pgmname,args)<0) return 0; } else { close(pipe_to[0]); /* close unused pipes */ close(pipe_from[1]); pipe_to[0]=-1; pipe_from[1]=-1; } /* if */ return pid; #else return (system(pgmname)==0); #endif }
/* libcall(const libname[], const funcname[], const typestring[], ...) * * Loads the DLL or shared library if not yet loaded (the name comparison is * case sensitive). * * typestring format: * Whitespace is permitted between the types, but not inside the type * specification. The string "ii[4]&u16s" is equivalent to "i i[4] &u16 s", * but the latter is easier on the eye. * * types: * i = signed integer, 16-bit in Windows 3.x, else 32-bit in Win32 and Linux * u = unsigned integer, 16-bit in Windows 3.x, else 32-bit in Win32 and Linux * f = IEEE floating point, 32-bit * p = packed string * s = unpacked string * The difference between packed and unpacked strings is only relevant when * the parameter is passed by reference (see below). * * pass-by-value and pass-by-reference: * By default, parameters are passed by value. To pass a parameter by * reference, prefix the type letter with an "&": * &i = signed integer passed by reference * i = signed integer passed by value * Same for '&u' versus 'u' and '&f' versus 'f'. * * Arrays are passed by "copy & copy-back". That is, libcall() allocates a * block of dynamic memory to copy the array into. On return from the foreign * function, libcall() copies the array back to the abstract machine. The * net effect is similar to pass by reference, but the foreign function does * not work in the AMX stack directly. During the copy and the copy-back * operations, libcall() may also transform the array elements, for example * between 16-bit and 32-bit elements. This is done because Pawn only * supports a single cell size, which may not fit the required integer size * of the foreign function. * * See "element ranges" for the syntax of passing an array. * * Strings may either be passed by copy, or by "copy & copy-back". When the * string is an output parameter (for the foreign function), the size of the * array that will hold the return string must be indicated between square * brackets behind the type letter (see "element ranges"). When the string * is "input only", this is not needed --libcall() will determine the length * of the input string itself. * * The tokens 'p' and 's' are equivalent, but 'p[10]' and 's[10]' are not * equivalent: the latter syntaxes determine whether the output from the * foreign function will be stored as a packed or an unpacked string. * * element sizes: * Add an integer behind the type letter; for example, 'i16' refers to a * 16-bit signed integer. Note that the value behind the type letter must * be either 8, 16 or 32. * * You should only use element size specifiers on the 'i' and 'u' types. That * is, do not use these specifiers on 'f', 's' and 'p'. * * element ranges: * For passing arrays, the size of the array may be given behind the type * letter and optional element size. The token 'u[4]' indicates an array of * four unsigned integers, which are typically 32-bit. The token 'i16[8]' * is an array of 8 signed 16-bit integers. Arrays are always passed by * "copy & copy-back" * * When compiled as Unicode, this library converts all strings to Unicode * strings. * * The calling convention for the foreign functions is assumed: * - "__stdcall" for Win32, * - "far pascal" for Win16 * - and the GCC default for Unix/Linux (_cdecl) * * C++ name mangling of the called function is not handled (there is no standard * convention for name mangling, so there is no portable way to convert C++ * function names to mangled names). Win32 name mangling (used by default by * Microsoft compilers on functions declared as __stdcall) is also not handled. * * Returns the value of the called function. */ static cell AMX_NATIVE_CALL n_libcall(AMX *amx, const cell *params) { const TCHAR *libname, *funcname, *typestring; MODLIST *item; int paramidx, typeidx, idx; PARAM ps[MAXPARAMS]; cell *cptr,result; LIBFUNC LibFunc; amx_StrParam(amx, params[1], libname); item = findlib(&ModRoot, amx, libname); if (item == NULL) item = addlib(&ModRoot, amx, libname); if (item == NULL) { amx_RaiseError(amx, AMX_ERR_NATIVE); return 0; } /* if */ /* library is loaded, get the function */ amx_StrParam(amx, params[2], funcname); LibFunc=(LIBFUNC)SearchProcAddress(item->inst, funcname); if (LibFunc==NULL) { amx_RaiseError(amx, AMX_ERR_NATIVE); return 0; } /* if */ #if defined HAVE_DYNCALL_H /* (re-)initialize the dyncall library */ if (dcVM==NULL) { dcVM=dcNewCallVM(4096); dcMode(dcVM,DC_CALL_C_X86_WIN32_STD); } /* if */ dcReset(dcVM); #endif /* decode the parameters */ paramidx=typeidx=0; amx_StrParam(amx, params[3], typestring); while (paramidx < MAXPARAMS && typestring[typeidx]!=__T('\0')) { /* skip white space */ while (typestring[typeidx]!=__T('\0') && typestring[typeidx]<=__T(' ')) typeidx++; if (typestring[typeidx]==__T('\0')) break; /* save "pass-by-reference" token */ ps[paramidx].type=0; if (typestring[typeidx]==__T('&')) { ps[paramidx].type=BYREF; typeidx++; } /* if */ /* store type character */ ps[paramidx].type |= (unsigned char)typestring[typeidx]; typeidx++; /* set default size, then check for an explicit size */ #if defined __WIN32__ || defined _WIN32 || defined WIN32 ps[paramidx].size=32; #elif defined _Windows ps[paramidx].size=16; #endif if (_istdigit(typestring[typeidx])) { ps[paramidx].size=(unsigned char)_tcstol(&typestring[typeidx],NULL,10); while (_istdigit(typestring[typeidx])) typeidx++; } /* if */ /* set default range, then check for an explicit range */ ps[paramidx].range=1; if (typestring[typeidx]=='[') { ps[paramidx].range=_tcstol(&typestring[typeidx+1],NULL,10); while (typestring[typeidx]!=']' && typestring[typeidx]!='\0') typeidx++; ps[paramidx].type |= BYREF; /* arrays are always passed by reference */ typeidx++; /* skip closing ']' too */ } /* if */ /* get pointer to parameter */ cptr=amx_Address(amx,params[paramidx+4]); switch (ps[paramidx].type) { case 'i': /* signed integer */ case 'u': /* unsigned integer */ case 'f': /* floating point */ assert(ps[paramidx].range==1); ps[paramidx].v.val=(int)*cptr; break; case 'i' | BYREF: case 'u' | BYREF: case 'f' | BYREF: ps[paramidx].v.ptr=cptr; if (ps[paramidx].range>1) { /* convert array and pass by address */ ps[paramidx].v.ptr = fillarray(amx, &ps[paramidx], cptr); } /* if */ break; case 'p': case 's': case 'p' | BYREF: case 's' | BYREF: if (ps[paramidx].type=='s' || ps[paramidx].type=='p') { int len; /* get length of input string */ amx_StrLen(cptr,&len); len++; /* include '\0' */ /* check max. size */ if (len<ps[paramidx].range) len=ps[paramidx].range; ps[paramidx].range=len; } /* if */ ps[paramidx].v.ptr=malloc(ps[paramidx].range*sizeof(TCHAR)); if (ps[paramidx].v.ptr==NULL) return amx_RaiseError(amx, AMX_ERR_NATIVE); amx_GetString((char *)ps[paramidx].v.ptr,cptr,sizeof(TCHAR)>1,UNLIMITED); break; default: /* invalid parameter type */ return amx_RaiseError(amx, AMX_ERR_NATIVE); } /* switch */ paramidx++; } /* while */ if ((params[0]/sizeof(cell)) - 3 != (size_t)paramidx) return amx_RaiseError(amx, AMX_ERR_NATIVE); /* format string does not match number of parameters */ #if defined HAVE_DYNCALL_H for (idx = 0; idx < paramidx; idx++) { if ((ps[idx].type=='i' || ps[idx].type=='u' || ps[idx].type=='f') && ps[idx].range==1) { switch (ps[idx].size) { case 8: dcArgChar(dcVM,(unsigned char)(ps[idx].v.val & 0xff)); break; case 16: dcArgShort(dcVM,(unsigned short)(ps[idx].v.val & 0xffff)); break; default: dcArgLong(dcVM,ps[idx].v.val); } /* switch */ } else { dcArgPointer(dcVM,ps[idx].v.ptr); } /* if */ } /* for */ result=(cell)dcCallPointer(dcVM,(void*)LibFunc); #else /* HAVE_DYNCALL_H */ /* push the parameters to the stack (left-to-right in 16-bit; right-to-left * in 32-bit) */ #if defined __WIN32__ || defined _WIN32 || defined WIN32 for (idx=paramidx-1; idx>=0; idx--) { #else for (idx=0; idx<paramidx; idx++) { #endif if ((ps[idx].type=='i' || ps[idx].type=='u' || ps[idx].type=='f') && ps[idx].range==1) { switch (ps[idx].size) { case 8: push((unsigned char)(ps[idx].v.val & 0xff)); break; case 16: push((unsigned short)(ps[idx].v.val & 0xffff)); break; default: push(ps[idx].v.val); } /* switch */ } else { push(ps[idx].v.ptr); } /* if */ } /* for */ /* call the function; all parameters are already pushed to the stack (the * function should remove the parameters from the stack) */ result=LibFunc(); #endif /* HAVE_DYNCALL_H */ /* store return values and free allocated memory */ for (idx=0; idx<paramidx; idx++) { switch (ps[idx].type) { case 'p': case 's': free(ps[idx].v.ptr); break; case 'p' | BYREF: case 's' | BYREF: cptr=amx_Address(amx,params[idx+4]); amx_SetString(cptr,(char *)ps[idx].v.ptr,ps[idx].type==('p'|BYREF),sizeof(TCHAR)>1,UNLIMITED); free(ps[idx].v.ptr); break; case 'i': case 'u': case 'f': assert(ps[idx].range==1); break; case 'i' | BYREF: case 'u' | BYREF: case 'f' | BYREF: cptr=amx_Address(amx,params[idx+4]); if (ps[idx].range==1) { /* modify directly in the AMX (no memory block was allocated */ switch (ps[idx].size) { case 8: *cptr= (ps[idx].type==('i' | BYREF)) ? (long)((signed char)*cptr) : (*cptr & 0xff); break; case 16: *cptr= (ps[idx].type==('i' | BYREF)) ? (long)((short)*cptr) : (*cptr & 0xffff); break; } /* switch */ } else { int i; for (i=0; i<ps[idx].range; i++) { switch (ps[idx].size) { case 8: *cptr= (ps[idx].type==('i' | BYREF)) ? ((signed char*)ps[idx].v.ptr)[i] : ((unsigned char*)ps[idx].v.ptr)[i]; break; case 16: *cptr= (ps[idx].type==('i' | BYREF)) ? ((short*)ps[idx].v.ptr)[i] : ((unsigned short*)ps[idx].v.ptr)[i]; break; default: *cptr= (ps[idx].type==('i' | BYREF)) ? ((long*)ps[idx].v.ptr)[i] : ((unsigned long*)ps[idx].v.ptr)[i]; } /* switch */ } /* for */ free((char *)ps[idx].v.ptr); } /* if */ break; default: assert(0); } /* switch */ } /* for */ return result; } /* bool: libfree(const libname[]="") * When the name is an empty string, this function frees all libraries (for this * abstract machine). The name comparison is case sensitive. * Returns true if one or more libraries were freed. */ static cell AMX_NATIVE_CALL n_libfree(AMX *amx, const cell *params) { const TCHAR *libname; amx_StrParam(amx,params[1],libname); return freelib(&ModRoot,amx,libname) > 0; } #else /* HAVE_DYNCALL_H || WIN32_FFI */ static cell AMX_NATIVE_CALL n_libcall(AMX *amx, const cell *params) { (void)amx; (void)params; return 0; }
static cell AMX_NATIVE_CALL n_fmuldiv(AMX *amx,const cell *params) { #if !USE_ANSI_C #if defined __WATCOMC__ && defined __386__ cell __fmuldiv(void); cell a=params[1]; cell b=params[2]; cell c=params[3]; if (c==0) { amx_RaiseError(amx,AMX_ERR_DIVIDE); return 0; } /* if */ #pragma aux __fmuldiv = \ "mov eax, [a]" \ "mov ecx, [c]" \ "imul [b]" \ "mov ebx, ecx" \ "shr ecx, 1" \ "add eax, ecx" \ "adc edx, 0" \ "idiv ebx" \ "mov [a], eax" \ modify [eax ebx ecx edx]; __fmuldiv(); return a; #elif _MSC_VER>=9 && defined _WIN32 __int64 a; cell divisor=params[3]; if (divisor==0) { amx_RaiseError(amx,AMX_ERR_DIVIDE); return 0; } /* if */ a=((__int64)params[1] * (__int64)params[2] + (__int64)(divisor/2)) / (__int64)divisor; return (cell)a; #elif defined __BORLANDC__ && __BORLANDC__ >= 0x500 && (defined __32BIT__ || defined __WIN32__) __int64 a; cell divisor=params[3]; if (divisor==0) { amx_RaiseError(amx,AMX_ERR_DIVIDE); return 0; } /* if */ a=((__int64)params[1] * (__int64)params[2] + (__int64)(divisor/2)) / (__int64)divisor; return (cell)a; #elif defined __GNUC__ long long a; cell divisor=params[3]; if (divisor==0) { amx_RaiseError(amx,AMX_ERR_DIVIDE); return 0; } /* if */ a=((long long)params[1] * (long long)params[2] + (long long)(divisor/2)) / (long long)divisor; return (cell)a; #else #error Unsupported compiler configuration, but USE_ANSI_C is false #endif #else // USE_ANSI_C ucell a,b,c,d; ucell v[2]; cell sign=1; cell divisor=params[3]; assert(MULTIPLIER<=(1L<<16)); if (divisor==0) { amx_RaiseError(amx,AMX_ERR_DIVIDE); return 0; } /* if */ /* make all three operands positive values, but keep the sign of the result */ if (params[1]<0) { ((cell*)params)[1]=-params[1]; sign=-sign; /* negate result */ } /* if */ if (params[2]<0) { ((cell*)params)[2]=-params[2]; sign=-sign; /* negate result */ } /* if */ if (divisor<0) { divisor=-divisor; sign=-sign; /* negate result */ } /* if */ a = HIWORD(params[1]); b = LOWORD(params[1]); c = HIWORD(params[2]); d = LOWORD(params[2]); /* store the intermediate into a 64-bit/128-bit number */ v[1]=c*a; v[0]=d*b; ADD_WRAP(v[0],v[1],d*a << WORDSHIFT); ADD_WRAP(v[0],v[1],c*b << WORDSHIFT); /* add half of the divisor, to round the data */ ADD_WRAP(v[0],v[1],(ucell)divisor/2); /* if the divisor is smaller than v[1], the result will not fit in a cell */ if ((ucell)divisor<v[1]) { amx_RaiseError(amx,AMX_ERR_DOMAIN); return 0; } /* if */ return (cell)div64_32(v,(ucell)divisor) * sign; #endif }
static cell AMX_NATIVE_CALL n_fdiv(AMX *amx,const cell *params) { #if !USE_ANSI_C #if defined __WATCOMC__ && defined __386__ cell __fdiv(void); cell a=params[1]; cell b=params[2]; #if MULTIPLIER != 1000 #error Assembler chunks must be modified for a different base #endif if (b==0) { amx_RaiseError(amx,AMX_ERR_DIVIDE); return 0; } /* if */ #pragma aux __fdiv = \ "mov eax, [a]" \ "mov ecx, [b]" \ "cdq" \ "mov ebx, 1000" \ "imul ebx" \ "mov ebx, ecx" \ "shr ecx, 1" \ "add eax, ecx" \ "adc edx, 0" \ "idiv ebx" \ "mov [a], eax" \ modify [eax ebx ecx edx]; __fdiv(); return a; #elif _MSC_VER>=9 && defined _WIN32 __int64 a; cell divisor=params[2]; if (divisor==0) { amx_RaiseError(amx,AMX_ERR_DIVIDE); return 0; } /* if */ a=((__int64)params[1] * (__int64)MULTIPLIER + (__int64)(divisor/2)) / (__int64)divisor; return (cell)a; #elif defined __BORLANDC__ && __BORLANDC__ >= 0x500 && (defined __32BIT__ || defined __WIN32__) __int64 a; cell divisor=params[2]; if (divisor==0) { amx_RaiseError(amx,AMX_ERR_DIVIDE); return 0; } /* if */ a=((__int64)params[1] * (__int64)MULTIPLIER + (__int64)(divisor/2)) / (__int64)divisor; return (cell)a; #elif defined __GNUC__ long long a; cell divisor=params[2]; if (divisor==0) { amx_RaiseError(amx,AMX_ERR_DIVIDE); return 0; } /* if */ a=((long long)params[1] * (long long)MULTIPLIER + (long long)(divisor/2)) / (long long)divisor; return (cell)a; #else #error Unsupported compiler configuration, but USE_ANSI_C is false #endif #else // USE_ANSI_C /* The dividend must be scaled prior to division. The dividend * is a 32-bit number, however, so when shifted, it will become * a value that no longer fits in a 32-bit variable. This routine * does the division by using only 16-bit and 32-bit values, but * with considerable effort. * If your compiler supports 64-bit integers, modify this routine * to use them. If your processor can do a simple 64-bit by 32-bit * division in assembler, write assembler chunks. * In other words: the straight C routine that follows is correct * and portable, but use it only as a last resort. * * This function was adapted from source code that appeared in * Dr. Dobb's Journal, August 1992, page 117. */ cell dividend=params[1]; cell divisor=params[2]; cell sign=1; ucell b[2]; if (divisor==0) { amx_RaiseError(amx,AMX_ERR_NATIVE); return 0; } /* if */ /* make both operands positive values, but keep the sign of the result */ if (dividend<0) { dividend=-dividend; sign=-sign; /* negate result */ } /* if */ if (divisor<0) { divisor=-divisor; sign=-sign; /* negate result */ } /* if */ /* pre-scale the dividend into a 64-bit/128-bit number */ b[0]=dividend*MULTIPLIER; b[1]=(HIWORD(dividend)*MULTIPLIER) >> WORDSHIFT; /* add half of the divisor, to round the data */ b[0]+=(ucell)divisor/2; if (b[0]<(ucell)divisor/2) b[1]+=1; /* wrap-around ocurred */ /* if the divisor is smaller than b[1], the result will not fit in a cell */ if ((ucell)divisor<b[1]) { amx_RaiseError(amx,AMX_ERR_DOMAIN); return 0; } /* if */ return (cell)div64_32(b,(ucell)divisor) * sign; #endif }
static cell AMX_NATIVE_CALL n_fmul(AMX *amx,const cell *params) { #if !USE_ANSI_C #if defined __WATCOMC__ && defined __386__ cell __fmul(void); cell a=params[1]; cell b=params[2]; #if MULTIPLIER != 1000 #error Assembler chunks must be modified for a different base #endif #pragma aux __fmul = \ "mov eax, [a]" \ "mov ebx, 1000" \ "imul [b]" \ "add eax, 500" \ "adc edx, 0" \ "idiv ebx" \ "mov [a], eax" \ modify [eax ebx edx]; __fmul(); (void)amx; return a; #elif _MSC_VER>=9 && defined _WIN32 __int64 a=(__int64)params[1] * (__int64)params[2]; a=(a+MULTIPLIER/2) / MULTIPLIER; (void)amx; return (cell)a; #elif defined __BORLANDC__ && __BORLANDC__ >= 0x500 && (defined __32BIT__ || defined __WIN32__) __int64 a=(__int64)params[1] * (__int64)params[2]; a=(a+MULTIPLIER/2) / MULTIPLIER; (void)amx; return (cell)a; #elif defined __GNUC__ long long a=(long long)params[1] * (long long)params[2]; a=(a+MULTIPLIER/2) / MULTIPLIER; (void)amx; return (cell)a; #else #error Unsupported compiler configuration, but USE_ANSI_C is false #endif #else // USE_ANSI_C /* (Xs * Ys) == (X*Y)ss, where "s" stands for scaled. * The desired result is (X*Y)s, so we must unscale once. * but we cannot do this before multiplication, because of loss * of precision, and we cannot do it after the multiplication * because of the possible overflow. * The technique used here is to cut the multiplicands into * components and to multiply these components separately: * * Assume Xs == (A << 16) + B and Ys == (C << 16) + D, where A, B, * C and D are 16 bit numbers. * * A B * C D * --- * * D*B + (D*A << 16) + (C*B << 16) + (C*A << (16+16)) * * Thus we have built a 64-bit number, which can now be scaled back * to 32-bit by dividing by the scale factor. */ #define ADD_WRAP(var,carry,expr) (((var)+=(expr)), ((carry)+=((var)<(expr)) ? 1 : 0)) ucell a,b,c,d; ucell v[2]; cell sign=1; (void)amx; assert(MULTIPLIER<=(1L<<WORDSHIFT)); /* make both operands positive values, but keep the sign of the result */ if (params[1]<0) { ((cell*)params)[1]=-params[1]; sign=-sign; /* negate result */ } /* if */ if (params[2]<0) { ((cell*)params)[2]=-params[2]; sign=-sign; /* negate result */ } /* if */ a = HIWORD(params[1]); b = LOWORD(params[1]); c = HIWORD(params[2]); d = LOWORD(params[2]); /* store the intermediate into a 64-bit/128-bit number */ v[1]=c*a; v[0]=d*b; ADD_WRAP(v[0],v[1],d*a << WORDSHIFT); ADD_WRAP(v[0],v[1],c*b << WORDSHIFT); /* add half of the divisor, to round the data */ ADD_WRAP(v[0],v[1],MULTIPLIER/2); /* if the divisor is smaller than v[1], the result will not fit in a cell */ if (MULTIPLIER<v[1]) { amx_RaiseError(amx,AMX_ERR_DOMAIN); return 0; } /* if */ return (cell)div64_32(v,MULTIPLIER) * sign; #endif }