diff options
Diffstat (limited to 'compiler/x86/aasmcpu.pas')
-rw-r--r-- | compiler/x86/aasmcpu.pas | 2155 |
1 files changed, 2155 insertions, 0 deletions
diff --git a/compiler/x86/aasmcpu.pas b/compiler/x86/aasmcpu.pas new file mode 100644 index 0000000000..68ebd7238a --- /dev/null +++ b/compiler/x86/aasmcpu.pas @@ -0,0 +1,2155 @@ +{ + Copyright (c) 1998-2002 by Florian Klaempfl and Peter Vreman + + Contains the abstract assembler implementation for the i386 + + * Portions of this code was inspired by the NASM sources + The Netwide Assembler is Copyright (c) 1996 Simon Tatham and + Julian Hall. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + **************************************************************************** +} +unit aasmcpu; + +{$i fpcdefs.inc} + +interface + + uses + globtype,globals,verbose, + cpubase, + cgbase,cgutils, + symtype, + aasmbase,aasmtai; + + const + { "mov reg,reg" source operand number } + O_MOV_SOURCE = 0; + { "mov reg,reg" destination operand number } + O_MOV_DEST = 1; + + { Operand types } + OT_NONE = $00000000; + + OT_BITS8 = $00000001; { size, and other attributes, of the operand } + OT_BITS16 = $00000002; + OT_BITS32 = $00000004; + OT_BITS64 = $00000008; { FPU only } + OT_BITS80 = $00000010; + OT_FAR = $00000020; { this means 16:16 or 16:32, like in CALL/JMP } + OT_NEAR = $00000040; + OT_SHORT = $00000080; + + OT_SIZE_MASK = $000000FF; { all the size attributes } + OT_NON_SIZE = longint(not OT_SIZE_MASK); + + OT_SIGNED = $00000100; { the operand need to be signed -128-127 } + + OT_TO = $00000200; { operand is followed by a colon } + { reverse effect in FADD, FSUB &c } + OT_COLON = $00000400; + + OT_REGISTER = $00001000; + OT_IMMEDIATE = $00002000; + OT_IMM8 = $00002001; + OT_IMM16 = $00002002; + OT_IMM32 = $00002004; + OT_IMM64 = $00002008; + OT_IMM80 = $00002010; + OT_REGMEM = $00200000; { for r/m, ie EA, operands } + OT_REGNORM = $00201000; { 'normal' reg, qualifies as EA } + OT_REG8 = $00201001; + OT_REG16 = $00201002; + OT_REG32 = $00201004; + OT_REG64 = $00201008; + OT_MMXREG = $00201008; { MMX registers } + OT_XMMREG = $00201010; { Katmai registers } + OT_MEMORY = $00204000; { register number in 'basereg' } + OT_MEM8 = $00204001; + OT_MEM16 = $00204002; + OT_MEM32 = $00204004; + OT_MEM64 = $00204008; + OT_MEM80 = $00204010; + OT_FPUREG = $01000000; { floating point stack registers } + OT_FPU0 = $01000800; { FPU stack register zero } + OT_REG_SMASK = $00070000; { special register operands: these may be treated differently } + { a mask for the following } + OT_REG_ACCUM = $00211000; { FUNCTION_RETURN_REG: AL, AX or EAX } + OT_REG_AL = $00211001; { REG_ACCUM | BITSxx } + OT_REG_AX = $00211002; { ditto } + OT_REG_EAX = $00211004; { and again } +{$ifdef x86_64} + OT_REG_RAX = $00211008; +{$endif x86_64} + OT_REG_COUNT = $00221000; { counter: CL, CX or ECX } + OT_REG_CL = $00221001; { REG_COUNT | BITSxx } + OT_REG_CX = $00221002; { ditto } + OT_REG_ECX = $00221004; { another one } +{$ifdef x86_64} + OT_REG_RCX = $00221008; +{$endif x86_64} + OT_REG_DX = $00241002; + OT_REG_EDX = $00241004; + + OT_REG_SREG = $00081002; { any segment register } + OT_REG_CS = $01081002; { CS } + OT_REG_DESS = $02081002; { DS, ES, SS (non-CS 86 registers) } + OT_REG_FSGS = $04081002; { FS, GS (386 extended registers) } + + OT_REG_CDT = $00101004; { CRn, DRn and TRn } + OT_REG_CREG = $08101004; { CRn } + OT_REG_CR4 = $08101404; { CR4 (Pentium only) } + OT_REG_DREG = $10101004; { DRn } + OT_REG_TREG = $20101004; { TRn } + + OT_MEM_OFFS = $00604000; { special type of EA } + { simple [address] offset } + OT_ONENESS = $00800000; { special type of immediate operand } + { so UNITY == IMMEDIATE | ONENESS } + OT_UNITY = $00802000; { for shift/rotate instructions } + + { Size of the instruction table converted by nasmconv.pas } +{$ifdef x86_64} + instabentries = {$i x8664nop.inc} +{$else x86_64} + instabentries = {$i i386nop.inc} +{$endif x86_64} + maxinfolen = 8; + MaxInsChanges = 3; { Max things a instruction can change } + + type + { What an instruction can change. Needed for optimizer and spilling code. + + Note: The order of this enumeration is should not be changed! } + TInsChange = (Ch_None, + {Read from a register} + Ch_REAX, Ch_RECX, Ch_REDX, Ch_REBX, Ch_RESP, Ch_REBP, Ch_RESI, Ch_REDI, + {write from a register} + Ch_WEAX, Ch_WECX, Ch_WEDX, Ch_WEBX, Ch_WESP, Ch_WEBP, Ch_WESI, Ch_WEDI, + {read and write from/to a register} + Ch_RWEAX, Ch_RWECX, Ch_RWEDX, Ch_RWEBX, Ch_RWESP, Ch_RWEBP, Ch_RWESI, Ch_RWEDI, + {modify the contents of a register with the purpose of using + this changed content afterwards (add/sub/..., but e.g. not rep + or movsd)} + Ch_MEAX, Ch_MECX, Ch_MEDX, Ch_MEBX, Ch_MESP, Ch_MEBP, Ch_MESI, Ch_MEDI, + Ch_CDirFlag {clear direction flag}, Ch_SDirFlag {set dir flag}, + Ch_RFlags, Ch_WFlags, Ch_RWFlags, Ch_FPU, + Ch_Rop1, Ch_Wop1, Ch_RWop1,Ch_Mop1, + Ch_Rop2, Ch_Wop2, Ch_RWop2,Ch_Mop2, + Ch_Rop3, Ch_WOp3, Ch_RWOp3,Ch_Mop3, + Ch_WMemEDI, + Ch_All, + { x86_64 registers } + Ch_RRAX, Ch_RRCX, Ch_RRDX, Ch_RRBX, Ch_RRSP, Ch_RRBP, Ch_RRSI, Ch_RRDI, + Ch_WRAX, Ch_WRCX, Ch_WRDX, Ch_WRBX, Ch_WRSP, Ch_WRBP, Ch_WRSI, Ch_WRDI, + Ch_RWRAX, Ch_RWRCX, Ch_RWRDX, Ch_RWRBX, Ch_RWRSP, Ch_RWRBP, Ch_RWRSI, Ch_RWRDI, + Ch_MRAX, Ch_MRCX, Ch_MRDX, Ch_MRBX, Ch_MRSP, Ch_MRBP, Ch_MRSI, Ch_MRDI + ); + + TInsProp = packed record + Ch : Array[1..MaxInsChanges] of TInsChange; + end; + + const + InsProp : array[tasmop] of TInsProp = +{$ifdef x86_64} + {$i x8664pro.inc} +{$else x86_64} + {$i i386prop.inc} +{$endif x86_64} + + type + TOperandOrder = (op_intel,op_att); + + tinsentry=packed record + opcode : tasmop; + ops : byte; + optypes : array[0..2] of longint; + code : array[0..maxinfolen] of char; + flags : longint; + end; + pinsentry=^tinsentry; + + { alignment for operator } + tai_align = class(tai_align_abstract) + reg : tregister; + constructor create(b:byte);override; + constructor create_op(b: byte; _op: byte);override; + function calculatefillbuf(var buf : tfillbuffer):pchar;override; + end; + + taicpu = class(tai_cpu_abstract) + opsize : topsize; + constructor op_none(op : tasmop); + constructor op_none(op : tasmop;_size : topsize); + + constructor op_reg(op : tasmop;_size : topsize;_op1 : tregister); + constructor op_const(op : tasmop;_size : topsize;_op1 : aint); + constructor op_ref(op : tasmop;_size : topsize;const _op1 : treference); + + constructor op_reg_reg(op : tasmop;_size : topsize;_op1,_op2 : tregister); + constructor op_reg_ref(op : tasmop;_size : topsize;_op1 : tregister;const _op2 : treference); + constructor op_reg_const(op:tasmop; _size: topsize; _op1: tregister; _op2: aint); + + constructor op_const_reg(op : tasmop;_size : topsize;_op1 : aint;_op2 : tregister); + constructor op_const_const(op : tasmop;_size : topsize;_op1,_op2 : aint); + constructor op_const_ref(op : tasmop;_size : topsize;_op1 : aint;const _op2 : treference); + + constructor op_ref_reg(op : tasmop;_size : topsize;const _op1 : treference;_op2 : tregister); + + constructor op_reg_reg_reg(op : tasmop;_size : topsize;_op1,_op2,_op3 : tregister); + constructor op_const_reg_reg(op : tasmop;_size : topsize;_op1 : aint;_op2 : tregister;_op3 : tregister); + constructor op_const_ref_reg(op : tasmop;_size : topsize;_op1 : aint;const _op2 : treference;_op3 : tregister); + constructor op_reg_reg_ref(op : tasmop;_size : topsize;_op1,_op2 : tregister; const _op3 : treference); + constructor op_const_reg_ref(op : tasmop;_size : topsize;_op1 : aint;_op2 : tregister;const _op3 : treference); + + { this is for Jmp instructions } + constructor op_cond_sym(op : tasmop;cond:TAsmCond;_size : topsize;_op1 : tasmsymbol); + + constructor op_sym(op : tasmop;_size : topsize;_op1 : tasmsymbol); + constructor op_sym_ofs(op : tasmop;_size : topsize;_op1 : tasmsymbol;_op1ofs:longint); + constructor op_sym_ofs_reg(op : tasmop;_size : topsize;_op1 : tasmsymbol;_op1ofs:longint;_op2 : tregister); + constructor op_sym_ofs_ref(op : tasmop;_size : topsize;_op1 : tasmsymbol;_op1ofs:longint;const _op2 : treference); + + procedure changeopsize(siz:topsize); + + function GetString:string; + procedure CheckNonCommutativeOpcodes; + private + FOperandOrder : TOperandOrder; + procedure init(_size : topsize); { this need to be called by all constructor } + {$ifndef NOAG386BIN} + public + { the next will reset all instructions that can change in pass 2 } + procedure ResetPass1; + procedure ResetPass2; + function CheckIfValid:boolean; + function Pass1(offset:longint):longint;override; + procedure Pass2(objdata:TAsmObjectdata);override; + procedure SetOperandOrder(order:TOperandOrder); + function is_same_reg_move(regtype: Tregistertype):boolean;override; + { register spilling code } + function spilling_get_operation_type(opnr: longint): topertype;override; + protected + procedure ppuloadoper(ppufile:tcompilerppufile;var o:toper);override; + procedure ppuwriteoper(ppufile:tcompilerppufile;const o:toper);override; + procedure ppubuildderefimploper(var o:toper);override; + procedure ppuderefoper(var o:toper);override; + private + { next fields are filled in pass1, so pass2 is faster } + inssize : shortint; + insoffset : longint; + LastInsOffset : longint; { need to be public to be reset } + insentry : PInsEntry; + function InsEnd:longint; + procedure create_ot; + function Matches(p:PInsEntry):longint; + function calcsize(p:PInsEntry):shortint; + procedure gencode(objdata:TAsmObjectData); + function NeedAddrPrefix(opidx:byte):boolean; + procedure Swapoperands; + function FindInsentry:boolean; + {$endif NOAG386BIN} + end; + + function spilling_create_load(const ref:treference;r:tregister): tai; + function spilling_create_store(r:tregister; const ref:treference): tai; + + procedure InitAsm; + procedure DoneAsm; + + +implementation + + uses + cutils, + itcpugas, + symsym; + +{***************************************************************************** + Instruction table +*****************************************************************************} + + const + {Instruction flags } + IF_NONE = $00000000; + IF_SM = $00000001; { size match first two operands } + IF_SM2 = $00000002; + IF_SB = $00000004; { unsized operands can't be non-byte } + IF_SW = $00000008; { unsized operands can't be non-word } + IF_SD = $00000010; { unsized operands can't be nondword } + IF_AR0 = $00000020; { SB, SW, SD applies to argument 0 } + IF_AR1 = $00000040; { SB, SW, SD applies to argument 1 } + IF_AR2 = $00000060; { SB, SW, SD applies to argument 2 } + IF_ARMASK = $00000060; { mask for unsized argument spec } + IF_PRIV = $00000100; { it's a privileged instruction } + IF_SMM = $00000200; { it's only valid in SMM } + IF_PROT = $00000400; { it's protected mode only } + IF_NOX86_64 = $00000800; { removed instruction in x86_64 } + IF_UNDOC = $00001000; { it's an undocumented instruction } + IF_FPU = $00002000; { it's an FPU instruction } + IF_MMX = $00004000; { it's an MMX instruction } + { it's a 3DNow! instruction } + IF_3DNOW = $00008000; + { it's a SSE (KNI, MMX2) instruction } + IF_SSE = $00010000; + { SSE2 instructions } + IF_SSE2 = $00020000; + { SSE3 instructions } + IF_SSE3 = $00040000; + { SSE64 instructions } + IF_SSE64 = $00080000; + { the mask for processor types } + {IF_PMASK = longint($FF000000);} + { the mask for disassembly "prefer" } + {IF_PFMASK = longint($F001FF00);} + IF_8086 = $00000000; { 8086 instruction } + IF_186 = $01000000; { 186+ instruction } + IF_286 = $02000000; { 286+ instruction } + IF_386 = $03000000; { 386+ instruction } + IF_486 = $04000000; { 486+ instruction } + IF_PENT = $05000000; { Pentium instruction } + IF_P6 = $06000000; { P6 instruction } + IF_KATMAI = $07000000; { Katmai instructions } + { Willamette instructions } + IF_WILLAMETTE = $08000000; + { Prescott instructions } + IF_PRESCOTT = $09000000; + IF_X86_64 = $0a000000; + IF_CYRIX = $10000000; { Cyrix-specific instruction } + IF_AMD = $20000000; { AMD-specific instruction } + { added flags } + IF_PRE = $40000000; { it's a prefix instruction } + IF_PASS2 = longint($80000000); { if the instruction can change in a second pass } + + type + TInsTabCache=array[TasmOp] of longint; + PInsTabCache=^TInsTabCache; + + const +{$ifdef x86_64} + InsTab:array[0..instabentries-1] of TInsEntry={$i x8664tab.inc} +{$else x86_64} + InsTab:array[0..instabentries-1] of TInsEntry={$i i386tab.inc} +{$endif x86_64} + var + InsTabCache : PInsTabCache; + + const +{$ifdef x86_64} + { Intel style operands ! } + opsize_2_type:array[0..2,topsize] of longint=( + (OT_NONE, + OT_BITS8,OT_BITS16,OT_BITS32,OT_BITS64,OT_BITS16,OT_BITS32,OT_BITS32,OT_BITS64,OT_BITS64,OT_BITS64, + OT_BITS16,OT_BITS32,OT_BITS64, + OT_BITS32,OT_BITS64,OT_BITS80,OT_BITS64,OT_NONE, + OT_BITS64, + OT_NEAR,OT_FAR,OT_SHORT, + OT_NONE, + OT_NONE + ), + (OT_NONE, + OT_BITS8,OT_BITS16,OT_BITS32,OT_BITS64,OT_BITS8,OT_BITS8,OT_BITS16,OT_BITS8,OT_BITS16,OT_BITS32, + OT_BITS16,OT_BITS32,OT_BITS64, + OT_BITS32,OT_BITS64,OT_BITS80,OT_BITS64,OT_NONE, + OT_BITS64, + OT_NEAR,OT_FAR,OT_SHORT, + OT_NONE, + OT_NONE + ), + (OT_NONE, + OT_BITS8,OT_BITS16,OT_BITS32,OT_BITS64,OT_NONE,OT_NONE,OT_NONE,OT_NONE,OT_NONE,OT_NONE, + OT_BITS16,OT_BITS32,OT_BITS64, + OT_BITS32,OT_BITS64,OT_BITS80,OT_BITS64,OT_NONE, + OT_BITS64, + OT_NEAR,OT_FAR,OT_SHORT, + OT_NONE, + OT_NONE + ) + ); + + reg_ot_table : array[tregisterindex] of longint = ( + {$i r8664ot.inc} + ); +{$else x86_64} + { Intel style operands ! } + opsize_2_type:array[0..2,topsize] of longint=( + (OT_NONE, + OT_BITS8,OT_BITS16,OT_BITS32,OT_BITS64,OT_BITS16,OT_BITS32,OT_BITS32, + OT_BITS16,OT_BITS32,OT_BITS64, + OT_BITS32,OT_BITS64,OT_BITS80,OT_BITS64,OT_NONE, + OT_BITS64, + OT_NEAR,OT_FAR,OT_SHORT, + OT_NONE, + OT_NONE + ), + (OT_NONE, + OT_BITS8,OT_BITS16,OT_BITS32,OT_BITS64,OT_BITS8,OT_BITS8,OT_BITS16, + OT_BITS16,OT_BITS32,OT_BITS64, + OT_BITS32,OT_BITS64,OT_BITS80,OT_BITS64,OT_NONE, + OT_BITS64, + OT_NEAR,OT_FAR,OT_SHORT, + OT_NONE, + OT_NONE + ), + (OT_NONE, + OT_BITS8,OT_BITS16,OT_BITS32,OT_BITS64,OT_NONE,OT_NONE,OT_NONE, + OT_BITS16,OT_BITS32,OT_BITS64, + OT_BITS32,OT_BITS64,OT_BITS80,OT_BITS64,OT_NONE, + OT_BITS64, + OT_NEAR,OT_FAR,OT_SHORT, + OT_NONE, + OT_NONE + ) + ); + + reg_ot_table : array[tregisterindex] of longint = ( + {$i r386ot.inc} + ); +{$endif x86_64} + + { Operation type for spilling code } + type + toperation_type_table=array[tasmop,0..Max_Operands] of topertype; + var + operation_type_table : ^toperation_type_table; + + +{**************************************************************************** + TAI_ALIGN + ****************************************************************************} + + constructor tai_align.create(b: byte); + begin + inherited create(b); + reg:=NR_ECX; + end; + + + constructor tai_align.create_op(b: byte; _op: byte); + begin + inherited create_op(b,_op); + reg:=NR_NO; + end; + + + function tai_align.calculatefillbuf(var buf : tfillbuffer):pchar; + const + alignarray:array[0..5] of string[8]=( + #$8D#$B4#$26#$00#$00#$00#$00, + #$8D#$B6#$00#$00#$00#$00, + #$8D#$74#$26#$00, + #$8D#$76#$00, + #$89#$F6, + #$90 + ); + var + bufptr : pchar; + j : longint; + begin + inherited calculatefillbuf(buf); + if not use_op then + begin + bufptr:=pchar(@buf); + while (fillsize>0) do + begin + for j:=0 to 5 do + if (fillsize>=length(alignarray[j])) then + break; + move(alignarray[j][1],bufptr^,length(alignarray[j])); + inc(bufptr,length(alignarray[j])); + dec(fillsize,length(alignarray[j])); + end; + end; + calculatefillbuf:=pchar(@buf); + end; + + +{***************************************************************************** + Taicpu Constructors +*****************************************************************************} + + procedure taicpu.changeopsize(siz:topsize); + begin + opsize:=siz; + end; + + + procedure taicpu.init(_size : topsize); + begin + { default order is att } + FOperandOrder:=op_att; + segprefix:=NR_NO; + opsize:=_size; +{$ifndef NOAG386BIN} + insentry:=nil; + LastInsOffset:=-1; + InsOffset:=0; + InsSize:=0; +{$endif} + end; + + + constructor taicpu.op_none(op : tasmop); + begin + inherited create(op); + init(S_NO); + end; + + + constructor taicpu.op_none(op : tasmop;_size : topsize); + begin + inherited create(op); + init(_size); + end; + + + constructor taicpu.op_reg(op : tasmop;_size : topsize;_op1 : tregister); + begin + inherited create(op); + init(_size); + ops:=1; + loadreg(0,_op1); + end; + + + constructor taicpu.op_const(op : tasmop;_size : topsize;_op1 : aint); + begin + inherited create(op); + init(_size); + ops:=1; + loadconst(0,_op1); + end; + + + constructor taicpu.op_ref(op : tasmop;_size : topsize;const _op1 : treference); + begin + inherited create(op); + init(_size); + ops:=1; + loadref(0,_op1); + end; + + + constructor taicpu.op_reg_reg(op : tasmop;_size : topsize;_op1,_op2 : tregister); + begin + inherited create(op); + init(_size); + ops:=2; + loadreg(0,_op1); + loadreg(1,_op2); + end; + + + constructor taicpu.op_reg_const(op:tasmop; _size: topsize; _op1: tregister; _op2: aint); + begin + inherited create(op); + init(_size); + ops:=2; + loadreg(0,_op1); + loadconst(1,_op2); + end; + + + constructor taicpu.op_reg_ref(op : tasmop;_size : topsize;_op1 : tregister;const _op2 : treference); + begin + inherited create(op); + init(_size); + ops:=2; + loadreg(0,_op1); + loadref(1,_op2); + end; + + + constructor taicpu.op_const_reg(op : tasmop;_size : topsize;_op1 : aint;_op2 : tregister); + begin + inherited create(op); + init(_size); + ops:=2; + loadconst(0,_op1); + loadreg(1,_op2); + end; + + + constructor taicpu.op_const_const(op : tasmop;_size : topsize;_op1,_op2 : aint); + begin + inherited create(op); + init(_size); + ops:=2; + loadconst(0,_op1); + loadconst(1,_op2); + end; + + + constructor taicpu.op_const_ref(op : tasmop;_size : topsize;_op1 : aint;const _op2 : treference); + begin + inherited create(op); + init(_size); + ops:=2; + loadconst(0,_op1); + loadref(1,_op2); + end; + + + constructor taicpu.op_ref_reg(op : tasmop;_size : topsize;const _op1 : treference;_op2 : tregister); + begin + inherited create(op); + init(_size); + ops:=2; + loadref(0,_op1); + loadreg(1,_op2); + end; + + + constructor taicpu.op_reg_reg_reg(op : tasmop;_size : topsize;_op1,_op2,_op3 : tregister); + begin + inherited create(op); + init(_size); + ops:=3; + loadreg(0,_op1); + loadreg(1,_op2); + loadreg(2,_op3); + end; + + + constructor taicpu.op_const_reg_reg(op : tasmop;_size : topsize;_op1 : aint;_op2 : tregister;_op3 : tregister); + begin + inherited create(op); + init(_size); + ops:=3; + loadconst(0,_op1); + loadreg(1,_op2); + loadreg(2,_op3); + end; + + + constructor taicpu.op_reg_reg_ref(op : tasmop;_size : topsize;_op1,_op2 : tregister;const _op3 : treference); + begin + inherited create(op); + init(_size); + ops:=3; + loadreg(0,_op1); + loadreg(1,_op2); + loadref(2,_op3); + end; + + + constructor taicpu.op_const_ref_reg(op : tasmop;_size : topsize;_op1 : aint;const _op2 : treference;_op3 : tregister); + begin + inherited create(op); + init(_size); + ops:=3; + loadconst(0,_op1); + loadref(1,_op2); + loadreg(2,_op3); + end; + + + constructor taicpu.op_const_reg_ref(op : tasmop;_size : topsize;_op1 : aint;_op2 : tregister;const _op3 : treference); + begin + inherited create(op); + init(_size); + ops:=3; + loadconst(0,_op1); + loadreg(1,_op2); + loadref(2,_op3); + end; + + + constructor taicpu.op_cond_sym(op : tasmop;cond:TAsmCond;_size : topsize;_op1 : tasmsymbol); + begin + inherited create(op); + init(_size); + condition:=cond; + ops:=1; + loadsymbol(0,_op1,0); + end; + + + constructor taicpu.op_sym(op : tasmop;_size : topsize;_op1 : tasmsymbol); + begin + inherited create(op); + init(_size); + ops:=1; + loadsymbol(0,_op1,0); + end; + + + constructor taicpu.op_sym_ofs(op : tasmop;_size : topsize;_op1 : tasmsymbol;_op1ofs:longint); + begin + inherited create(op); + init(_size); + ops:=1; + loadsymbol(0,_op1,_op1ofs); + end; + + + constructor taicpu.op_sym_ofs_reg(op : tasmop;_size : topsize;_op1 : tasmsymbol;_op1ofs:longint;_op2 : tregister); + begin + inherited create(op); + init(_size); + ops:=2; + loadsymbol(0,_op1,_op1ofs); + loadreg(1,_op2); + end; + + + constructor taicpu.op_sym_ofs_ref(op : tasmop;_size : topsize;_op1 : tasmsymbol;_op1ofs:longint;const _op2 : treference); + begin + inherited create(op); + init(_size); + ops:=2; + loadsymbol(0,_op1,_op1ofs); + loadref(1,_op2); + end; + + + function taicpu.GetString:string; + var + i : longint; + s : string; + addsize : boolean; + begin + s:='['+std_op2str[opcode]; + for i:=0 to ops-1 do + begin + with oper[i]^ do + begin + if i=0 then + s:=s+' ' + else + s:=s+','; + { type } + addsize:=false; + if (ot and OT_XMMREG)=OT_XMMREG then + s:=s+'xmmreg' + else + if (ot and OT_MMXREG)=OT_MMXREG then + s:=s+'mmxreg' + else + if (ot and OT_FPUREG)=OT_FPUREG then + s:=s+'fpureg' + else + if (ot and OT_REGISTER)=OT_REGISTER then + begin + s:=s+'reg'; + addsize:=true; + end + else + if (ot and OT_IMMEDIATE)=OT_IMMEDIATE then + begin + s:=s+'imm'; + addsize:=true; + end + else + if (ot and OT_MEMORY)=OT_MEMORY then + begin + s:=s+'mem'; + addsize:=true; + end + else + s:=s+'???'; + { size } + if addsize then + begin + if (ot and OT_BITS8)<>0 then + s:=s+'8' + else + if (ot and OT_BITS16)<>0 then + s:=s+'16' + else + if (ot and OT_BITS32)<>0 then + s:=s+'32' + else + s:=s+'??'; + { signed } + if (ot and OT_SIGNED)<>0 then + s:=s+'s'; + end; + end; + end; + GetString:=s+']'; + end; + + + procedure taicpu.Swapoperands; + var + p : POper; + begin + { Fix the operands which are in AT&T style and we need them in Intel style } + case ops of + 2 : begin + { 0,1 -> 1,0 } + p:=oper[0]; + oper[0]:=oper[1]; + oper[1]:=p; + end; + 3 : begin + { 0,1,2 -> 2,1,0 } + p:=oper[0]; + oper[0]:=oper[2]; + oper[2]:=p; + end; + end; + end; + + + procedure taicpu.SetOperandOrder(order:TOperandOrder); + begin + if FOperandOrder<>order then + begin + Swapoperands; + FOperandOrder:=order; + end; + end; + + + procedure taicpu.ppuloadoper(ppufile:tcompilerppufile;var o:toper); + begin + o.typ:=toptype(ppufile.getbyte); + o.ot:=ppufile.getlongint; + case o.typ of + top_reg : + ppufile.getdata(o.reg,sizeof(Tregister)); + top_ref : + begin + new(o.ref); + ppufile.getdata(o.ref^.segment,sizeof(Tregister)); + ppufile.getdata(o.ref^.base,sizeof(Tregister)); + ppufile.getdata(o.ref^.index,sizeof(Tregister)); + o.ref^.scalefactor:=ppufile.getbyte; + o.ref^.offset:=ppufile.getaint; + o.ref^.symbol:=ppufile.getasmsymbol; + o.ref^.relsymbol:=ppufile.getasmsymbol; + end; + top_const : + o.val:=ppufile.getaint; + top_local : + begin + new(o.localoper); + with o.localoper^ do + begin + ppufile.getderef(localsymderef); + localsymofs:=ppufile.getaint; + localindexreg:=tregister(ppufile.getlongint); + localscale:=ppufile.getbyte; + localgetoffset:=(ppufile.getbyte<>0); + end; + end; + end; + end; + + + procedure taicpu.ppuwriteoper(ppufile:tcompilerppufile;const o:toper); + begin + ppufile.putbyte(byte(o.typ)); + ppufile.putlongint(o.ot); + case o.typ of + top_reg : + ppufile.putdata(o.reg,sizeof(Tregister)); + top_ref : + begin + ppufile.putdata(o.ref^.segment,sizeof(Tregister)); + ppufile.putdata(o.ref^.base,sizeof(Tregister)); + ppufile.putdata(o.ref^.index,sizeof(Tregister)); + ppufile.putbyte(o.ref^.scalefactor); + ppufile.putaint(o.ref^.offset); + ppufile.putasmsymbol(o.ref^.symbol); + ppufile.putasmsymbol(o.ref^.relsymbol); + end; + top_const : + ppufile.putaint(o.val); + top_local : + begin + with o.localoper^ do + begin + ppufile.putderef(localsymderef); + ppufile.putaint(localsymofs); + ppufile.putlongint(longint(localindexreg)); + ppufile.putbyte(localscale); + ppufile.putbyte(byte(localgetoffset)); + end; + end; + end; + end; + + + procedure taicpu.ppubuildderefimploper(var o:toper); + begin + case o.typ of + top_local : + o.localoper^.localsymderef.build(tlocalvarsym(o.localoper^.localsym)); + end; + end; + + + procedure taicpu.ppuderefoper(var o:toper); + begin + case o.typ of + top_ref : + begin + if assigned(o.ref^.symbol) then + objectlibrary.derefasmsymbol(o.ref^.symbol); + if assigned(o.ref^.relsymbol) then + objectlibrary.derefasmsymbol(o.ref^.relsymbol); + end; + top_local : + o.localoper^.localsym:=tlocalvarsym(o.localoper^.localsymderef.resolve); + end; + end; + + + procedure taicpu.CheckNonCommutativeOpcodes; + begin + { we need ATT order } + SetOperandOrder(op_att); + + if ( + (ops=2) and + (oper[0]^.typ=top_reg) and + (oper[1]^.typ=top_reg) and + { if the first is ST and the second is also a register + it is necessarily ST1 .. ST7 } + ((oper[0]^.reg=NR_ST) or + (oper[0]^.reg=NR_ST0)) + ) or + { ((ops=1) and + (oper[0]^.typ=top_reg) and + (oper[0]^.reg in [R_ST1..R_ST7])) or} + (ops=0) then + begin + if opcode=A_FSUBR then + opcode:=A_FSUB + else if opcode=A_FSUB then + opcode:=A_FSUBR + else if opcode=A_FDIVR then + opcode:=A_FDIV + else if opcode=A_FDIV then + opcode:=A_FDIVR + else if opcode=A_FSUBRP then + opcode:=A_FSUBP + else if opcode=A_FSUBP then + opcode:=A_FSUBRP + else if opcode=A_FDIVRP then + opcode:=A_FDIVP + else if opcode=A_FDIVP then + opcode:=A_FDIVRP; + end; + if ( + (ops=1) and + (oper[0]^.typ=top_reg) and + (getregtype(oper[0]^.reg)=R_FPUREGISTER) and + (oper[0]^.reg<>NR_ST) + ) then + begin + if opcode=A_FSUBRP then + opcode:=A_FSUBP + else if opcode=A_FSUBP then + opcode:=A_FSUBRP + else if opcode=A_FDIVRP then + opcode:=A_FDIVP + else if opcode=A_FDIVP then + opcode:=A_FDIVRP; + end; + end; + + +{***************************************************************************** + Assembler +*****************************************************************************} + +{$ifndef NOAG386BIN} + + type + ea=packed record + sib_present : boolean; + bytes : byte; + size : byte; + modrm : byte; + sib : byte; + end; + + procedure taicpu.create_ot; + { + this function will also fix some other fields which only needs to be once + } + var + i,l,relsize : longint; + begin + if ops=0 then + exit; + { update oper[].ot field } + for i:=0 to ops-1 do + with oper[i]^ do + begin + case typ of + top_reg : + begin + ot:=reg_ot_table[findreg_by_number(reg)]; + end; + top_ref : + begin + if ref^.refaddr=addr_no then + begin + { create ot field } + if (ot and OT_SIZE_MASK)=0 then + ot:=OT_MEMORY or opsize_2_type[i,opsize] + else + ot:=OT_MEMORY or (ot and OT_SIZE_MASK); + if (ref^.base=NR_NO) and (ref^.index=NR_NO) then + ot:=ot or OT_MEM_OFFS; + { fix scalefactor } + if (ref^.index=NR_NO) then + ref^.scalefactor:=0 + else + if (ref^.scalefactor=0) then + ref^.scalefactor:=1; + end + else + begin + l:=ref^.offset; + if assigned(ref^.symbol) then + inc(l,ref^.symbol.address); + { when it is a forward jump we need to compensate the + offset of the instruction since the previous time, + because the symbol address is then still using the + 'old-style' addressing. + For backwards jumps this is not required because the + address of the symbol is already adjusted to the + new offset } + if (l>InsOffset) and (LastInsOffset<>-1) then + inc(l,InsOffset-LastInsOffset); + { instruction size will then always become 2 (PFV) } + relsize:=(InsOffset+2)-l; + if (not assigned(ref^.symbol) or + ((ref^.symbol.currbind<>AB_EXTERNAL) and (ref^.symbol.address<>0))) and + (relsize>=-128) and (relsize<=127) then + ot:=OT_IMM32 or OT_SHORT + else + ot:=OT_IMM32 or OT_NEAR; + end; + end; + top_local : + begin + if (ot and OT_SIZE_MASK)=0 then + ot:=OT_MEMORY or opsize_2_type[i,opsize] + else + ot:=OT_MEMORY or (ot and OT_SIZE_MASK); + end; + top_const : + begin + if opsize=S_NO then + message(asmr_e_invalid_opcode_and_operand); + if (opsize<>S_W) and (longint(val)>=-128) and (val<=127) then + ot:=OT_IMM8 or OT_SIGNED + else + ot:=OT_IMMEDIATE or opsize_2_type[i,opsize]; + end; + top_none : + begin + { generated when there was an error in the + assembler reader. It never happends when generating + assembler } + end; + else + internalerror(200402261); + end; + end; + end; + + + function taicpu.InsEnd:longint; + begin + InsEnd:=InsOffset+InsSize; + end; + + + function taicpu.Matches(p:PInsEntry):longint; + { * IF_SM stands for Size Match: any operand whose size is not + * explicitly specified by the template is `really' intended to be + * the same size as the first size-specified operand. + * Non-specification is tolerated in the input instruction, but + * _wrong_ specification is not. + * + * IF_SM2 invokes Size Match on only the first _two_ operands, for + * three-operand instructions such as SHLD: it implies that the + * first two operands must match in size, but that the third is + * required to be _unspecified_. + * + * IF_SB invokes Size Byte: operands with unspecified size in the + * template are really bytes, and so no non-byte specification in + * the input instruction will be tolerated. IF_SW similarly invokes + * Size Word, and IF_SD invokes Size Doubleword. + * + * (The default state if neither IF_SM nor IF_SM2 is specified is + * that any operand with unspecified size in the template is + * required to have unspecified size in the instruction too...) + } + var + i,j,asize,oprs : longint; + siz : array[0..2] of longint; + begin + Matches:=100; + + { Check the opcode and operands } + if (p^.opcode<>opcode) or (p^.ops<>ops) then + begin + Matches:=0; + exit; + end; + + { Check that no spurious colons or TOs are present } + for i:=0 to p^.ops-1 do + if (oper[i]^.ot and (not p^.optypes[i]) and (OT_COLON or OT_TO))<>0 then + begin + Matches:=0; + exit; + end; + + { Check that the operand flags all match up } + for i:=0 to p^.ops-1 do + begin + if ((p^.optypes[i] and (not oper[i]^.ot)) or + ((p^.optypes[i] and OT_SIZE_MASK) and + ((p^.optypes[i] xor oper[i]^.ot) and OT_SIZE_MASK)))<>0 then + begin + if ((p^.optypes[i] and (not oper[i]^.ot) and OT_NON_SIZE) or + (oper[i]^.ot and OT_SIZE_MASK))<>0 then + begin + Matches:=0; + exit; + end + else + Matches:=1; + end; + end; + + { Check operand sizes } + { as default an untyped size can get all the sizes, this is different + from nasm, but else we need to do a lot checking which opcodes want + size or not with the automatic size generation } + asize:=longint($ffffffff); + if (p^.flags and IF_SB)<>0 then + asize:=OT_BITS8 + else if (p^.flags and IF_SW)<>0 then + asize:=OT_BITS16 + else if (p^.flags and IF_SD)<>0 then + asize:=OT_BITS32; + if (p^.flags and IF_ARMASK)<>0 then + begin + siz[0]:=0; + siz[1]:=0; + siz[2]:=0; + if (p^.flags and IF_AR0)<>0 then + siz[0]:=asize + else if (p^.flags and IF_AR1)<>0 then + siz[1]:=asize + else if (p^.flags and IF_AR2)<>0 then + siz[2]:=asize; + end + else + begin + { we can leave because the size for all operands is forced to be + the same + but not if IF_SB IF_SW or IF_SD is set PM } + if asize=-1 then + exit; + siz[0]:=asize; + siz[1]:=asize; + siz[2]:=asize; + end; + + if (p^.flags and (IF_SM or IF_SM2))<>0 then + begin + if (p^.flags and IF_SM2)<>0 then + oprs:=2 + else + oprs:=p^.ops; + for i:=0 to oprs-1 do + if ((p^.optypes[i] and OT_SIZE_MASK) <> 0) then + begin + for j:=0 to oprs-1 do + siz[j]:=p^.optypes[i] and OT_SIZE_MASK; + break; + end; + end + else + oprs:=2; + + { Check operand sizes } + for i:=0 to p^.ops-1 do + begin + if ((p^.optypes[i] and OT_SIZE_MASK)=0) and + ((oper[i]^.ot and OT_SIZE_MASK and (not siz[i]))<>0) and + { Immediates can always include smaller size } + ((oper[i]^.ot and OT_IMMEDIATE)=0) and + (((p^.optypes[i] and OT_SIZE_MASK) or siz[i])<(oper[i]^.ot and OT_SIZE_MASK)) then + Matches:=2; + end; + end; + + + procedure taicpu.ResetPass1; + begin + { we need to reset everything here, because the choosen insentry + can be invalid for a new situation where the previously optimized + insentry is not correct } + InsEntry:=nil; + InsSize:=0; + LastInsOffset:=-1; + end; + + + procedure taicpu.ResetPass2; + begin + { we are here in a second pass, check if the instruction can be optimized } + if assigned(InsEntry) and + ((InsEntry^.flags and IF_PASS2)<>0) then + begin + InsEntry:=nil; + InsSize:=0; + end; + LastInsOffset:=-1; + end; + + + function taicpu.CheckIfValid:boolean; + begin + result:=FindInsEntry; + end; + + + function taicpu.FindInsentry:boolean; + var + i : longint; + begin + result:=false; + { Things which may only be done once, not when a second pass is done to + optimize } + if (Insentry=nil) or ((InsEntry^.flags and IF_PASS2)<>0) then + begin + { We need intel style operands } + SetOperandOrder(op_intel); + { create the .ot fields } + create_ot; + { set the file postion } + aktfilepos:=fileinfo; + end + else + begin + { we've already an insentry so it's valid } + result:=true; + exit; + end; + { Lookup opcode in the table } + InsSize:=-1; + i:=instabcache^[opcode]; + if i=-1 then + begin + Message1(asmw_e_opcode_not_in_table,gas_op2str[opcode]); + exit; + end; + insentry:=@instab[i]; + while (insentry^.opcode=opcode) do + begin + if matches(insentry)=100 then + begin + result:=true; + exit; + end; + inc(i); + insentry:=@instab[i]; + end; + Message1(asmw_e_invalid_opcode_and_operands,GetString); + { No instruction found, set insentry to nil and inssize to -1 } + insentry:=nil; + inssize:=-1; + end; + + + function taicpu.Pass1(offset:longint):longint; + begin + Pass1:=0; + { Save the old offset and set the new offset } + InsOffset:=Offset; + { Error? } + if (Insentry=nil) and (InsSize=-1) then + exit; + { set the file postion } + aktfilepos:=fileinfo; + { Get InsEntry } + if FindInsEntry then + begin + { Calculate instruction size } + InsSize:=calcsize(insentry); + if segprefix<>NR_NO then + inc(InsSize); + { Fix opsize if size if forced } + if (insentry^.flags and (IF_SB or IF_SW or IF_SD))<>0 then + begin + if (insentry^.flags and IF_ARMASK)=0 then + begin + if (insentry^.flags and IF_SB)<>0 then + begin + if opsize=S_NO then + opsize:=S_B; + end + else if (insentry^.flags and IF_SW)<>0 then + begin + if opsize=S_NO then + opsize:=S_W; + end + else if (insentry^.flags and IF_SD)<>0 then + begin + if opsize=S_NO then + opsize:=S_L; + end; + end; + end; + LastInsOffset:=InsOffset; + Pass1:=InsSize; + exit; + end; + LastInsOffset:=-1; + end; + + + procedure taicpu.Pass2(objdata:TAsmObjectData); + var + c : longint; + begin + { error in pass1 ? } + if insentry=nil then + exit; + aktfilepos:=fileinfo; + { Segment override } + if (segprefix<>NR_NO) then + begin + case segprefix of + NR_CS : c:=$2e; + NR_DS : c:=$3e; + NR_ES : c:=$26; + NR_FS : c:=$64; + NR_GS : c:=$65; + NR_SS : c:=$36; + end; + objdata.writebytes(c,1); + { fix the offset for GenNode } + inc(InsOffset); + end; + { Generate the instruction } + GenCode(objdata); + end; + + + function taicpu.needaddrprefix(opidx:byte):boolean; + begin + result:=(oper[opidx]^.typ=top_ref) and + (oper[opidx]^.ref^.refaddr=addr_no) and + ( + ( + (oper[opidx]^.ref^.index<>NR_NO) and + (getsubreg(oper[opidx]^.ref^.index)<>R_SUBD) + ) or + ( + (oper[opidx]^.ref^.base<>NR_NO) and + (getsubreg(oper[opidx]^.ref^.base)<>R_SUBD) + ) + ); + end; + + + function regval(r:Tregister):byte; + const + {$ifdef x86_64} + opcode_table:array[tregisterindex] of tregisterindex = ( + {$i r8664op.inc} + ); + {$else x86_64} + opcode_table:array[tregisterindex] of tregisterindex = ( + {$i r386op.inc} + ); + {$endif x86_64} + var + regidx : tregisterindex; + begin + regidx:=findreg_by_number(r); + if regidx<>0 then + result:=opcode_table[regidx] + else + begin + Message1(asmw_e_invalid_register,generic_regname(r)); + result:=0; + end; + end; + + + function process_ea(const input:toper;var output:ea;rfield:longint):boolean; + var + sym : tasmsymbol; + md,s,rv : byte; + base,index,scalefactor, + o : longint; + ir,br : Tregister; + isub,bsub : tsubregister; + begin + process_ea:=false; + {Register ?} + if (input.typ=top_reg) then + begin + rv:=regval(input.reg); + output.sib_present:=false; + output.bytes:=0; + output.modrm:=$c0 or (rfield shl 3) or rv; + output.size:=1; + process_ea:=true; + exit; + end; + {No register, so memory reference.} + if (input.typ<>top_ref) then + internalerror(200409262); + if ((input.ref^.index<>NR_NO) and (getregtype(input.ref^.index)<>R_INTREGISTER)) or + ((input.ref^.base<>NR_NO) and (getregtype(input.ref^.base)<>R_INTREGISTER)) then + internalerror(200301081); + ir:=input.ref^.index; + br:=input.ref^.base; + isub:=getsubreg(ir); + bsub:=getsubreg(br); + s:=input.ref^.scalefactor; + o:=input.ref^.offset; + sym:=input.ref^.symbol; + { it's direct address } + if (br=NR_NO) and (ir=NR_NO) then + begin + { it's a pure offset } + output.sib_present:=false; + output.bytes:=4; + output.modrm:=5 or (rfield shl 3); + end + else + { it's an indirection } + begin + { 16 bit address? } + if ((ir<>NR_NO) and (isub<>R_SUBD)) or + ((br<>NR_NO) and (bsub<>R_SUBD)) then + message(asmw_e_16bit_not_supported); +{$ifdef OPTEA} + { make single reg base } + if (br=NR_NO) and (s=1) then + begin + br:=ir; + ir:=NR_NO; + end; + { convert [3,5,9]*EAX to EAX+[2,4,8]*EAX } + if (br=NR_NO) and + (((s=2) and (ir<>NR_ESP)) or + (s=3) or (s=5) or (s=9)) then + begin + br:=ir; + dec(s); + end; + { swap ESP into base if scalefactor is 1 } + if (s=1) and (ir=NR_ESP) then + begin + ir:=br; + br:=NR_ESP; + end; +{$endif OPTEA} + { wrong, for various reasons } + if (ir=NR_ESP) or ((s<>1) and (s<>2) and (s<>4) and (s<>8) and (ir<>NR_NO)) then + exit; + { base } + case br of + NR_EAX : base:=0; + NR_ECX : base:=1; + NR_EDX : base:=2; + NR_EBX : base:=3; + NR_ESP : base:=4; + NR_NO, + NR_EBP : base:=5; + NR_ESI : base:=6; + NR_EDI : base:=7; + else + exit; + end; + { index } + case ir of + NR_EAX : index:=0; + NR_ECX : index:=1; + NR_EDX : index:=2; + NR_EBX : index:=3; + NR_NO : index:=4; + NR_EBP : index:=5; + NR_ESI : index:=6; + NR_EDI : index:=7; + else + exit; + end; + case s of + 0, + 1 : scalefactor:=0; + 2 : scalefactor:=1; + 4 : scalefactor:=2; + 8 : scalefactor:=3; + else + exit; + end; + if (br=NR_NO) or + ((br<>NR_EBP) and (o=0) and (sym=nil)) then + md:=0 + else + if ((o>=-128) and (o<=127) and (sym=nil)) then + md:=1 + else + md:=2; + if (br=NR_NO) or (md=2) then + output.bytes:=4 + else + output.bytes:=md; + { SIB needed ? } + if (ir=NR_NO) and (br<>NR_ESP) then + begin + output.sib_present:=false; + output.modrm:=(md shl 6) or (rfield shl 3) or base; + end + else + begin + output.sib_present:=true; + output.modrm:=(md shl 6) or (rfield shl 3) or 4; + output.sib:=(scalefactor shl 6) or (index shl 3) or base; + end; + end; + if output.sib_present then + output.size:=2+output.bytes + else + output.size:=1+output.bytes; + process_ea:=true; + end; + + + function taicpu.calcsize(p:PInsEntry):shortint; + var + codes : pchar; + c : byte; + len : shortint; + ea_data : ea; + begin + len:=0; + codes:=@p^.code; + repeat + c:=ord(codes^); + inc(codes); + case c of + 0 : + break; + 1,2,3 : + begin + inc(codes,c); + inc(len,c); + end; + 8,9,10 : + begin + inc(codes); + inc(len); + end; + 4,5,6,7 : + begin + if opsize=S_W then + inc(len,2) + else + inc(len); + end; + 15, + 12,13,14, + 16,17,18, + 20,21,22, + 40,41,42 : + inc(len); + 24,25,26, + 31, + 48,49,50 : + inc(len,2); + 28,29,30, { we don't have 16 bit immediates code } + 32,33,34, + 52,53,54, + 56,57,58 : + inc(len,4); + 192,193,194 : + if NeedAddrPrefix(c-192) then + inc(len); + 208, + 210 : + inc(len); + 200, + 201, + 202, + 209, + 211, + 217,218: ; + 219,220 : + inc(len); + 216 : + begin + inc(codes); + inc(len); + end; + 224,225,226 : + begin + InternalError(777002); + end; + else + begin + if (c>=64) and (c<=191) then + begin + if not process_ea(oper[(c shr 3) and 7]^, ea_data, 0) then + Message(asmw_e_invalid_effective_address) + else + inc(len,ea_data.size); + end + else + InternalError(777003); + end; + end; + until false; + calcsize:=len; + end; + + + procedure taicpu.GenCode(objdata:TAsmObjectData); + { + * the actual codes (C syntax, i.e. octal): + * \0 - terminates the code. (Unless it's a literal of course.) + * \1, \2, \3 - that many literal bytes follow in the code stream + * \4, \6 - the POP/PUSH (respectively) codes for CS, DS, ES, SS + * (POP is never used for CS) depending on operand 0 + * \5, \7 - the second byte of POP/PUSH codes for FS, GS, depending + * on operand 0 + * \10, \11, \12 - a literal byte follows in the code stream, to be added + * to the register value of operand 0, 1 or 2 + * \17 - encodes the literal byte 0. (Some compilers don't take + * kindly to a zero byte in the _middle_ of a compile time + * string constant, so I had to put this hack in.) + * \14, \15, \16 - a signed byte immediate operand, from operand 0, 1 or 2 + * \20, \21, \22 - a byte immediate operand, from operand 0, 1 or 2 + * \24, \25, \26 - an unsigned byte immediate operand, from operand 0, 1 or 2 + * \30, \31, \32 - a word immediate operand, from operand 0, 1 or 2 + * \34, \35, \36 - select between \3[012] and \4[012] depending on 16/32 bit + * assembly mode or the address-size override on the operand + * \37 - a word constant, from the _segment_ part of operand 0 + * \40, \41, \42 - a long immediate operand, from operand 0, 1 or 2 + * \50, \51, \52 - a byte relative operand, from operand 0, 1 or 2 + * \60, \61, \62 - a word relative operand, from operand 0, 1 or 2 + * \64, \65, \66 - select between \6[012] and \7[012] depending on 16/32 bit + * assembly mode or the address-size override on the operand + * \70, \71, \72 - a long relative operand, from operand 0, 1 or 2 + * \1ab - a ModRM, calculated on EA in operand a, with the spare + * field the register value of operand b. + * \2ab - a ModRM, calculated on EA in operand a, with the spare + * field equal to digit b. + * \30x - might be an 0x67 byte, depending on the address size of + * the memory reference in operand x. + * \310 - indicates fixed 16-bit address size, i.e. optional 0x67. + * \311 - indicates fixed 32-bit address size, i.e. optional 0x67. + * \312 - indicates fixed 64-bit address size, i.e. optional 0x48. + * \320 - indicates fixed 16-bit operand size, i.e. optional 0x66. + * \321 - indicates fixed 32-bit operand size, i.e. optional 0x66. + * \322 - indicates fixed 64-bit operand size, i.e. optional 0x48. + * \323 - indicates that this instruction is only valid when the + * operand size is the default (instruction to disassembler, + * generates no code in the assembler) + * \330 - a literal byte follows in the code stream, to be added + * to the condition code value of the instruction. + * \340 - reserve <operand 0> bytes of uninitialised storage. + * Operand 0 had better be a segmentless constant. + } + + var + currval : longint; + currsym : tasmsymbol; + + procedure getvalsym(opidx:longint); + begin + case oper[opidx]^.typ of + top_ref : + begin + currval:=oper[opidx]^.ref^.offset; + currsym:=oper[opidx]^.ref^.symbol; + end; + top_const : + begin + currval:=longint(oper[opidx]^.val); + currsym:=nil; + end; + else + Message(asmw_e_immediate_or_reference_expected); + end; + end; + + const + CondVal:array[TAsmCond] of byte=($0, + $7, $3, $2, $6, $2, $4, $F, $D, $C, $E, $6, $2, + $3, $7, $3, $5, $E, $C, $D, $F, $1, $B, $9, $5, + $0, $A, $A, $B, $8, $4); + var + c : byte; + pb, + codes : pchar; + bytes : array[0..3] of byte; + rfield, + data,s,opidx : longint; + ea_data : ea; + begin +{$ifdef EXTDEBUG} + { safety check } + if objdata.currsec.datasize<>insoffset then + internalerror(200130121); +{$endif EXTDEBUG} + { load data to write } + codes:=insentry^.code; + { Force word push/pop for registers } + if (opsize=S_W) and ((codes[0]=#4) or (codes[0]=#6) or + ((codes[0]=#1) and ((codes[2]=#5) or (codes[2]=#7)))) then + begin + bytes[0]:=$66; + objdata.writebytes(bytes,1); + end; + repeat + c:=ord(codes^); + inc(codes); + case c of + 0 : + break; + 1,2,3 : + begin + objdata.writebytes(codes^,c); + inc(codes,c); + end; + 4,6 : + begin + case oper[0]^.reg of + NR_CS: + bytes[0]:=$e; + NR_NO, + NR_DS: + bytes[0]:=$1e; + NR_ES: + bytes[0]:=$6; + NR_SS: + bytes[0]:=$16; + else + internalerror(777004); + end; + if c=4 then + inc(bytes[0]); + objdata.writebytes(bytes,1); + end; + 5,7 : + begin + case oper[0]^.reg of + NR_FS: + bytes[0]:=$a0; + NR_GS: + bytes[0]:=$a8; + else + internalerror(777005); + end; + if c=5 then + inc(bytes[0]); + objdata.writebytes(bytes,1); + end; + 8,9,10 : + begin + bytes[0]:=ord(codes^)+regval(oper[c-8]^.reg); + inc(codes); + objdata.writebytes(bytes,1); + end; + 15 : + begin + bytes[0]:=0; + objdata.writebytes(bytes,1); + end; + 12,13,14 : + begin + getvalsym(c-12); + if (currval<-128) or (currval>127) then + Message2(asmw_e_value_exceeds_bounds,'signed byte',tostr(currval)); + if assigned(currsym) then + objdata.writereloc(currval,1,currsym,RELOC_ABSOLUTE) + else + objdata.writebytes(currval,1); + end; + 16,17,18 : + begin + getvalsym(c-16); + if (currval<-256) or (currval>255) then + Message2(asmw_e_value_exceeds_bounds,'byte',tostr(currval)); + if assigned(currsym) then + objdata.writereloc(currval,1,currsym,RELOC_ABSOLUTE) + else + objdata.writebytes(currval,1); + end; + 20,21,22 : + begin + getvalsym(c-20); + if (currval<0) or (currval>255) then + Message2(asmw_e_value_exceeds_bounds,'unsigned byte',tostr(currval)); + if assigned(currsym) then + objdata.writereloc(currval,1,currsym,RELOC_ABSOLUTE) + else + objdata.writebytes(currval,1); + end; + 24,25,26 : + begin + getvalsym(c-24); + if (currval<-65536) or (currval>65535) then + Message2(asmw_e_value_exceeds_bounds,'word',tostr(currval)); + if assigned(currsym) then + objdata.writereloc(currval,2,currsym,RELOC_ABSOLUTE) + else + objdata.writebytes(currval,2); + end; + 28,29,30 : + begin + getvalsym(c-28); + if assigned(currsym) then + objdata.writereloc(currval,4,currsym,RELOC_ABSOLUTE) + else + objdata.writebytes(currval,4); + end; + 32,33,34 : + begin + getvalsym(c-32); + if assigned(currsym) then + objdata.writereloc(currval,4,currsym,RELOC_ABSOLUTE) + else + objdata.writebytes(currval,4); + end; + 40,41,42 : + begin + getvalsym(c-40); + data:=currval-insend; + if assigned(currsym) then + inc(data,currsym.address); + if (data>127) or (data<-128) then + Message1(asmw_e_short_jmp_out_of_range,tostr(data)); + objdata.writebytes(data,1); + end; + 52,53,54 : + begin + getvalsym(c-52); + if assigned(currsym) then + objdata.writereloc(currval,4,currsym,RELOC_RELATIVE) + else + objdata.writereloc(currval-insend,4,nil,RELOC_ABSOLUTE) + end; + 56,57,58 : + begin + getvalsym(c-56); + if assigned(currsym) then + objdata.writereloc(currval,4,currsym,RELOC_RELATIVE) + else + objdata.writereloc(currval-insend,4,nil,RELOC_ABSOLUTE) + end; + 192,193,194 : + begin + if NeedAddrPrefix(c-192) then + begin + bytes[0]:=$67; + objdata.writebytes(bytes,1); + end; + end; + 200 : + begin + bytes[0]:=$67; + objdata.writebytes(bytes,1); + end; + 208 : + begin + bytes[0]:=$66; + objdata.writebytes(bytes,1); + end; + 210 : + begin + bytes[0]:=$48; + objdata.writebytes(bytes,1); + end; + 216 : + begin + bytes[0]:=ord(codes^)+condval[condition]; + inc(codes); + objdata.writebytes(bytes,1); + end; + 201, + 202, + 209, + 211, + 217,218 : + begin + { these are dissambler hints or 32 bit prefixes which + are not needed } + end; + 219 : + begin + bytes[0]:=$f3; + objdata.writebytes(bytes,1); + end; + 220 : + begin + bytes[0]:=$f2; + objdata.writebytes(bytes,1); + end; + 31, + 48,49,50, + 224,225,226 : + begin + InternalError(777006); + end + else + begin + if (c>=64) and (c<=191) then + begin + if (c<127) then + begin + if (oper[c and 7]^.typ=top_reg) then + rfield:=regval(oper[c and 7]^.reg) + else + rfield:=regval(oper[c and 7]^.ref^.base); + end + else + rfield:=c and 7; + opidx:=(c shr 3) and 7; + if not process_ea(oper[opidx]^,ea_data,rfield) then + Message(asmw_e_invalid_effective_address); + + pb:=@bytes; + pb^:=chr(ea_data.modrm); + inc(pb); + if ea_data.sib_present then + begin + pb^:=chr(ea_data.sib); + inc(pb); + end; + + s:=pb-pchar(@bytes); + objdata.writebytes(bytes,s); + + case ea_data.bytes of + 0 : ; + 1 : + begin + if (oper[opidx]^.ot and OT_MEMORY)=OT_MEMORY then + objdata.writereloc(oper[opidx]^.ref^.offset,1,oper[opidx]^.ref^.symbol,RELOC_ABSOLUTE) + else + begin + bytes[0]:=oper[opidx]^.ref^.offset; + objdata.writebytes(bytes,1); + end; + inc(s); + end; + 2,4 : + begin + objdata.writereloc(oper[opidx]^.ref^.offset,ea_data.bytes, + oper[opidx]^.ref^.symbol,RELOC_ABSOLUTE); + inc(s,ea_data.bytes); + end; + end; + end + else + InternalError(777007); + end; + end; + until false; + end; +{$endif NOAG386BIN} + + + function taicpu.is_same_reg_move(regtype: Tregistertype):boolean; + begin + result:=(((opcode=A_MOV) or (opcode=A_XCHG)) and + (regtype = R_INTREGISTER) and + (ops=2) and + (oper[0]^.typ=top_reg) and + (oper[1]^.typ=top_reg) and + (oper[0]^.reg=oper[1]^.reg) + ) or + (((opcode=A_MOVSS) or (opcode=A_MOVSD)) and + (regtype = R_MMREGISTER) and + (ops=2) and + (oper[0]^.typ=top_reg) and + (oper[1]^.typ=top_reg) and + (oper[0]^.reg=oper[1]^.reg) + ); + end; + + + procedure build_spilling_operation_type_table; + var + opcode : tasmop; + i : integer; + begin + new(operation_type_table); + fillchar(operation_type_table^,sizeof(toperation_type_table),byte(operand_read)); + for opcode:=low(tasmop) to high(tasmop) do + begin + for i:=1 to MaxInsChanges do + begin + case InsProp[opcode].Ch[i] of + Ch_Rop1 : + operation_type_table^[opcode,0]:=operand_read; + Ch_Wop1 : + operation_type_table^[opcode,0]:=operand_write; + Ch_RWop1, + Ch_Mop1 : + operation_type_table^[opcode,0]:=operand_readwrite; + Ch_Rop2 : + operation_type_table^[opcode,1]:=operand_read; + Ch_Wop2 : + operation_type_table^[opcode,1]:=operand_write; + Ch_RWop2, + Ch_Mop2 : + operation_type_table^[opcode,1]:=operand_readwrite; + Ch_Rop3 : + operation_type_table^[opcode,2]:=operand_read; + Ch_Wop3 : + operation_type_table^[opcode,2]:=operand_write; + Ch_RWop3, + Ch_Mop3 : + operation_type_table^[opcode,2]:=operand_readwrite; + end; + end; + end; + { Special cases that can't be decoded from the InsChanges flags } + operation_type_table^[A_IMUL,1]:=operand_readwrite; + end; + + + function taicpu.spilling_get_operation_type(opnr: longint): topertype; + begin + { the information in the instruction table is made for the string copy + operation MOVSD so hack here (FK) + } + if (opcode=A_MOVSD) and (ops=2) then + begin + case opnr of + 0: + result:=operand_read; + 1: + result:=operand_write; + else + internalerror(200506055); + end + end + else + result:=operation_type_table^[opcode,opnr]; + end; + + + function spilling_create_load(const ref:treference;r:tregister): tai; + begin + case getregtype(r) of + R_INTREGISTER : + result:=taicpu.op_ref_reg(A_MOV,reg2opsize(r),ref,r); + R_MMREGISTER : + case getsubreg(r) of + R_SUBMMD: + result:=taicpu.op_ref_reg(A_MOVSD,reg2opsize(r),ref,r); + R_SUBMMS: + result:=taicpu.op_ref_reg(A_MOVSS,reg2opsize(r),ref,r); + else + internalerror(200506043); + end; + else + internalerror(200401041); + end; + end; + + + function spilling_create_store(r:tregister; const ref:treference): tai; + begin + case getregtype(r) of + R_INTREGISTER : + result:=taicpu.op_reg_ref(A_MOV,reg2opsize(r),r,ref); + R_MMREGISTER : + case getsubreg(r) of + R_SUBMMD: + result:=taicpu.op_reg_ref(A_MOVSD,reg2opsize(r),r,ref); + R_SUBMMS: + result:=taicpu.op_reg_ref(A_MOVSS,reg2opsize(r),r,ref); + else + internalerror(200506042); + end; + else + internalerror(200401041); + end; + end; + + +{***************************************************************************** + Instruction table +*****************************************************************************} + + procedure BuildInsTabCache; +{$ifndef NOAG386BIN} + var + i : longint; +{$endif} + begin +{$ifndef NOAG386BIN} + new(instabcache); + FillChar(instabcache^,sizeof(tinstabcache),$ff); + i:=0; + while (i<InsTabEntries) do + begin + if InsTabCache^[InsTab[i].OPcode]=-1 then + InsTabCache^[InsTab[i].OPcode]:=i; + inc(i); + end; +{$endif NOAG386BIN} + end; + + + procedure InitAsm; + begin + build_spilling_operation_type_table; +{$ifndef NOAG386BIN} + if not assigned(instabcache) then + BuildInsTabCache; +{$endif NOAG386BIN} + end; + + + procedure DoneAsm; + begin + if assigned(operation_type_table) then + begin + dispose(operation_type_table); + operation_type_table:=nil; + end; +{$ifndef NOAG386BIN} + if assigned(instabcache) then + begin + dispose(instabcache); + instabcache:=nil; + end; +{$endif NOAG386BIN} + end; + + +begin + cai_align:=tai_align; + cai_cpu:=taicpu; +end. |