X86 Assembly/Data Transfer

From Wikibooks, open books for an open world
Jump to: navigation, search

Some of the most important and most frequently used instructions are those that move data. Without them, there would be no way for registers or memory to even have anything in them to operate on.

Contents

Data transfer instructions [edit]

Move [edit]

mov src, dest GAS Syntax
mov dest, src Intel syntax


Move

The mov instruction copies the src operand into the dest operand.

Operands

src

  • Immediate
  • Register
  • Memory

dest

  • Register
  • Memory

Modified flags

  • No FLAGS are modified by this instruction

Example

 .data
 
 value:
         .long   2
 
 .text
         .global _start
 
 _start:
         movl    $6, %eax
         # %eax is now 6
 
         movw    %ax, value
         # value is now 6
 
         movl    $0, %ebx
         # %ebx is now 0
 
         movb    %al, %bl
         # %ebx is now 6
 
         movl    value, %ebx
         # %ebx is now 6
 
         movl    $value, %esi
         # %esi is now the address of value
 
         xorl    %ebx, %ebx
         # %ebx is now 0
 
         movw    value(, %ebx, 1), %bx
         # %ebx is now 6
 
 # Linux sys_exit
         mov     $1, %eax
         xorl    %ebx, %ebx
         int     $0x80

Data Swap [edit]

xchg src, dest GAS Syntax
xchg dest, src Intel syntax


Exchange

The xchg instruction swaps the src operand with the dest operand.

If one of the operands is a memory address, then the operation has an implicit LOCK prefix, that is, the exchange operation is atomic. This can have a large performance penalty.

It's also worth noting that the common NOP (no op) instruction, 0x90, is the opcode for xchgl %eax, %eax.

Operands

src

  • Register
  • Memory

dest

  • Register
  • Memory

However, note that only one operand can be in memory: at least one has to be a register.

Modified flags

  • No FLAGS are modified by this instruction

Example

 .data
 
 value:
        .long   2
 
 .text
        .global _start
 
 _start:
        movl    $54, %ebx
        xorl    %eax, %eax
 
        xchgl   value, %ebx
        # %ebx is now 2
        # value is now 54
 
        xchgw   %ax, value
        # Value is now 0
        # %eax is now 54
 
        xchgb   %al, %bl
        # %ebx is now 54
        # %eax is now 2
 
        xchgw   value(%eax), %ax
        # value is now 0x00020000 = 131072
        # %eax is now 0
 
 # Linux sys_exit 
        mov     $1, %eax
        xorl    %ebx, %ebx
        int     $0x80


cmpxchg arg2, arg1 GAS Syntax
cmpxchg arg1, arg2 Intel syntax


Compare and Exchange

The cmpxchg instruction has two implicit operands AL/AX/EAX(depending on the size of arg1) and ZF(zero) flag. The instruction compares arg1 to AL/AX/EAX and if they are equal sets arg1 to arg2 and sets the zero flag otherwise it sets AL/AX/EAX to arg2 and clears the zero flag. Unlike xchg there is not an implicit lock prefix and if the instruction is required to be atomic then lock must be prefixed.

Operands

arg1

  • Register
  • Memory

arg2

  • Register

Modified flags

  • The ZF flag is modified by this instruction

Example

The following example shows how to use the cmpxchg instruction to create a spin lock which will be used to protect the result variable. The last thread to grab the spin lock will get to set the final value of result:

global main 
 
extern printf
extern pthread_create
extern pthread_exit
extern pthread_join
 
section .data
        align 4
        sLock:          dd 0    ; The lock, values are:
                                ; 0     unlocked
                                ; 1     locked  
        tID1:           dd 0
        tID2:           dd 0
        fmtStr1:        db "In thread %d with ID: %02x", 0x0A, 0
        fmtStr2:        db "Result %d", 0x0A, 0
 
section .bss
        align 4
        result:         resd 1
 
