; AMXEXECN.ASM Abstract Machine for the "Pawn" language ; ;Some notes: ; * This file was adapted from AMXEXEC.ASM (for MASM/TASM/WASM). This ; version is for NASM (Netwide Assembler). NASM uses Intel syntax for ; the mnemonics, but it is not compatible with MASM. ; * The "calling convention" is __cdecl for the amx_exec_asm() itself and ; __cdecl or __stdcall for the native routines (the default is __cdecl, ; define the macro STDECL to set __stdcall). ; * The borland compiler uses different segment definitions as Microsoft ; Visual C/C++ and GNU GCC. To assemble the abstract machine with "Borland" ; segments, add the definition "BORLAND" on the command line. ; * You will need to compile the standard AMX.C file with the macro ASM32 ; defined. On the command line, use: ; nasmw -f obj -d BORLAND amxexecn.asm ; bcc32 -DASM32 srun.c amx.c amxcore.c amxcons.c amxexecn.obj ; or ; nasmw -f win32 amxexecn.asm ; or ; nasm -f elf amxexecn.asm ; gcc -o srun -DLINUX -DASM32 -I../linux srun.c amx.c amxcore.c amxcons.c amxexecn.o ; * See the notes in AMXEXEC.ASM for more information and a change log). ; ; ;Copyright and license of use, please read ;----------------------------------------- ;The assembler implementation of the abstract machine for the Pawn language, ;specifically the file AMXEXEC.ASM, is copyright (c) 1998-2000 by Marc Peter. ; ;Permission is hereby granted, without written agreement and without paid ;license or royalty fees, to use, copy, modify, and distribute this software ;and its documentation for any purpose, subject to the following conditions: ; ;1. The above copyright notice and this permission notice shall appear in all ; copies or substantial portions of this software. ; ;2. Modifications of this software that do not originate from me (Marc Peter) ; must be explicitly mentioned in a README file or another appropriate ; place. ; ;The use of this software as a subsystem of a larger software product is ;explicitly allowed, regardless of whether that larger product is proprietary, ;gratis or commercially available. ; ;I (Marc Peter) specifically disclaim any warranties, including, but not ;limited to, the implied warranties of merchantability and fitness for a ;particular purpose. The software is provided on an "as is" basis, ;and I have no obligation to provide maintenance, support, updates, ;enhancements or modifications. ; ;I cannot be held liable for any damage or loss of profits that results ;from the use of the software (or part thereof), or from the inability to ;use it. ; ; ;History (list of changes) ;------------------------- ; 24 february 2013 by Scott Ehlert ; Aligned stack to 16-byte boundary for native calls in case they make library ; calls on Mac OS X or use SSE instructions. ; 10 february 2006 by David Anderson ; Addition of float opcodes ; 17 february 2005 by Thiadmer Riemersms ; Addition of the BREAK opcode, removal of the older debugging opcode table. ; 6 march 2004 by Thiadmer Riemersma ; Corrected a bug in OP_FILL, where a cell preceding the array would ; be overwritten (zero'ed out). This bug was brought to my attention ; by Robert Daniels. ; 2 february 2004 by Thiadmer Riemersma (TR) ; Added checking of the return address in the RET and RETN opcodes. ; Changed handling of LINE opcode, so that the debugger can force a ; sleep. ; 22 december 2003 by Thiadmer Riemersma (TR) ; Added support for the SYMTAG and SYSCALL.D opcodes ; 3 october 2003 by Thiadmer Riemersma (TR) ; Added "non-debug" versions of various opcodes, to avoid repetitive ; checking of the "debug" flag. ; 15 September 2003 by Thiadmer Riemersma (TR) ; Minor corrections, mostly to support older versions of NASM ; 26 January 2003 by Thiadmer Riemersma (TR) ; Port to NASM ;----- ;CPU 386 -- some older versions of NASM do not support this keyword ; Macro to begin a code segment %macro Start_CODE 0 %ifdef BORLAND segment _TEXT public align=1 class=CODE use32 %else segment .text %endif %endmacro ; Macro to begin a data segment %macro Start_DATA 0 %ifdef BORLAND segment _DATA public align=4 class=DATA use32 %else segment .data %endif %endmacro %include "amxdefn.asm" ;#define PUSH(v) ( stk-=sizeof(cell), *(cell *)(data+(int)stk)=v ) %macro _PUSH 1 mov [edi+ecx-4],%1 sub ecx,4 %endmacro ;#define POP(v) ( v=*(cell *)(data+(int)stk), stk+=sizeof(cell) ) %macro _POP 1 mov %1,[edi+ecx] add ecx,4 %endmacro %macro GO_ON 0 jmp DWORD [esi] ; ALIGN 4 %endmacro %macro _CHKSTACK 0 cmp ecx,stp jg near err_stacklow %endmacro %macro _CHKMARGIN 0 lea ebp,[ecx-16*4] ;savety margin = 16 cells cmp hea,ebp jg near err_stack %endmacro ;Normal abort, saves pri/alt %macro _ABORT 1 mov ebp,amx mov [ebp+_pri], dword eax ; store values in AMX structure (PRI, ALT) mov [ebp+_alt], dword edx ; store values in AMX structure (PRI, ALT) mov [ebp+_error], dword %1 jmp _return %endmacro ;Checked abort, saves nothing and uses a conditional %macro _CHKABORT 1 mov ebp,amx mov [ebp+_error], %1 cmp %1, AMX_ERR_NONE jne _return %endmacro ;Fast abort, only aborts, nothing else %macro _FASTABORT 0 jmp _return %endmacro %macro _CHKHEAP 0 mov ebp,amx mov ebp,[ebp+_hlw] cmp DWORD hea,ebp jl near err_heaplow %endmacro %macro _CHKDIVIDEZERO 0 or ebp,ebp ; check for divide by zero jz near err_divide %endmacro %macro _VERIFYADDRESS 1 ; used in load.i, store.i & lidx cmp %1,stp ; error if address >= stp jae near err_memaccess cmp %1,hea ; so address=hea, ok if address>=stk jb near err_memaccess %%address_ok: %endmacro %macro _SAVEREGS 0 ; save the registers (that may not be PUSHAD ; __stdcall calling conventions) %endmacro %macro _RESTOREREGS 0 POPAD %endmacro %macro _DROPARGS 1 ; remove function arguments from the stack %ifndef STDECL ; (only for __cdecl calling convention) add esp,%1 %endif %endmacro %macro _STK_ALIGN 1 ; align stack to 16-byte boundary and ; allocate %1 bytes of stack space %if %1 % 16 != 0 %error "expected 16-byte aligned value" %endif push edi mov edi, esp and esp, 0xFFFFFFF0 sub esp, %1 %endmacro %macro _STK_RESTORE 0 ; restore stack pointer after 16-byte alignment mov esp, edi pop edi %endmacro Start_CODE ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ;cell asm_exec( cell *regs, cell *retval, cell stp, cell hea ); ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; GLOBAL _amx_exec_asm GLOBAL amx_exec_asm amx_exec_asm: _amx_exec_asm: ;PROC push ebx mov eax,[esp+08h] mov edx,[esp+0ch] mov ebx,[esp+10h] mov ecx,[esp+14h] push edi push esi push ebp sub esp,4*3 ; place for PRI, ALT & STK at SYSREQs push DWORD [eax+20h] ; store code size push DWORD [eax+1ch] ; store pointer to code segment push DWORD [eax+18h] ; store pointer to AMX push edx ; store address of retval push ebx ; store STP push ecx ; store HEA push DWORD [eax+14h] ; store FRM %define stk [esp+36] ; define some aliases to registers %define alt [esp+32] ; that are stored on the stack %define pri [esp+28] %define codesiz [esp+24] %define code [esp+20] %define amx [esp+16] %define retval [esp+12] %define stp [esp+8] %define hea [esp+4] %define frm [esp] ; FRM is NOT stored in ebp, rather FRM+DAT ; is being held in ebx. mov edx,code ; change the code size to an... add codesiz,edx ; ..."end of code" address mov edx,[eax+04h] ; get ALT mov esi,[eax+08h] ; get CIP mov edi,[eax+0ch] ; get pointer to data segment mov ecx,[eax+10h] ; get STK mov ebx,[eax+14h] ; get FRM mov eax,[eax] ; get PRI add ebx,edi ; relocate frame GO_ON ; start interpreting OP_LOAD_PRI: mov eax,[esi+4] add esi,8 mov eax,[edi+eax] GO_ON OP_LOAD_ALT: mov edx,[esi+4] add esi,8 mov edx,[edi+edx] GO_ON ;good OP_LOAD_S_PRI: mov eax,[esi+4] add esi,8 mov eax,[ebx+eax] GO_ON ;good OP_LOAD_S_ALT: mov edx,[esi+4] add esi,8 mov edx,[ebx+edx] GO_ON OP_LOAD_I: add esi,4 _VERIFYADDRESS eax mov eax,[edi+eax] GO_ON OP_LODB_I: _VERIFYADDRESS eax mov ebp,[esi+4] mov eax,[edi+eax] ;subject to misalignment stalls add esi,8 and eax,DWORD [(lodb_and-4)+ebp*4] GO_ON OP_LREF_PRI: mov eax,[esi+4] add esi,8 mov eax,[edi+eax] mov eax,[edi+eax] GO_ON OP_LREF_ALT: mov edx,[esi+4] add esi,8 mov edx,[edi+edx] mov edx,[edi+edx] GO_ON OP_LREF_S_PRI: mov eax,[esi+4] add esi,8 mov eax,[ebx+eax] mov eax,[edi+eax] GO_ON OP_LREF_S_ALT: mov edx,[esi+4] add esi,8 mov edx,[ebx+edx] mov edx,[edi+edx] GO_ON ;good OP_CONST_PRI: mov eax,[esi+4] add esi,8 GO_ON ;good OP_CONST_ALT: mov edx,[esi+4] add esi,8 GO_ON ;good OP_ADDR_PRI: mov eax,[esi+4] add esi,8 add eax,frm GO_ON ;good OP_ADDR_ALT: mov edx,[esi+4] add esi,8 add edx,frm GO_ON OP_STOR_PRI: mov ebp,[esi+4] add esi,8 mov [ebp+edi],eax GO_ON OP_STOR_ALT: mov ebp,[esi+4] add esi,8 mov [ebp+edi],edx GO_ON ;good OP_STOR_S_PRI: mov ebp,[esi+4] add esi,8 mov [ebp+ebx],eax GO_ON ;good OP_STOR_S_ALT: mov ebp,[esi+4] add esi,8 mov [ebp+ebx],edx GO_ON ;good OP_STOR_I: add esi,4 _VERIFYADDRESS edx mov [edi+edx],eax GO_ON OP_STRB_I: _VERIFYADDRESS edx mov ebp,[esi+4] add esi,8 cmp ebp,1 jne short strb_not1byte mov [edi+edx],al GO_ON strb_not1byte: cmp ebp,4 je short strb_4byte mov [edi+edx],ax GO_ON strb_4byte: mov [edi+edx],eax GO_ON OP_SREF_PRI: mov ebp,[esi+4] add esi,8 mov ebp,[edi+ebp] mov [edi+ebp],eax GO_ON OP_SREF_ALT: mov ebp,[esi+4] add esi,8 mov ebp,[edi+ebp] mov [edi+ebp],edx GO_ON OP_SREF_S_PRI: mov ebp,[esi+4] add esi,8 mov ebp,[ebx+ebp] mov [edi+ebp],eax GO_ON OP_SREF_S_ALT: mov ebp,[esi+4] add esi,8 mov ebp,[ebx+ebp] mov [edi+ebp],edx GO_ON ;good OP_LIDX: lea eax,[edx+4*eax] add esi,4 _VERIFYADDRESS eax mov eax,[edi+eax] GO_ON OP_LIDX_B: push ecx mov ecx,[esi+4] shl eax,cl add esi,8 add eax,edx pop ecx _VERIFYADDRESS eax mov eax,[edi+eax] GO_ON ;good OP_IDXADDR: add esi,4 lea eax,[edx+4*eax] GO_ON OP_IDXADDR_B: push ecx mov ecx,[esi+4] add esi,8 shl eax,cl pop ecx add eax,edx GO_ON OP_ALIGN_PRI: mov ebp,4 ; ??? one operation too many? sub ebp,[esi+4] add esi,8 xor eax,ebp GO_ON OP_ALIGN_ALT: mov ebp,4 sub ebp,[esi+4] add esi,8 xor edx,ebp GO_ON OP_LCTRL: mov ebp,[esi+4] add esi,8 cmp ebp,0 jne short lctrl_1 mov eax,code ; COD GO_ON lctrl_1: cmp ebp,1 jne short lctrl_2 mov eax,edi ; DAT GO_ON lctrl_2: cmp ebp,2 jne short lctrl_3 mov eax,hea ; 2=HEA GO_ON lctrl_3: cmp ebp,3 jne short lctrl_4 mov ebp,amx mov eax,stp GO_ON lctrl_4: cmp ebp,4 jne short lctrl_5 mov eax,ecx ; 4=STK GO_ON lctrl_5: cmp ebp,5 jne short lctrl_6 mov eax,frm ; 5=FRM GO_ON lctrl_6: mov eax,esi ; 6=CIP sub eax,code GO_ON OP_SCTRL: mov ebp,[esi+4] add esi,8 cmp ebp,2 jne short sctrl_4 mov hea,eax ; 2=HEA GO_ON sctrl_4: cmp ebp,4 jne short sctrl_5 mov ecx,eax ; 4=STK GO_ON sctrl_5: cmp ebp,5 jne short sctrl_6 mov ebx,eax ; 5=FRM mov frm,eax add ebx,edi ; relocate FRM sctrl_6: GO_ON OP_MOVE_PRI: add esi,4 mov eax,edx GO_ON ;good OP_MOVE_ALT: add esi,4 mov edx,eax GO_ON OP_XCHG: add esi,4 xchg eax,edx GO_ON ;good OP_PUSH_PRI: add esi,4 _PUSH eax GO_ON ;good OP_PUSH_ALT: add esi,4 _PUSH edx GO_ON OP_PUSH_R_PRI: mov ebp,[esi+4] add esi,8 push_loop: _PUSH eax dec ebp jnz short push_loop GO_ON ;good OP_PUSH_C: mov ebp,[esi+4] add esi,8 _PUSH ebp GO_ON OP_PUSH: mov ebp,[esi+4] add esi,8 mov ebp,[ebp+edi] _PUSH ebp GO_ON ;good OP_PUSH_S: mov ebp,[esi+4] add esi,8 mov ebp,[ebp+ebx] _PUSH ebp GO_ON OP_POP_PRI: add esi,4 _POP eax GO_ON ;good OP_POP_ALT: add esi,4 _POP edx GO_ON ;good OP_STACK: mov edx,ecx add ecx,[esi+4] _CHKMARGIN _CHKSTACK add esi,8 GO_ON ;good OP_HEAP: mov ebp,[esi+4] mov edx,hea add esi,8 add hea,ebp _CHKMARGIN _CHKHEAP GO_ON ;good OP_PROC: mov ebx,frm add esi,4 _PUSH ebx mov ebx,edi mov frm,ecx add ebx,ecx _CHKMARGIN GO_ON OP_RET: _POP ebx _POP esi cmp esi,code ; verify ESI>=code jb err_memaccess cmp esi,codesiz ; verify ESI=code jb err_memaccess cmp esi,codesiz ; verify ESI 0 and 0 => 1 GO_ON OP_NEG: add esi,4 neg eax GO_ON OP_INVERT: add esi,4 not eax GO_ON ;good OP_ADD_C: add eax,[esi+4] add esi,8 GO_ON ;good OP_SMUL_C: mov ebp,[esi+4] push edx imul ebp pop edx add esi,8 GO_ON ;good OP_ZERO_PRI: add esi,4 sub eax,eax GO_ON ;good OP_ZERO_ALT: add esi,4 sub edx,edx GO_ON OP_ZERO: mov ebp,[esi+4] add esi,8 mov DWORD [edi+ebp],0 GO_ON OP_ZERO_S: mov ebp,[esi+4] add esi,8 mov DWORD [ebx+ebp],0 GO_ON OP_SIGN_PRI: shl eax,24 add esi,4 sar eax,24 GO_ON OP_SIGN_ALT: shl edx,24 add esi,4 sar edx,24 GO_ON OP_EQ: add esi,4 cmp eax,edx ; PRI == ALT ? mov eax,0 sete al GO_ON OP_NEQ: add esi,4 cmp eax,edx ; PRI != ALT ? mov eax,0 setne al GO_ON OP_LESS: add esi,4 cmp eax,edx ; PRI < ALT ? (unsigned) mov eax,0 setb al GO_ON OP_LEQ: add esi,4 cmp eax,edx ; PRI <= ALT ? (unsigned) mov eax,0 setbe al GO_ON OP_GRTR: add esi,4 cmp eax,edx ; PRI > ALT ? (unsigned) mov eax,0 seta al GO_ON OP_GEQ: add esi,4 cmp eax,edx ; PRI >= ALT ? (unsigned) mov eax,0 setae al GO_ON ;good OP_SLESS: add esi,4 cmp eax,edx ; PRI < ALT ? (signed) mov eax,0 setl al GO_ON OP_SLEQ: add esi,4 cmp eax,edx ; PRI <= ALT ? (signed) mov eax,0 setle al GO_ON OP_SGRTR: add esi,4 cmp eax,edx ; PRI > ALT ? (signed) mov eax,0 setg al GO_ON OP_SGEQ: add esi,4 cmp eax,edx ; PRI >= ALT ? (signed) mov eax,0 setge al GO_ON OP_EQ_C_PRI: cmp eax,[esi+4] ; PRI == value ? lea esi,[esi+8] mov eax,0 sete al GO_ON OP_EQ_C_ALT: xor eax,eax cmp edx,[esi+4] ; ALT == value ? lea esi,[esi+8] sete al GO_ON OP_INC_PRI: add esi,4 inc eax GO_ON OP_INC_ALT: add esi,4 inc edx GO_ON OP_INC: mov ebp,[esi+4] add esi,8 inc DWORD [edi+ebp] GO_ON ;good OP_INC_S: mov ebp,[esi+4] add esi,8 inc DWORD [ebx+ebp] GO_ON OP_INC_I: add esi,4 inc DWORD [edi+eax] GO_ON OP_DEC_PRI: add esi,4 dec eax GO_ON OP_DEC_ALT: add esi,4 dec edx GO_ON OP_DEC: mov ebp,[esi+4] add esi,8 dec DWORD [edi+ebp] GO_ON OP_DEC_S: mov ebp,[esi+4] add esi,8 dec DWORD [ebx+ebp] GO_ON OP_DEC_I: add esi,4 sub DWORD [edi+eax],1 GO_ON OP_MOVS: _VERIFYADDRESS eax ; PRI _VERIFYADDRESS edx ; ALT mov ebp,eax add ebp,[esi+4] dec ebp _VERIFYADDRESS ebp ; PRI + size - 1 mov ebp,edx add ebp,[esi+4] dec ebp _VERIFYADDRESS ebp ; ALT + size - 1 push ecx mov ecx,[esi+4] add esi,8 push edi push esi lea esi,[edi+eax] lea edi,[edi+edx] push ecx shr ecx,2 rep movsd pop ecx and ecx,3 rep movsb pop esi pop edi pop ecx GO_ON OP_CMPS: _VERIFYADDRESS eax ; PRI _VERIFYADDRESS edx ; ALT mov ebp,eax add ebp,[esi+4] ; size in bytes dec ebp ; EBP = PRI + size - 1 _VERIFYADDRESS ebp ; PRI + size - 1 sub ebp,eax ; EBP = size - 1 add ebp,edx ; EBP = ALT + size - 1 _VERIFYADDRESS ebp ; ALT + size - 1 push ecx mov ecx,[esi+4] add esi,8 push edi push esi lea esi,[edi+edx] lea edi,[edi+eax] xor eax,eax repe cmpsb je short cmps1 sbb eax,eax sbb eax,0ffffffffh cmps1: pop esi pop edi pop ecx GO_ON OP_FILL: mov ebp,[esi+4] ; get byte count add esi,8 and ebp,0fffffffch ; align to words jz short fill_ready _VERIFYADDRESS edx ; ALT dec ebp ; EBP = size - 1 add ebp,edx ; EBP = ALT + size - 1 _VERIFYADDRESS ebp ; ALT + size - 1 sub ebp,edx ; restore EBP inc ebp push ecx push edi mov ecx,ebp ; ECX = count (in bytes) lea edi,[edi+edx] ; EDI = physical starting address shr ecx,2 ; ECX = count (in DWORDS) rep stosd pop edi pop ecx fill_ready: GO_ON OP_HALT: cmp DWORD retval,0 je short halt_no_retval mov ebp,retval mov [ebp],eax halt_no_retval: ; store the complete status in the AMX mov ebp,amx ; get amx into ebp mov [ebp+_pri],eax ; store values in AMX structure (PRI, ALT, STK, HEA, FRM, ...) mov [ebp+_alt],edx mov [ebp+_stk],ecx mov ecx,hea mov ebx,frm mov [ebp+_hea],ecx mov [ebp+_frm],ebx ; EBX & ECX are invalid by now mov ebx,[esi+4] ; EBX=parameter of the HALT opcode add esi,8 ; skip this instruction mov eax,esi ; EAX=CIP sub eax,code mov [ebp+_cip],eax _ABORT ebx OP_BOUNDS: mov ebp,[esi+4] add esi,8 cmp eax,ebp ja near err_bounds ; use unsigned comparison, so <0 is >bounds GO_ON OP_SYSREQ_C: mov eax,[esi+4] ; get function number add esi,4 OP_SYSREQ_PRI: mov ebp,amx ; get amx into ebp add esi,4 mov stk,ecx ; save STK mov alt,edx ; save ALT mov [ebp+_stk],ecx ; store values in AMX structure (STK, HEA, FRM) ;we don't save regs since they're useless after this mov ecx,hea mov ebx,frm mov [ebp+_hea],ecx mov [ebp+_frm],ebx ; ebx & ecx are invalid by now mov ebx,esi ; also store CIP sub ebx,code mov [ebp+_cip],ebx mov edx,eax ; 2nd param: function number mov eax,ebp ; 1st param: amx mov ecx,stk lea ebx,pri ; 3rd param: addr. of retval add ecx,edi ; 4th param: addr. of function parameters ; save a few registers (it is not necessary to save them all ; and EAX should *not* be saved because it will hold the return ; value) push ebp push esi push edi _STK_ALIGN 16 ; align stack to 16-byte boundary and ; allocate 16 bytes of stack space ; push the parameters mov [esp+12], ecx mov [esp+08], ebx mov [esp+04], edx mov [esp], eax call [ebp+_callback] _STK_RESTORE ; restore stack pointer pop edi ; restore saved registers pop esi pop ebp _CHKABORT eax ; if result was invalid, leave mov eax,pri ; get retval into eax (PRI) mov edx,alt ; restore ALT mov ebx,frm mov ecx,stk ; restore STK add ebx,edi ; restore FRM GO_ON OP_SYSREQ_D: ; (TR) mov ebx,[esi+4] ; get function address mov ebp,amx ; get amx into ebp add esi,8 mov stk,ecx ; save STK mov alt,edx ; save ALT mov [ebp+_stk],ecx ; store values in AMX structure (STK, HEA, FRM) mov ecx,hea mov eax,frm mov [ebp+_hea],ecx mov [ebp+_frm],eax ; eax & ecx are invalid by now mov eax,ebp ; 1st param: amx mov edx,stk add edx,edi ; 2nd param: addr. of function parameters ; save a few registers (it is not necessary to save them all ; and EAX should *not* be saved because it will hold the return ; value) push ebp push esi push edi _STK_ALIGN 16 ; align stack to 16-byte boundary and ; allocate 16 bytes of stack space ; push the parameters mov [esp+04], edx mov [esp], eax call ebx ; direct call _STK_RESTORE ; restore stack pointer pop edi ; restore saved registers pop esi pop ebp mov eax,[ebp+_error] _CHKABORT eax ; function result is in eax (PRI) mov edx,alt ; restore ALT mov ebx,frm mov ecx,stk ; restore STK add ebx,edi ; restore FRM GO_ON OP_FILE: jmp OP_INVALID OP_LINE: add esi,12 GO_ON OP_SYMBOL: add esi,[esi+4] add esi,8 ; skip "fixed" part GO_ON OP_SRANGE: add esi,12 GO_ON OP_SYMTAG: add esi,8 GO_ON OP_JUMP_PRI: mov esi,eax GO_ON OP_SWITCH: push ecx mov ebp,[esi+4] ; get offset of the switch table add ebp,4 ; skip the "OP_CASETBL" opcode mov ecx,[ebp] ; ECX = number of records mov esi,[ebp+4] ; preset ESI to "none-matched" case op_switch_loop: or ecx, ecx ; number of records == 0? jz short op_switch_end ; yes, no more records, exit loop add ebp,8 ; skip previous record dec ecx ; already decrement cases to do cmp eax,[ebp] ; PRI == case label? jne short op_switch_loop ; no, continue loop mov esi,[ebp+4] ; yes, get jump address and exit loop op_switch_end: pop ecx GO_ON OP_CASETBL: jmp OP_INVALID OP_SWAP_PRI: mov ebp,[edi+ecx] add esi,4 mov [edi+ecx],eax mov eax,ebp GO_ON OP_SWAP_ALT: mov ebp,[edi+ecx] add esi,4 mov [edi+ecx],edx mov edx,ebp GO_ON OP_PUSHADDR: mov ebp,[esi+4] add esi,8 add ebp,frm _PUSH ebp GO_ON OP_NOP: add esi,4 GO_ON OP_FLOAT_MUL: add esi,4 fld dword [edi+ecx+4] fmul dword [edi+ecx+8] sub esp,4 fstp dword [esp] pop eax GO_ON OP_FLOAT_DIV: add esi,4 fld dword [edi+ecx+4] fdiv dword [edi+ecx+8] sub esp,4 fstp dword [esp] pop eax GO_ON OP_FLOAT_ADD: add esi,4 fld dword [edi+ecx+4] fadd dword [edi+ecx+8] sub esp,4 fstp dword [esp] pop eax GO_ON OP_FLOAT_SUB: add esi,4 fld dword [edi+ecx+4] fsub dword [edi+ecx+8] sub esp, 4 fstp dword [esp] pop eax GO_ON OP_FLOAT_TO: add esi,4 fild dword [edi+ecx+4] sub esp,4 fstp dword [esp] pop eax GO_ON OP_FLOAT_ROUND: add esi,4 ;get the float control word push 0 mov ebp,esp fstcw [ebp] mov eax,[ebp] push eax ;clear the top bits xor ah,ah ;get the control method push edx mov edx,[edi+ecx+8] and edx,3 ;sanity check shl edx,2 ;shift it to right position ;set the bits or ah,dl ;set bits 15,14 of FCW to rounding method or ah,3 ;set precision to 64bit mov [ebp], eax fldcw [ebp] ;calculate sub esp,4 fld dword [edi+ecx+4] test edx,edx jz .correct jmp .skip_correct .correct: fadd st0 fadd dword [g_round_nearest] fistp dword [esp] pop eax sar eax,1 jmp .done .skip_correct: frndint fistp dword [esp] pop eax .done: pop edx ;restore bits pop ebp mov [esp], ebp fldcw [esp] pop ebp GO_ON OP_FLOAT_CMP: add esi, 4 fld dword [edi+ecx+8] fld dword [edi+ecx+4] fucompp fnstsw ax fwait sahf cmovz eax, [g_flags+4] cmova eax, [g_flags+8] cmovb eax, [g_flags+0] GO_ON OP_BREAK: mov ebp,amx ; get amx into ebp add esi,4 cmp DWORD [ebp+_debug], 0 jnz break_calldebug GO_ON ; debug hook not active, ignore break_calldebug: ; store the status in the AMX (FRM, STK, HEA, CIP, and PRI + ALT) mov [ebp+_pri],eax mov [ebp+_alt],edx ; EAX and EDX are now free to use mov eax,frm mov edx,hea mov [ebp+_frm],eax ; store values in AMX structure (STK, FRM & HEA) mov [ebp+_hea],edx mov [ebp+_stk],ecx mov eax,esi sub eax,code ; EAX = CIP (relative to start of code segment) mov [ebp+_cip],eax ; call the debug hook mov eax,ebp ; 1st parm: amx _SAVEREGS _STK_ALIGN 16 ; align stack to 16-byte boundary and ; allocate 16 bytes of stack space mov [esp], eax call [ebp+_debug] ; call debug function _STK_RESTORE ; restore stack pointer cmp eax,AMX_ERR_NONE je short break_noabort; continue running mov [ebp+_error],eax ; save EAX (error code) before restoring all regs _RESTOREREGS ; abort run, but restore stack first mov eax,[ebp+_error] ; get error code in EAX back again _FASTABORT break_noabort: _RESTOREREGS mov eax,[ebp+_pri] ; restore PRI and ALT mov edx,[ebp+_alt] GO_ON OP_INVALID: _ABORT AMX_ERR_INVINSTR err_call: _ABORT AMX_ERR_CALLBACK err_stack: _ABORT AMX_ERR_STACKERR err_stacklow: _ABORT AMX_ERR_STACKLOW err_memaccess: _ABORT AMX_ERR_MEMACCESS err_bounds: _ABORT AMX_ERR_BOUNDS err_heaplow: _ABORT AMX_ERR_HEAPLOW err_divide: _ABORT AMX_ERR_DIVIDE _return: ; save a few parameters, mostly for the "sleep"function mov ebp,amx ; get amx into ebp mov [ebp+_cip],esi ; get corrected cip for amxmodx mov eax,[ebp+_error]; get error code pop esi ; remove FRM from stack pop ecx pop ebx pop edx pop esi ; remove pointer to amx from stack pop esi ; remove code segment pointer pop esi ; remove code size add esp,4*3 ; place for PRI, ALT & STK at SYSREQs pop ebp pop esi pop edi pop ebx ret ; _amx_exec_asm ENDP Start_DATA ALIGN 4 ; This is essential to avoid misalignment stalls. lodb_and DD 0ffh, 0ffffh, 0, 0ffffffffh g_round_nearest DD 0.5 GLOBAL g_flags g_flags: DD -1 DD 0 DD 1 GLOBAL amx_opcodelist GLOBAL _amx_opcodelist amx_opcodelist: _amx_opcodelist DD OP_INVALID DD OP_LOAD_PRI DD OP_LOAD_ALT DD OP_LOAD_S_PRI DD OP_LOAD_S_ALT DD OP_LREF_PRI DD OP_LREF_ALT DD OP_LREF_S_PRI DD OP_LREF_S_ALT DD OP_LOAD_I DD OP_LODB_I DD OP_CONST_PRI DD OP_CONST_ALT DD OP_ADDR_PRI DD OP_ADDR_ALT DD OP_STOR_PRI DD OP_STOR_ALT DD OP_STOR_S_PRI DD OP_STOR_S_ALT DD OP_SREF_PRI DD OP_SREF_ALT DD OP_SREF_S_PRI DD OP_SREF_S_ALT DD OP_STOR_I DD OP_STRB_I DD OP_LIDX DD OP_LIDX_B DD OP_IDXADDR DD OP_IDXADDR_B DD OP_ALIGN_PRI DD OP_ALIGN_ALT DD OP_LCTRL DD OP_SCTRL DD OP_MOVE_PRI DD OP_MOVE_ALT DD OP_XCHG DD OP_PUSH_PRI DD OP_PUSH_ALT DD OP_PUSH_R_PRI DD OP_PUSH_C DD OP_PUSH DD OP_PUSH_S DD OP_POP_PRI DD OP_POP_ALT DD OP_STACK DD OP_HEAP DD OP_PROC DD OP_RET DD OP_RETN DD OP_CALL DD OP_CALL_PRI DD OP_JUMP DD OP_JREL DD OP_JZER DD OP_JNZ DD OP_JEQ DD OP_JNEQ DD OP_JLESS DD OP_JLEQ DD OP_JGRTR DD OP_JGEQ DD OP_JSLESS DD OP_JSLEQ DD OP_JSGRTR DD OP_JSGEQ DD OP_SHL DD OP_SHR DD OP_SSHR DD OP_SHL_C_PRI DD OP_SHL_C_ALT DD OP_SHR_C_PRI DD OP_SHR_C_ALT DD OP_SMUL DD OP_SDIV DD OP_SDIV_ALT DD OP_UMUL DD OP_UDIV DD OP_UDIV_ALT DD OP_ADD DD OP_SUB DD OP_SUB_ALT DD OP_AND DD OP_OR DD OP_XOR DD OP_NOT DD OP_NEG DD OP_INVERT DD OP_ADD_C DD OP_SMUL_C DD OP_ZERO_PRI DD OP_ZERO_ALT DD OP_ZERO DD OP_ZERO_S DD OP_SIGN_PRI DD OP_SIGN_ALT DD OP_EQ DD OP_NEQ DD OP_LESS DD OP_LEQ DD OP_GRTR DD OP_GEQ DD OP_SLESS DD OP_SLEQ DD OP_SGRTR DD OP_SGEQ DD OP_EQ_C_PRI DD OP_EQ_C_ALT DD OP_INC_PRI DD OP_INC_ALT DD OP_INC DD OP_INC_S DD OP_INC_I DD OP_DEC_PRI DD OP_DEC_ALT DD OP_DEC DD OP_DEC_S DD OP_DEC_I DD OP_MOVS DD OP_CMPS DD OP_FILL DD OP_HALT DD OP_BOUNDS DD OP_SYSREQ_PRI DD OP_SYSREQ_C DD OP_FILE DD OP_LINE DD OP_SYMBOL DD OP_SRANGE DD OP_JUMP_PRI DD OP_SWITCH DD OP_CASETBL DD OP_SWAP_PRI DD OP_SWAP_ALT DD OP_PUSHADDR DD OP_NOP DD OP_SYSREQ_D DD OP_SYMTAG DD OP_BREAK DD OP_FLOAT_MUL DD OP_FLOAT_DIV DD OP_FLOAT_ADD DD OP_FLOAT_SUB DD OP_FLOAT_TO DD OP_FLOAT_ROUND DD OP_FLOAT_CMP