Sign in Join
FPDoc Onlineexperiment

Intel 80x86 Inline assembler

Intel syntax

Free Pascalsupports Intel syntax for the Intel family of Ix86 processors in its asm blocks.

The Intel syntax in your asm block is converted to AT&T syntax by the compiler, after which it is inserted in the compiled source. The supported assembler constructs are a subset of the normal assembly syntax. In what follows we specify what constructs are not supported in Free Pascal, but which exist in Turbo Pascal:

  • The TBYTE qualifier is not supported.

  • The & identifier override is not supported.

  • The HIGH operator is not supported.

  • The LOW operator is not supported.

  • The OFFSET and SEG operators are not supported. Use LEA and the various Lxx instructions instead.

  • Expressions with constant strings are not allowed.

  • Access to record fields via parenthesis is not allowed

  • Typecasts with normal pascal types are not allowed, only recognized assembler typecasts are allowed. Example:

    mov al, byte ptr MyWord       -- allowed,
    mov al, byte(MyWord)          -- allowed,
    mov al, shortint(MyWord)      -- not allowed.
  • Pascal type typecasts on constants are not allowed. Example:

    const s= 10; const t = 32767;

    in Turbo Pascal:

    mov al, byte(s)            -- useless typecast.
    mov al, byte(t)            -- syntax error!

    In Free Pascal the compiler emits a syntax error in both cases will give out a syntax error.

  • Expressions with constants only are not allowed (in all cases they do not work in protected mode, e.g. under Linuxi386). Examples:

    mov al,byte ptr ['c']      -- not allowed.
    mov  al,byte ptr [100h]    -- not allowed.

    (This is due to the limitation of the GNU Assembler).

  • Brackets within brackets are not allowed.

  • Expressions with segment overrides fully in brackets are currently not supported, but they can easily be implemented in the compiler if requested. Example:

    mov al,[ds:bx]     -- not allowed

    use instead:

    mov al,ds:[bx]
  • Possible allowed indexing are as follows:

    • Sreg:[REG+REG*SCALING+/-disp]

    • SReg:[REG+/-disp]

    • SReg:[REG]

    • SReg:[REG+REG+/-disp]

    • SReg:[REG+REG*SCALING]

    Where Sreg is optional and specifies the segment override. Notes:

    1. The order of terms is important contrary to Turbo Pascal.

    2. The Scaling value must be a value, and not an identifier to a symbol. Examples:

      const myscale = 1;
      ...
      mov al,byte ptr [esi+ebx*myscale] -- not allowed.

      use:

      mov al, byte ptr [esi+ebx*1]
  • Possible variable identifier syntax is as follows: (Id = Variable or typed constant identifier.)

    1. ID

    2. [ID]

    3. [ID+expr]

    4. ID[expr]

    Possible fields are as follows:

    1. ID.subfield.subfield …

    2. [ref].ID.subfield.subfield …

    3. [ref].typename.subfield …

  • Local labels: Contrary to Turbo Pascal, local labels, must at least contain one character after the local symbol indicator. Example:

    @:                -- not allowed

    use instead:

    @1:               -- allowed
  • Contrary to Turbo Pascal, local references cannot be used as references, only as displacements. Example:

    lds si,@mylabel   -- not allowed
  • Contrary to Turbo Pascal, SEGCS, SEGDS, SEGES and SEGSS segment overrides are presently not supported. (This is a planned addition though).

  • Contrary to Turbo Pascal where memory sizes specifiers can be practically anywhere, the Free PascalIntel inline assembler requires memory size specifiers to be outside the brackets. Example:

    mov al,[byte ptr myvar]    -- not allowed.

    use:

    mov al,byte ptr [myvar]    -- allowed.
  • Base and Index registers must be 32-bit registers. (limitation of the GNU Assembler).

  • XLAT is equivalent to XLATB.

  • Only Single and Double FPU opcodes are supported.

  • Floating point opcodes are currently not supported (except those which involve only floating point registers).

The Intel inline assembler supports the following macros:

@Result

represents the function result return value.

Self

represents the object method pointer in methods.

AT&T Syntax

In earlier versions, Free Pascalused only the GNUas assembler to generate its object files for the Intel x86 processors. Only after some time, an internal assembler was created, which wrote directly to an object file.

Since the GNUassembler uses AT&T assembly syntax, the code you write should use the same syntax. The differences between AT&T and Intel syntax as used in Turbo Pascalare summarized in the following:

  • The opcode names include the size of the operand. In general, one can say that the AT&T opcode name is the Intel opcode name, suffixed with a ’l’, ’w’ or ’b’ for, respectively, longint (32 bit), word (16 bit) and byte (8 bit) memory or register references. As an example, the Intel construct ’mov al, bl is equivalent to the AT&T style ’movb %bl,%al’ instruction.

  • AT&T immediate operands are designated with ’$’, while Intel syntax doesn’t use a prefix for immediate operands. Thus the Intel construct ’mov ax, 2’ becomes ’movb $2, %al’ in AT&T syntax.

  • AT&T register names are prefixed with a ’%’ sign. They are not delimited in Intel syntax.

  • AT&T indicates absolute jump/call operands with ’*’, Intel syntax doesn’t delimit these addresses.

  • The order of the source and destination operands is reversed. AT&T syntax uses ’Source, Dest’, while Intel syntax features ’Dest, Source’. Thus the Intel construct ’add eax, 4’ transforms to ’addl $4, %eax’ in the AT&T dialect.

  • Immediate long jumps are prefixed with the ’l’ prefix. Thus the Intel ’call/jmp section:offset' is transformed to ’lcall/ljmp $section,$offset’. Similarly, the far return is ’lret’, instead of the Intel ’ret far’.

  • Memory references are specified differently in AT&T and Intel assembly. The Intel indirect memory reference

    Segment:[Base + Index*Scale + Offs]

    is written in AT&T syntax as:

    Segment:Offs(Base,Index,Scale)

    Where Base and Index are optional 32-bit base and index registers, and Scale is used to multiply Index. It can take the values 1,2,4 and 8. The Segment is used to specify an optional segment register for the memory operand.

More information about the AT&T syntax can be found in the as manual, although the following differences with normal AT&T assembly must be taken into account:

  • Only the following directives are presently supported:

    .byte
    .word
    .long
    .ascii
    .asciz
    .globl
  • The following directives are recognized but are not supported:

    .align
    .lcomm

    Eventually they will be supported.

  • Directives are case sensitive, other identifiers are not case sensitive.

  • Contrary to gas, local labels/symbols must start with .L.

  • The not operator '!' is not supported.

  • String expressions in operands are not supported.

  • CBTW,CWTL,CWTD and CLTD are not supported, use the normal Intel equivalents instead.

  • Constant expressions which represent memory references are not allowed, even though constant immediate value expressions are supported. Examples:

    const myid = 10;
    ...
    movl $myid,%eax       -- allowed
    movl myid(%esi),%eax  -- not allowed.
  • When the .globl directive is found, the symbol immediately following it is made public and is immediately emitted. Therefore label names with this name will be ignored.

  • Only Single and Double FPU opcodes are supported.

The AT&T inline assembler supports the following macros:

__RESULT

represents the function result return value.

__SELF

represents the object method pointer in methods.

__OLDEBP

represents the old base pointer in recursive routines.

An experiment, built from development snapshots. Not the official Free Pascal or Lazarus documentation.