section .text
        main:                   ; Using main since we are using gcc to link
 
                                ;
                                ; Call pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                                ;                       void *(*start_routine) (void *), void *arg);
                                ;
        push    dword 0         ; Arg Four: argument pointer
        push    thread1         ; Arg Three: Address of routine
        push    dword 0         ; Arg Two: Attributes
        push    tID1            ; Arg One: pointer to the thread ID
        call    pthread_create
 
        push    dword 0         ; Arg Four: argument pointer
        push    thread2         ; Arg Three: Address of routine
        push    dword 0         ; Arg Two: Attributes
        push    tID2            ; Arg One: pointer to the thread ID
        call    pthread_create
 
                                ;
                                ; Call int pthread_join(pthread_t thread, void **retval) ;
                                ;
        push    dword 0         ; Arg Two: retval
        push    dword [tID1]    ; Arg One: Thread ID to wait on
        call    pthread_join
        push    dword 0         ; Arg Two: retval
        push    dword [tID2]    ; Arg One: Thread ID to wait on
        call    pthread_join
 
        push    dword [result]
        push    dword fmtStr2
        call    printf
        add     esp, 8          ; Pop stack 2 times 4 bytes
 
        call exit
 
thread1:
        pause
        push    dword [tID1]
        push    dword 1 
        push    dword fmtStr1
        call    printf
        add     esp, 12         ; Pop stack 3 times 4 bytes
 
        call    spinLock
 
        mov     [result], dword 1
        call    spinUnlock
 
        push    dword 0         ; Arg one: retval
        call    pthread_exit
 
thread2:
        pause
        push    dword [tID2]
        push    dword 2 
        push    dword fmtStr1
        call    printf
        add     esp, 12         ; Pop stack 3 times 4 bytes
 
        call    spinLock
 
        mov     [result], dword 2
        call    spinUnlock
 
        push    dword 0         ; Arg one: retval
        call    pthread_exit
 
spinLock:
        push    ebp
        mov     ebp, esp
        mov     edx, 1          ; Value to set sLock to
spin:   mov     eax, [sLock]    ; Check sLock
        test    eax, eax        ; If it was zero, maybe we have the lock
        jnz     spin            ; If not try again
        ;
        ; Attempt atomic compare and exchange:
        ; if (sLock == eax):
        ;       sLock           <- edx
        ;       zero flag       <- 1
        ; else:
        ;       eax             <- edx
        ;       zero flag       <- 0
        ;
        ; If sLock is still zero then it will have the same value as eax and
        ; sLock will be set to edx which is one and therefore we aquire the
        ; lock. If the lock was acquire between the first test and the
        ; cmpxchg then eax will not be zero and we will spin again.
        ;
        lock    cmpxchg [sLock], edx
        test    eax, eax
        jnz     spin
        pop     ebp
        ret
 
spinUnlock:
        push    ebp
        mov     ebp, esp
        mov     eax, 0
        xchg    eax, [sLock]
        pop     ebp
        ret
 
exit:
                                ;
                                ; Call exit(3) syscall
                                ;       void exit(int status)
                                ;
        mov     ebx, 0          ; Arg one: the status
        mov     eax, 1          ; Syscall number:
        int     0x80

In order to assemble, link and run the program we need to do the following:

$ nasm -felf32 -g cmpxchgSpinLock.asm
$ gcc -o cmpxchgSpinLock cmpxchgSpinLock.o -lpthread
$ ./cmpxchgSpinLock

Zero Extend [edit]

movz src, dest GAS Syntax
movzx dest, src Intel syntax

Move zero extend

The movz instruction copies the src operand in the dest operand and pads the remaining bits not provided by src with zeros (0).

This instruction is useful for copying an unsigned small value to a bigger register.

Operands

src

  • Register
  • Memory

dest

  • Register

Modified flags

  • No FLAGS are modified by this instruction

Example

.data

byteval:
       .byte   204

.text
       .global _start

_start:
       movzbw  byteval, %ax
       # %eax is now 204

       movzwl  %ax, %ebx
       # %ebx is now 204

       movzbl  byteval, %esi
       # %esi is now 204

# Linux sys_exit 
       mov     $1, %eax
       xorl    %ebx, %ebx
       int     $0x80

Sign Extend [edit]

movs src, dest GAS Syntax
movsx dest, src Intel syntax

Move sign extend.

The movs instruction copies the src operand in the dest operand and pads the remaining bits not provided by src but the sign of src.

This instruction is useful for copying a signed small value to a bigger register.

Operands

src

  • Register
  • Memory

dest

  • Register

Modified flags

  • No FLAGS are modified by this instruction

Example

.data

byteval:
       .byte   -24 # = 0xe8

.text
       .global _start

_start:
       movsbw  byteval, %ax
       # %ax is now -24 = 0xffe8

       movswl  %ax, %ebx
       # %ebx is now -24 = 0xffffffe8

       movsbl  byteval, %esi
       # %esi is now -24 = 0xffffffe8

# Linux sys_exit 
       mov     $1, %eax
       xorl    %ebx, %ebx
       int     $0x80

Move String [edit]

movsb

Move byte

The movsb instruction copies one byte from the memory location specified in esi to the location specified in edi. If the direction flag is cleared, then esi and edi are incremented after the operation. Otherwise, if the direction flag is set, then the pointers are decremented. In that case the copy would happen in the reverse direction, starting at the highest address and moving toward lower addresses until ecx is zero.

Operands

None.

Modified flags

  • No FLAGS are modified by this instruction

Example

section .text
  ; copy mystr into mystr2
  mov esi, mystr    ; loads address of mystr into esi
  mov edi, mystr2   ; loads address of mystr2 into edi
  cld               ; clear direction flag (forward)
  mov ecx,6
  rep movsb         ; copy six times
 
section .bss
  mystr2: resb 6
 
section .data
  mystr db "Hello", 0x0


movsw

Move word

The movsw instruction copies one word (two bytes) from the location specified in esi to the location specified in edi. It basically does the same thing as movsb, except with words instead of bytes.

Operands

None.

Modified flags

  • No FLAGS are modified by this instruction

Example

section .code
  ; copy mystr into mystr2
  mov esi, mystr
  mov edi, mystr2
  cld
  mov ecx,4
  rep movsw
  ; mystr2 is now AaBbCca\0
 
section .bss
  mystr2: resb 8
 
section .data
  mystr db "AaBbCca", 0x0

Load Effective Address [edit]

lea src, dest GAS Syntax
lea dest, src Intel syntax


Load Effective Address

The lea instruction calculates the address of the src operand and loads it into the dest operand.

Operands

src

  • Immediate
  • Register
  • Memory

dest

  • Register

Modified flags

  • No FLAGS are modified by this instruction

Note Load Effective Address calculates its src operand in the same way as the mov instruction does, but rather than loading the contents of that address into the dest operand, it loads the address itself.

lea can be used not only for calculating addresses, but also general-purpose unsigned integer arithmetic (with the caveat and possible advantage that FLAGS are unmodified). This can be quite powerful, since the src operand can take up to 4 parameters: base register, index register, scalar multiplier and displacement, e.g. [eax + edx*4 -4] (Intel syntax) or -4(%eax, %edx, 4) (GAS syntax). The scalar multiplier is limited to constant values 1, 2, 4, or 8 for byte, word, double word or quad word offsets respectively. This by itself allows for multiplication of a general register by constant values 2, 3, 4, 5, 8 and 9, as shown below (using NASM syntax):

lea ebx, [ebx*2]      ; Multiply ebx by 2
lea ebx, [ebx*8+ebx]  ; Multiply ebx by 9, which totals ebx*18

Data transfer instructions of 8086 microprocessor [edit]

General purpose byte or word transfer instructions:

  • MOV: copy byte or word from specified source to specified destination
  • PUSH: copy specified word to top of stack.
  • POP: copy word from top of stack to specified location
  • PUSHA: copy all registers to stack
  • POPA: copy words from stack to all registers.
  • XCHG: Exchange bytes or exchange words
  • XLAT: translate a byte in AL using a table in memory.

These are I/O port transfer instructions:

  • IN: copy a byte or word from specific port to accumulator
  • OUT: copy a byte or word from accumulator to specific port

Special address transfer Instructions:

  • LEA: load effective address of operand into specified register
  • LDS: load DS register and other specified register from memory
  • LES: load ES register and other specified register from memory

Flag transfer instructions:

  • LAHF: load AH with the low byte of flag register
  • SAHF: Stores AH register to low byte of flag register
  • PUSHF: copy flag register to top of stack
  • POPF: copy top of stack word to flag register