Programación en ensamblador (x86-64)

Índice
- Introducción
- Objetivos
- 1.Arquitectura del computador
- 1.1.Modos de operación
- 1.2.El modo de 64 bits
- 1.2.1.Organización de la memoria
- 1.2.2.Registros
- 2.Lenguajes de programación
- 3.El lenguaje de ensamblador para la arquitectura x86-64
- 4.Introducción al lenguaje C
- 4.1.Estructura de un programa en C
- 4.2.Elementos de un programa en C
- 4.2.1.Directivas
- 4.2.2.Variables
- 4.2.3.Operadores
- 4.2.4.Control de flujo
- 4.2.5.Vectores
- 4.2.6.Apuntadores
- 4.2.7.Funciones
- 4.2.8.Funciones de E/S
- 5.Conceptos de programación en ensamblador y C
- 5.1.Acceso a datos
- 5.1.1.Estructuras de datos
- 5.1.2.Gestión de la pila
- 5.2.Operaciones aritméticas
- 5.3.Control de flujo
- 5.3.1.Estructura if
- 5.3.2.Estructura if-else
- 5.3.3.Estructura while
- 5.3.4.Estructura do-while
- 5.3.5.Estructura for
- 5.3.6.Estructura switch-case
- 5.4.Subrutinas y paso de parámetros
- 5.5.Entrada/salida
- 5.5.1.E/S programada
- 5.6.Controlar la consola
- 5.7.Funciones del sistema operativo (system calls)
- 5.1.Acceso a datos
- 6.Anexo: manual básico del juego de instrucciones
- 6.1.ADC: suma aritmética con bit de transporte
- 6.2.ADD: suma aritmética
- 6.3.AND: Y lógica
- 6.4.CALL: llamada a subrutina
- 6.5.CMP: comparación aritmética
- 6.6.DEC: decrementa el operando
- 6.7.DIV: división entera sin signo
- 6.8.IDIV: división entera con signo
- 6.9.IMUL: multiplicación entera con signo
- 6.10.IN: lectura de un puerto de entrada/salida
- 6.11.INC: incrementa el operando
- 6.12.INT: llamada a una interrupción software
- 6.13.IRET: retorno de interrupción
- 6.14.Jxx: salto condicional
- 6.15.JMP: salto incondicional
- 6.16.LOOP: bucle hasta RCX=0
- 6.17.MOV: transferir un dato
- 6.18.MOVSX/MOVSXD: transferir un dato con extensión de signo
- 6.19.MOVZX: transferir un dato añadiendo ceros
- 6.20.MUL: multiplicación entera sin signo
- 6.21.NEG: negación aritmética en complemento a 2
- 6.22.NOT: negación lógica (negación en complemento a 1)
- 6.23.OUT: escritura en un puerto de entrada/salida
- 6.24.OR: o lógica
- 6.25.POP: extraer el valor de la cima de la pila
- 6.26.PUSH: introducir un valor en la pila
- 6.27.RET: retorno de subrutina
- 6.28.ROL: rotación a la izquierda
- 6.29.ROR: rotación a la derecha
- 6.30.SAL: desplazamiento aritmético (o lógico) a la izquierda
- 6.31.SAR: desplazamiento aritmético a la derecha
- 6.32.SBB: resta con transporte (borrow)
- 6.33.SHL: desplazamiento lógico a la izquierda
- 6.34.SHR: desplazamiento lógico a la derecha
- 6.35.SUB: resta sin transporte
- 6.36.TEST: comparación lógica
- 6.37.XCHG: intercambio de operandos
- 6.38.XOR: o exclusiva
Introducción
-
Descripción de la arquitectura x86-64 desde el punto de vista del programador, y en concreto del modo de 64 bits, en el cual se desarrollarán las prácticas de programación.
-
Descripción del entorno de programación con el que se trabajará: edición, compilación y depuración de los programas en ensamblador y en C.
-
Descripción de los elementos que componen un programa en ensamblador.
-
Introducción a la programación en lenguaje C.
-
Conceptos de programación en lenguaje de ensamblador y en lenguaje C, y cómo utilizar funciones escritas en ensamblador dentro de programas en C.
-
También se incluye una referencia de las instrucciones más habituales del lenguaje de ensamblador con ejemplos de uso de cada una.
Objetivos
-
Aprender a utilizar un entorno de programación con lenguaje C y lenguaje de ensamblador.
-
Conocer el repertorio de instrucciones básicas de los procesadores de la familia x86-64, las diferentes formas de direccionamiento para tratar los datos y el control de flujo dentro de un programa.
-
Saber estructurar en subrutinas un programa; pasar y recibir parámetros.
-
Tener una idea global de la arquitectura interna del procesador según las características del lenguaje de ensamblador.
1.Arquitectura del computador
1.1.Modos de operación

Modo de operación |
Sistema operativo |
Las aplicaciones |
Por defecto |
Tamaño de los registros de propósito general |
||
Tamaño (en bits) de las direcciones |
Tamaño (en bits) de los operandos |
|||||
Modo |
Modo de 64 bits |
Sistema operativo de 64 bits |
sí |
64 |
32 |
64 |
Modo compatibilidad |
no |
32 |
32 |
|||
16 |
16 |
16 |
||||
Modo |
Modo protegido |
Sistema operativo de 32 bits |
no |
32 |
32 |
32 |
16 |
16 |
|||||
Modo |
16 |
16 |
16 |
|||
Modo real |
Sistema operativo de 16 bits |
1.1.1.Modo extendido de 64 bits
Modo de 64 bits
Modo de compatibilidad
1.1.2.Modo heredado de 16 y 32 bits
1.1.3.El modo de gestión de sistema
1.2.El modo de 64 bits
-
El registro contador de programa (RIP, instruction pointer register) es de 64 bits.
-
El registro de bits de estado también es de 64 bits (RFLAGS). Los 32 bits de la parte alta están reservados; los 32 bits de la parte baja son accesibles y corresponden a los mismos bits de la arquitectura IA-32 (registro EFLAGS).
-
Los registros de segmento en general no se utilizan en el modo de 64 bits.

1.2.1.Organización de la memoria
Paginación

Orden de los bytes

Tamaño de las direcciones
Tamaño de los desplazamientos y de los valores inmediatos
1.2.2.Registros
Registros de propósito general



RAX[63-0] |
Registro de 8 bytes |
|||
EAX[31-0] |
= RAX[31-0] |
Registro de 4 bytes |
||
AX[15-0] |
= EAX[15-0] |
= RAX[15-0] |
Registro de 2 bytes |
|
AH[7-0] |
= AX[15-8] |
= EAX[15-8] |
= RAX[15-8] |
Registro de 1 byte |
Al/AL[7-0] |
= AX[7-0] |
= EAX[7-0] |
= RAX[7-0] |
Registre de 1 byte |
-
En una misma instrucción no se puede usar un registro del conjunto AH, BH, CH, DH junto con uno del conjunto SIL, DIL, BPL, SPL, R8B – R15B.
-
Registro RSP: tiene una función especial, funciona como apuntador de pila, contiene siempre la dirección del primer elemento de la pila. Si lo utilizamos con otras finalidades, perderemos el acceso a la pila.
-
Cuando se utiliza un registro de 32 bits como operando destino de una instrucción, la parte alta del registro está fijada en 0.
Registros de propósito específico
-
CS: code segment
-
DS: data segment
-
SS: stack segment
-
ES: extra segment
-
FS: extra segment
-
GS: extra segment

bit 0 |
CF |
Carry Flag |
0 = no se ha producido transporte; 1 = sí que se ha producido transporte |
bit 1 |
No definido |
||
bit 2 |
PF |
Parity Flag |
0 = número de bits 1 es impar; 1 = número de bits 1 es par |
bit 3 |
No definido |
||
bit 4 |
AF |
Auxiliary Carry Flag |
0 = no se ha producido transporte en operaciones BCD; 1 = sí que se ha producido transporte en operaciones BCD |
bit 5 |
No definido |
||
bit 6 |
ZF |
Zero Flag |
0 = el resultado no ha sido cero; 1 = el resultado ha sido cero |
bit 7 |
SF |
Sign Flag |
0 = el resultado no ha sido negativo; 1 = el resultado ha sido negativo |
bit 8 |
TF |
Trap Flag |
Facilita la ejecución paso a paso. |
bit 9 |
IF |
Interrupt Enable Flag |
Reservado para el sistema operativo en modo protegido. |
bit 10 |
DF |
Direction Flag |
0 = autoincremento hacia direcciones altas; 1 = autoincremento hacia direcciones bajas |
bit 11 |
OF |
Overflow Flag |
0 = no se ha producido desbordamiento; 1 = sí que se ha producido desbordamiento |
bit 12 |
IOPL |
I/O Privilege Level |
Reservado para el sistema operativo en modo protegido. |
bit 13 |
IOPL |
I/O Privilege Level |
Reservado para el sistema operativo en modo protegido. |
bit 14 |
NT |
Nested Task Flag |
Reservado para el sistema operativo en modo protegido. |
bit 15 |
No definido |
||
bit 16 |
RF |
Resume Flag |
Facilita la ejecución paso a paso. |
bit 17 |
VM |
Virtual-86 Mode Flag |
Reservado para el sistema operativo en modo protegido. |
bit 18 |
AC |
Alignment Check Flag |
Reservado para el sistema operativo en modo protegido. |
bit 19 |
VIF |
Virtual Interrupt Flag |
Reservado para el sistema operativo en modo protegido. |
bit 20 |
VIP |
Virtual Interrupt Pending |
Reservado para el sistema operativo en modo protegido. |
bit 21 |
ID |
CPU ID |
Si el bit puede ser modificado por los programas en el espacio de usuario, CPUID está disponible. |
bit 22-31 |
No definidos |
2.Lenguajes de programación
2.1.Entorno de trabajo
3.El lenguaje de ensamblador para la arquitectura x86-64
3.1.Estructura de un programa en ensamblador
section .data section .bss section .text

3.2.Directivas
3.2.1.Definición de constantes
ServicioSO equ 80h
Mensaje1 equ 'Hola'
3.2.2.Definición de variables
Sección .data, variables inicializadas
-
db: define una variable de tipo byte, 8 bits.
-
dw: define una variable de tipo palabra (word), 2 bytes = 16 bits.
-
dd: define una variable de tipo doble palabra (double word), 2 palabras = 4 bytes = 32 bits.
-
dq: define una variable de tipo cuádruple palabra (quad word), 4 palabras = 8 bytes = 64 bits.
nombre_variable directiva valor_inicial
var1 db 255 ; define una variable con el valor FFh Var2 dw 65535 ; en hexadecimal FFFFh var4 dd 4294967295 ; en hexadecimal FFFFFFFFh var8 dq 18446744073709551615 ; en hexadecimal FFFFFFFFFFFFFFFFh
tamañoVec equ 5 indexVec db tamañoVec
var4 dd 4_294_967_295 var8 dq FF_FF_FF_FF_FF_FF_FF_FFh
var db 67 ;el valor 67 decimal var db 67d ;el mismo valor
var db 0xFF var dw 0hA3 var db $43 var dw 33FFh
var db $FF ;deberíamos escribir: var db $0FF var db FFh ;deberíamos escribir: var db 0FFh
var db 103o var db 103q var db 0o103 var db 0q103
var db 0b01000011 var dw 0b0110_1100_0100_0011 var db 01000011b var dw 0110_0100_0011b
var db 'A' var db "A" var db `A`
cadena db 'Hola' ;define una cadena formada por 4 caracteres cadena db "Hola" cadena db `Hola`
cadena db 'Hola' cadena db 'H','o','l','a' cadena db 'Hol','a'
cadena db 'Hola',10 ;añade el carácter ASCII de salto de línea cadena db 'Hola',9 ;añade el carácter ASCII de tabulación cadena db 72,111,108,97 ;añade igualmente la cadena 'Hola'
cadena db `Hola\n` ;añade el carácter de salto de línea al final cadena db `Hola\t` ;añade el carácter de salto de línea al final cadena db `\e[10,5H` ;define una secuencia de escape que empieza con ;el carácter ESC = \e = ASCII(27) cadena db `Hola \u263a` ;muestra: Hola☺
"El ensamblador","El ensamblador",'El ensamblador', `El ensamblador´, `El ensamblador`, 'Vocales acentuadas "àèéíòóú", con diéresis "ïü" y símbolos "ç€@#".' `Vocales acentuadas "àèéíòóú", con diéresis "ïü" y símbolos "ç€@#".`
cadena times 4 db 'PILA' ;define una variable inicializada ;con el valor 'PILA' 4 veces cadena2 db 'PILAPILAPILAPILA' ;es equivalente a la declaración anterior
Vectores
vector1 db 23, 42, -1, 65, 14 ;vector formado por 5 valores de tipo ;byte vector2 db 'a'b', 'c', 'de ;vector formado por 4 bytes, ;inicializado usando caracteres vector3 db 97, 98, 99, 100 ;es equivalente al vector anterior vector4 dw 1000, 2000, -1000, 3000 ;vector formado por 4 palabras
vector5 times 5 dw 0 ;vector formado por 5 palabras inicializadas en 0
Valores y constantes numéricas
Sección .bss, variables no inicializadas
-
resb: reserva espacio en unidades de byte
-
resw: reserva espacio en unidades de palabra, 2 bytes
-
resd: reserva espacio en unidades de doble palabra, 4 bytes
-
resq: reserva espacio en unidades de cuádruple palabra, 8 bytes
nombre_variable directiva multiplicidad
section .bss var1 resb 1 ;reserva 1 byte var2 resb 4 ;reserva 4 bytes var3 resw 2 ;reserva 2 palabras = 4 bytes, equivalente al caso anterior var3 resd 1 ;reserva una cuádruple palabra = 4 bytes ;equivalente a los dos casos anteriores
3.2.3.Definición de otros elementos
extern símbolo1, símbolo2, ..., símboloN
global símbolo1, símbolo2, ..., símboloN
global printHola, msg ;hacemos visibles la subrutina ;printHola y la variable msg. section .data msg db "Hola!",10 ... section .text printHola: mov rax,4 mov rbx,1 mov rcx,msg mov rdx,6 int 80h ret
extern printHola, msg ;indicamos que están declaradas ;en otro fichero. global main ;hacemos visible la etiqueta main ;para poder iniciar la ejecución. section. text main: call printHola ;ejecutamos el código del Prog1.asm mov rax,4 ;ejecutamos este código pero mov rbx,1 ;utilizamos la variable definida mov rcx,msg ;en el fichero Prog1.asm mov rdx,6 int 80h mov rax,1 mov rbx,0 int 80h
cpu tipo_procesador
3.3.Formato de las instrucciones
3.3.1.Etiquetas
Servicio equ 80h section .data msg db "Hola!",10 section .text printHola: mov rax,4 mov rbx,1 mov rcx,msg mov rdx,6 int Servicio jmp printHola
... Etiqueta1: mov rax,0 ... jmp Etiqueta1 ;Esta instrucción salta a la ;instrucción mov rax,0 ...
... Etiqueta1: mov rax,0 ... jmp Etiqueta1 ;Esta instrucción salta a la instrucción mov rax,0 ...
3.4.Juego de instrucciones y modos de direccionamiento
ret ;retorno de subrutina
push rax ;guarda el valor de rax en la pila; rax es un operando fuente pop rax ;carga el valor de la cima de la pila en rax; rax es un ; operando destino call subr1 ;llama a la subrutina subr1, subr1 es un operando fuente inc rax ;rax=rax+1, rax hace de operando fuente y también de operando destino.
mov rax [vec+rsi];mueve el valor de la posición del vector vec ;indicada por rsi; es el operando fuente, en rax, ;rax es el operando destino add rax, 4 ;rax=rax+4
3.4.1.Tipos de operandos de las instrucciones x86-64
Operandos fuente y destino
push rax
pop rax
inc rax
mov rax, rbx
add rax, 4
Localización de los operandos
mov al, 10 b ;un valor inmediato expresado en binario cinco equ 5 h ;se define una constante en hexadecimal mov al, cinco ;se utiliza el valor de la constante como ;valor inmediato mov eax, 0xABFE001C ;un valor inmediato expresado en hexadecimal. mov ax, 'HI' ;un valor inmediato expresado como una cadena ;de caracteres
mov al, 100 mov ax, 1000 mov eax, 100000 mov rax, 10000000 mov rbx, rax mul bl ;ax = al * bl, los registros al y ax son implícitos, ;al como operando fuente y ax como operando de destino.
section .data var1 dd 100 ;variable de 4 bytes (var1 = 100) section .text mov rax, var1 ;se carga en rax la dirección de la variable var1 mov rbx, [var1] ;se carga en rbx el contenido de var1, rbx=100 mov rbx, [rax] ;esta instrucción hará lo mismo que la anterior. mov [var1], rbx ;se carga en var1 el contenido del registro rbx
;si consideramos que la dirección de memoria a la que hace referencia var1 ;es la dirección 12345678h mov QWORD [var2], var1 ;se carga en var2 la dirección de var1 mov QWORD [var2],12345678h ;es equivalente a la instrucción anterior.
Tamaño de los operandos
-
BYTE: indica que el tamaño del operando es de un byte (8 bits).
-
WORD: indica que el tamaño del operando es de una palabra (word) o dos bytes (16 bits).
-
DWORD: indica que el tamaño del operando es de una doble palabra (double word) o cuatro bytes (32 bits).
-
QWORD: indica que el tamaño del operando es de una cuádruple palabra (quad word) u ocho bytes (64 bits).
.data var1 dd 100 ;variable definida de 4 bytes . text mov DWORD [var1], 0ABCh ;se indica que la variable var1 ;es de 32 bits
mov [var1], 0ABCh
inc QWORD [var1] ;se indica que la posición de memoria afectada es ;de tamaño de 8 bytes. [var1]=[var1]+1 push WORD [var1] ;se indica que ponemos 2 bytes en la pila.
3.4.2.Modos de direccionamiento
mov rax, 0102030405060708h ;el segundo operando utiliza direccionamiento inmediato ;expresado con 64 bits. mov QWORD [var], 100 ;el segundo operando utiliza direccionamiento inmediato ;carga el valor 100 y se guarda en var mov rbx, var ;el segundo operando utiliza direccionamiento inmediato ;carga la dirección de var en el registro rbx mov rbx, var+16 * 2 ;el segundo operando utiliza direccionamiento inmediato ;carga la dirección de var+32 en el registro rbx
mov rax, rbx ;los dos operandos utilizan direccionamiento ;directo a registro, rax = rbx
mov rax,[var] ;el segundo operando utiliza direccionamiento ;directo a memoria, rax = [var] add [suma],rcx ;el primer operando utiliza direccionamiento ;directo a memoria [suma]=[suma]+rcx
mov rbx, var ;se carga en rbx la dirección de la variable var mov rax, [rbx] ;el segundo operando utiliza la dirección que tenemos en rbx ;para acceder a memoria, se mueven 8 bytes a partir de ;la dirección especificada por rbx y se guardan en rax.
mov rax, [vector+rsi] ;vector contiene la dirección base, rsi actúa ;como registro índice add [1234h+r9],rax ;1234h es la dirección base, r9 actúa ;como registro índice.
mov rbx, var ;se carga en rbx la dirección de la variable var mov rax, [rbx+4] ;el segundo operando utiliza direccionamiento relativo ;4 es el desplazamiento respecto a esta dirección mov [rbx+16], rcx ;rbx contiene la dirección base, 16 es el ;desplazamiento respecto a esta dirección.
[Registro Base + Registro Index * escala + desplazamiento]
[rbx + rsi * 2 + 4 * (12+127)] [r9 + r10 * 8] [r9 - 124 * 8] [rax * 4 + 12 / 4] [vec+16 + rsi * 2]
je etiqueta1
push fuente ;coloca un dato en la pila pop destino ;extrae un dato de la pila
push word 23h push qword [rax] pop qword [var] pop bx
3.4.3.Tipos de instrucciones
-
mov destino, fuente: instrucción genérica para mover un dato desde un origen a un destino.
-
push fuente: instrucción que mueve el operando de la instrucción a la cima de la pila.
-
pop destino: mueve el dato que se encuentra en la cima de la pila al operando destino.
-
xchg destino, fuente: intercambia contenidos de los operandos.
-
add destino, fuente: suma aritmética de los dos operandos.
-
adc destino, fuente: suma aritmética de los dos operandos considerando el bit de transporte.
-
sub destino, fuente: resta aritmética de los dos operandos.
-
sbb destino, fuente: resta aritmética de los dos operandos considerando el bit de transporte.
-
inc destino: incrementa el operando en una unidad.
-
dec destino: decrementa el operando en una unidad.
-
mul fuente: multiplicación entera sin signo.
-
imul fuente: multiplicación entera con signo.
-
div fuente: división entera sin signo.
-
idiv fuente: división entera con signo.
-
neg destino: negación aritmética en complemento a 2.
-
cmp destino, fuente: comparación de los dos operandos; hace una resta sin guardar el resultado.
-
and destino, fuente: operación 'y' lógica.
-
or destino, fuente: operación 'o' lógica.
-
xor destino, fuente: operación `o exclusiva´ lógica.
-
not destino: negación lógica bit a bit.
-
test destino, fuente: comparación lógica de los dos operandos; hace una 'y' lógica sin guardar el resultado.
-
sal destino, fuente / shl destino, fuente: desplazamiento aritmético/lógico a la izquierda.
-
sar destino, fuente: desplazamiento aritmético a la derecha.
-
shr destino, fuente: desplazamiento lógico a la derecha.
-
rol destino, fuente: rotación lógica a la izquierda.
-
ror destino, fuente: rotación lógica a la derecha.
-
rcl destino, fuente: rotación lógica a la izquierda considerando el bit de transporte.
-
rcr destino, fuente: rotación lógica a la derecha considerando el bit de transporte.
-
jmp etiqueta: salta de manera incondicional a la etiqueta.
-
je etiqueta / jz etiqueta: salta a la etiqueta si igual, si el bit de cero es activo.
-
jne etiqueta / jnz etiqueta: salta a la etiqueta si diferente, si el bit de cero no es activo.
-
jc etiqueta: salta a la etiqueta si el bit de transporte es activo.
-
jnc etiqueta: salta a la etiqueta si el bit de transporte no es activo.
-
jo etiqueta: salta a la etiqueta si el bit de desbordamiento es activo.
-
jno etiqueta: salta a la etiqueta si el bit de desbordamiento no es activo.
-
js etiqueta: salta a la etiqueta si el bit de signo es activo.
-
jns etiqueta: salta a la etiqueta si el bit de signo no es activo.
-
jb etiqueta / jnae etiqueta: salta a la etiqueta si es más pequeño.
-
jbe etiqueta / jna etiqueta: salta a la etiqueta si es más pequeño o igual.
-
ja etiqueta / jnbe etiqueta: salta a la etiqueta si es mayor.
-
jae etiqueta / jnb etiqueta: salta a la etiqueta si es mayor o igual.
-
jl etiqueta / jnge etiqueta: salta si es más pequeño.
-
jle etiqueta / jng etiqueta: salta si es más pequeño o igual.
-
jg etiqueta / jnle etiqueta: salta si es mayor.
-
jge etiqueta / jnl etiqueta: salta si es mayor o igual.
-
loop etiqueta: decrementa el registro rcx y salta si rcx es diferente de cero.
-
call etiqueta: llamada a subrutina.
-
ret: retorno de subrutina.
-
iret: retorno de rutina de servicio de interrupción (RSI).
-
int servicio: llamada al sistema operativo.
-
in destino, fuente: lectura del puerto de E/S especificado en el operando fuente y se guarda en el operando destino.
-
out destino, fuente: escritura del valor especificado por el operando fuente en el puerto de E/S especificado en el operando destino.
4.Introducción al lenguaje C
4.1.Estructura de un programa en C
-
Directivas de compilación.
-
Definición de variables globales.
-
Declaración e implementación de funciones.
-
Declaración e implementación de la función main.
1 /*Fichero hola.c*/ 2 #include <stdio.h> 3 int main(){ 4 printf ("Hola!\n"); 5 return 0; 6 }
/* Esto es un
comentario
de varias líneas */
4.1.1.Generación de un programa ejecutable
$ gcc hola.c -o hola -g
$ ./hola Hola! $ _
4.2.Elementos de un programa en C
4.2.1.Directivas
#include <stdio.h>
#define CADENA "Hola" #define NUMBIN 01110101b #define NUMOCT 014q #define NUMDEC 12 #define NUMHEX 0x0C
extern [tipo_del_retorno] nombre_de_función ([lista_de_tipos]); extern [tipo] nombre_variable [= valor_inicial];
extern int y; extern printVariable(int);
4.2.2.Variables
int x=0; //x: variable global int main(){ int y=1; //y: variable local de la función main for (y=0; y>3; y++) {int z = y*2; } //z: variable local del for }
-
caracteres (char),
-
enteros (int) y (long) y
-
números reales (float) y (double).
/*Una variable de tipo carácter inicializada con el valor 'A'*/ char c='A'; /*Dos variables de tipo entero, una inicializada y otra sin inicializar*/ int x=0, y; /*Un número real, expresado en punto fijo*/ float pi=3.1416;
Tipo de variable en C |
Tamaño |
Rango de valores |
Tipo ensamblador |
Modificador |
---|---|---|---|---|
char |
1 byte |
–127 a +127 |
db |
BYTE |
unsigned char |
1 byte |
0 a 255 |
db |
BYTE |
short int |
2 bytes |
–32.768 a +32.767 |
dw |
WORD |
unsigned short int |
2 bytes |
0 a 65.535 |
dw |
WORD |
int |
4 bytes |
–2.147.483.648 a +2.147.483.647 |
dd |
DWORD |
unsigned int |
4 bytes |
0 a 4.294.967.295 |
dd |
DWORD |
long int |
8 bytes |
–9.223.372.036.854.775.808 a +9.223.372.036.854.775.807 |
dq |
QWORD |
unsigned long int |
8 bytes |
0 a 18.446.744.073.709.551.615 |
dq |
QWORD |
4.2.3.Operadores
a = 3; c = a;
suma: + resta y negación: – incremento: ++ decremento: –- producto: * división: / resto de la división: % |
a = b + c; x = y * z
igualdad: = = diferente: != menor: < menor o igual: <= mayor: > mayor o igual: >= |
Y lógica (AND): && O lógica (OR): || Negación lógica: ! |
(a = = b) && (c != 3) (a <= b) || !(c > 10)
OR (O): | AND (Y): & XOR (O exclusiva): ^ Negación lógica: ~ Desplazamiento a la derecha: >> Desplazamiento a la izquierda: << |
z = x | y; z = x & y; z = x ^ y; z = x >> 2; // desplazar los bits de la variable x 2 posiciones // a la derecha y guardar el resultado en z z = ~x; //z es el complemento a 1 de x z = -x; //z es el complemento a 2 de x
4.2.4.Control de flujo
if (condición) { bloque de sentencias }
if (condición) sentencia;
if (a > b) { printf("a es mayor que b\n"); a--; } if (a >= b) b++;
if (condición) { bloque de sentencias } else { bloque de sentencias alternativas }
if (a >b) { printf("a es mayor que b\n"); a--; } else printf(("a no es mayor que b\n");
if (condición 1) { bloque de sentencias que se ejecutan si se cumple la condición 1 } else if (condición 2) { bloque de sentencias que se ejecutan si se cumple la condición 2 y no se cumple la condición 1 } else { bloque de sentencias que se ejecutan si no se cumple ninguna de las condiciones }
if (a > b) { printf("a es mayor que b\n"); } else if (a < b) { printf("a es menor que b\n"); } else { printf("a es igual que b\n"); }
for (valores iniciales; condiciones; actualización) { bloque de sentencias que ejecutar }
for (valores iniciales; condiciones; actualización) sentencia;
//bucle que se ejecuta 5 veces, mientras x<5, cada vez se incrementa x en 1 int x; for (x = 0; x < 5 ; x++) { printf("el valor de x es: %d\n", x); }
while (condiciones) { bloque de sentencias que ejecutar }
while (condiciones) sentencia;
//bucle que se ejecuta 5 veces, cabe definir previamente la variable x int x = 0; while (x < 5) { printf("el valor de x es: %d\n", x); x++; //necesario para salir del bucle }
do { bloque de sentencias a ejecutar }while (condiciones);
do sentencia; while (condiciones);
//bucle que se ejecuta 5 veces, cabe definir previamente la variable x int x = 0; do { printf("el valor de x es: %d\n", x); x++; } while (x < 5);
4.2.5.Vectores
int vector[5]; // vector de 5 enteros char cadena[4]; // vector de 4 caracteres int matriz[3][4]; // matriz de 3 filas y 4 columnas: 12 enteros
int vector[]={1, 2, 3, 4, 5}; // vector de 5 enteros char cadena[4]={'H', 'o', 'l', 'a'}; // vector de 4 caracteres int vector2[3]={1, 2}; // vector de 3 enteros con las dos // primeras posiciones inicializadas int vector2[3]={1, 2, 3, 4}; // declaración incorrecta int matriz[][]={{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}; // matriz de enteros de 3 filas y 4 columnas
char cadena[]="Hola";
int vector[3]={1, 2, 3}; //índices válidos: 0, 1 y 2 //lo que equivale a hacer lo siguiente: int vector[3]; vector[0]=1; vector[1]=2; vector[2]=3; // o también se puede hacer lo siguiente: int i; for (i=0; i<3; i++) vector[i]=i+1; // Si hacemos lo siguiente, no se producirá error, pero realmente // estaremos accediendo fuera del vector, lo que puede ocasionar // múltiples problemas: vector[3]=4; vector[20]=300;
4.2.6.Apuntadores
int *p1; // Se define un apuntador de tipo int char *p2; // Se define un apuntador de tipo char int x=1, y; // se definen dos variables de tipo int char c='a'; // se define una variables de tipo char p1=&x; // se asigna a p1 la dirección de la variable x p2=&c; // se asigna a p2 la dirección de la variable c y=*p1; // como p1 apunta a x, es equivalente a y = x.
int vector[3]={1, 2, 3}; //lo que es equivalente a hacer lo siguiente: int vector[3]; int *v; int i; v = vector; //vector i *v son apuntadores a entero. for (i=0; i<3; i++) *(v+i)=i+1; // *(v+i): indica que estamos accediendo al contenido // de la dirección 'v+i'
4.2.7.Funciones
tipo_del_retorno nombre_función(lista_de_parámetros){ definición de variables; sentencias; return valor; }
-
tipo_del_retorno: tipo del dato que devolverá la función; si no se especifica el tipo de retorno, por defecto es de tipo entero (int).
-
lista_de_parámetros: lista de tipos y nombres de variables separados por comas; no es obligatorio que la función tenga parámetros.
-
return: finaliza la ejecución de la función y devuelve un valor del tipo especificado en el campo tipo_del_retorno. Si no se utiliza esta sentencia o no se especifica un valor, se devolverá un valor indeterminado.
int funcioSuma(int a, int b){ int resultado; //variable local resultado = a + b; //sentencia de la función return resultado; //valor de retorno }
// fichero suma.c #include <stdio.h> int funcioSuma(int a, int b){ int resultado; //variable local resultado = a + b; //sentencia de la función return resultado; //valor de retorno } int main(){ int x, y, r; //variables locales printf ("\nIntroduce el valor de x: "); scanf("%d",&x); printf ("Introduce el valor de y: "); scanf("%d",&y); r=funcioSuma(x,y); //llamamos a la función que hemos definido printf("La suma de x e y es: %d\n", r); }
4.2.8.Funciones de E/S
Función printf
-
Los caracteres que queremos mostrar por pantalla.
-
Órdenes de formato que indican cómo se mostrarán los parámetros.
%d |
Para mostrar un valor entero, el valor de una variable de tipo char o int. |
%ld |
Para mostrar un valor entero largo, el valor de una variable de tipo long. |
%c |
Para mostrar un carácter, el contenido de una variable de tipo char. |
%s |
Para mostrar una cadena de caracteres, el contenido de un vector de tipo char. |
%f |
Para mostrar un número real, el valor de una variable de tipo float o double. Se puede indicar el número de dígitos de la parte entera y de la parte decimal, utilizando la expresión siguiente: %[dígitos enteros].[dígitos decimales]f |
%p |
Para mostrar una dirección de memoria, por ejemplo el valor de un apuntador. |
\n |
carácter de salto de línea |
\t |
carácter de tabulación |
int x=5; float pi=3.1416; char msg[]="Hola", c='A'; printf("Hola!" ); printf ("Valor de x: %d. Dirección de x: %p\n", x, &x); printf ("El valor de PI con 1 entero y 2 decimales: %1.2f\n", pi); printf ("Contenido de msg: %s. Dirección de msg: %p\n", msg, msg); printf ("Valor de c: %c. El código ASCII guardado en c: %d\n", c, c); printf ("Constante entera: %d, y una letra: %c", 100, 'A');
Función scanf
int x; float f; char msg[5]; scanf("%d %f", &x, &f); // se lee un entero y un real // si escribimos: 2 3.5 y se aprieta ENTER, se asignará x=2 y f=3.5 // hará lo mismo si hacemos: 2 y se aprieta ENTER, 3.5 y se // aprieta ENTER scanf("%s", msg); // Al leer una cadena se añade un \0 al final
5.Conceptos de programación en ensamblador y C
5.1.Acceso a datos
.data var1 db 0 ;variable definida de 1 byte var2 db 61h ;variable definida de 1 byte var3 dw 0200h ;variable definida de 2 bytes var4 dd 0001E26Ch ;variable definida de 4 bytes
Dirección |
Valor |
var1 |
00h |
var2 |
61h |
var3 |
00h |
02h |
|
var4 |
6Ch |
E2h |
|
01h |
|
00h |
mov eax, dword[var1]
int vec[4]={1,2,3,4}; int x=258; //258=00000102h char c=3; int main() { int i=0; for (i=0;i<4;i++) printf("contenido de vec", vec[i]); c = x; }
char vec_char[4]; int vec_int[4]; long int l_int; short int s_int;
;Variables definidas en C que se utilizan en ensamblador extern vec_char, vec_int, l_int, s_int
5.1.1.Estructuras de datos
;direccionamiento indirecto mov edx, vec_int ;Tomamos la dirección del vector mov DWORD [edx], 0 ;Ponemos a 0 el primer elemento del vector vec_int add edx, 4 ;Sumamos 4 para acceder al siguiente elemento del vector add DWORD [edx], 0 ;Ponemos a 0 el segundo elemento del vector vec_int add edx, 4 ;Sumamos 4 para acceder al siguiente elemento del vector ... ;direccionamiento relativo mov al, byte [vec_char+0] ;primer elemento del vector, vec_char[0] mov bl, byte [vec_char+1] ;segundo elemento del vector, vec_char[1] mov eax, dword [vec_int+0] ;primer elemento del vector, vec_int[0] mov ebx, dword [vec_int+12] ;cuarto elemento del vector, vec_int[3] ;direccionamiento indexado mov esi, 0 ;Inicializamos el índice para acceder al vector. inicializa: mov dword [vec_int+esi],0 ;Ponemos a 0 un elemento del vector add esi, 4 ;Sumamos 4 para acceder al siguiente elemento del vector cmp esi, 12 ;Accedemos a vec_int[0], vec_int[1] jle inicializa ;vec_int[2] y vec_int[3] ;combinación indexado y relativo mov edx, vec_int ;Tomamos la dirección del vector mov esi, 0 ;Inicializamos el índice para acceder al vector. inicializa: mov dword [edx+esi*4],0 ;Ponemos a 0 un elemento del vector add esi, 1 ;Sumamos 1 para acceder al siguiente elemento del ;vector porque utilizamos el factor de escala 4 cmp esi, 3 ;Accedemos a vec_int[0], vec_int[1] jle inicializa ;vec_int[2] y vec_int[3]
int main(){ int vec[6]={1,2,3,4,5,6},mat[2][3]={{1,2,3},{4,5,6}}; int i,j, sumaVec=0, sumaMat=0; for (i=0;i<6;i++) sumaVec=sumaVec+vec[i]; for (i=0;i<2;i++){ for(j=0;j<3;j++) sumaMat=sumaMat+mat[i][j]; } }
vec dd 1,2,3,4,5,6 mat dd 1,2,3 dd 4,5,6 ;recorremos el vector para sumarlo mov esi,0 ;esi será el índice para acceder a los datos mov eax,0 ;eax será donde guardaremos la suma loop_vec: add eax, dword[vec+esi*4] ;multiplican el índice por 4 inc esi ;porque cada elemento ocupa 4 bytes. cmp esi, 6 ;comparamos con 6 porque es el índice del primer ;elemento fuera del vector. jl loop_vec ;recorremos la matriz con un solo índice para sumarla mov esi,0 ;esi será el índice para acceder a los datos mov ebx,0 ;ebx será donde guardaremos la suma loop_mat: add ebx, dword[mat+esi*4] ;multiplican el índice por 4 inc esi ;porque cada elemento ocupa 4 bytes. cmp esi, 6 ;comparamos con 6 porque es el índice del primer elemento ;fuera de la matriz, la matriz tiene 2*3=6 elementos. jl loop_mat ;recorremos la matriz con dos índices para sumarla mov esi,0 ;esi será el índice de la columna mov edi,0 ;edi será el índice de la fila mov ebx,0 ;ebx será donde guardaremos la suma loop_mat2: add ebx, dword[mat+edi+esi*4] ;multiplican el índice de la columna inc esi ;por 4 porque cada elemento ocupa 4 bytes. cmp esi, 3 ;comparamos con 3 porque son los elementos de una fila. jl loop_mat2 mov esi, 0 add edi, 12 ;cada fila ocupa 12 bytes, 3 elementos de 4 bytes. cmp edi, 24 ;comparamos con 24 porque es el primer índice jl loop_mat2 ; fuera de la matriz.
edi = 0 [mat+0+0*4] [mat+0+1*4] [mat+0+2*4] edi = 12 [mat+12+0*4] [mat+12+1*4] [mat+12+2*4]
5.1.2.Gestión de la pila
Supongamos que rax = 0102030405060708h (registro de 64 bits), bx = 0A0Bh (registro de 16 bits) y rsp = 0000000010203050h. push rax push bx pop bx pop rax
Dirección de memoria |
Estado inicial |
push rax |
push bx |
pop bx |
pop rax |
---|---|---|---|---|---|
10203044h |
|||||
10203045h |
|||||
10203046h |
0B |
||||
10203047h |
0A |
||||
10203048h |
08 |
08 |
08 |
||
10203049h |
07 |
07 |
07 |
||
1020304Ah |
06 |
06 |
06 |
||
1020304Bh |
05 |
05 |
05 |
||
1020304Ch |
04 |
04 |
04 |
||
1020304Dh |
03 |
03 |
03 |
||
1020304Eh |
02 |
02 |
02 |
||
1020304Fh |
01 |
01 |
01 |
||
10203050h |
xx |
xx |
xx |
xx |
xx |
RSP |
10203050h |
10203048h |
10203046h |
10203048h |
10203050h |
-
push rax. Decrementa RSP en 8 unidades y traslada el valor del registro RAX a partir de la posición de memoria indicada por RSP; funcionalmente la instrucción anterior sería equivalente a:
sub rsp, 8 mov qword[rsp], rax
-
push bx. Decrementa RSP en 2 unidades y traslada el valor del registro BX a partir de la posición de memoria indicada por RSP; funcionalmente la instrucción anterior sería equivalente a:
sub rsp, 2 mov word[rsp], bx
-
pop bx. Traslada hacia BX el valor de 2 bytes almacenado a partir de la posición de memoria indicada por RSP, a continuación se incrementa RSP en 2. Sería equivalente a efectuar:
mov bx, word [rsp] add rsp, 2
-
pop rax. Traslada hacia RAX el valor de 8 bytes almacenado a partir de la posición de memoria indicada por RSP, a continuación se incrementa RSP en 8. Sería equivalente a efectuar:
mov rax, qword [rsp] add rsp, 8
5.2.Operaciones aritméticas
r=(a+b)*4 / (c>>2);
mov eax [a] mov ebx, [b] add eax, ebx ;(a+b) imul eax, 4 ;(a+b)*4 mov ecx,[c] sar ecx, 2 ;(c>>2) idiv ecx ;(a+b)*4 / (c>>2) mov [r], eax ;r=(a+b)*4 / (c>>2)
5.3.Control de flujo
5.3.1.Estructura if
if (condición) { bloque de sentencias }
if (a > b) { maxA = 1; maxB = 0; }
mov rax, qword [a] ;Se cargan las variables en registros mov rbx, qword [b] cmp rax, rbx ;Se hace la comparación jg cierto ;Si se cumple la condición, salta a la etiqueta cierto jmp fin ;Si no se cumple la condición, salta a la etiqueta fin cierto: mov byte [maxA], 1 ;Estas instrucciones solo se ejecutan mov byte [maxB], 0 ;cuando se cumple la condición fin:
mov rax, qword [a] ;Se carga solo la variable a en un registro cmp rax, qword [b] ;Se hace la comparación jle fin ;Si se cumple la condición (a<=b) salta a fin mov byte [maxA], 1 ;Estas instrucciones solo se ejecutan mov byte [maxB], 0 ;cuando se cumple la condición (a > b) fin:
if ((a != b) || (a>=1 && a<=5)) { b = a; }
mov rax, qword [a] ;Se cargan las variables en registros mov rbx, qword [b] cmp rax, rbx ;Se hace la comparación (a != b) jne cierto ;Si se cumple la condición salta a la etiqueta cierto ;Si no se cumple, como es una OR, se puede cumplir la otra condición. cmp rax, 1 ;Se hace la comparación (a >= 1), si no se cumple, jl fin ;como es una AND, no hay que mirar la otra condición. cmp rax, 5 ;Se hace la comparación (a <= 5) jg fin ;Si no salta, se cumple que (a>=1 && a<=5) cierto: mov qword [b], rax ;Hacemos la asignación cuando la condición es cierta. fin:
5.3.2.Estructura if-else
if (condición) { bloque de sentencias } else { bloque de sentencias alternativas }
if (a > b) { max = 'a'; } else { // (a <= b) max = 'b'; }
mov rax, qword [a] ;Se cargan las variables en registros mov rbx, qword [b] cmp rax, rbx ;Se hace la comparación jg cierto ;Si se cumple la condición, se salta a cierto mov byte [max], 'b' ;else (a <= b) jmp fin cierto: mov byte [max], 'a' ;if (a > b) fin:
5.3.3.Estructura while
while (condiciones) { bloque de sentencias que ejecutar }
resultado=1; while (num > 1){ //mientras num sea mayor que 1 hacer ... resultado = resultado * num; num--; }
mov rax, 1 ;rax será [resultado] mov rbx, qword [num] ;Se carga la variable en un registro while: cmp rbx, 1 ;Se hace la comparación jg cierto ;Si se cumple la condición (num > 1) salta a cierto jmp fin ;Si no, salta a fin cierto: imul rax, rbx ;rax = rax * rbx dec rbx jmp while fin: mov qword [resultado], rax mov qword [num], rbx
mov rax, 1 ;rax será [resultado] while: cmp qword [num], 1 ;Se hace la comparación jle fi ;Si se cumple la condición (num <= 1) salta a fin imul rax, qword [num] ;rax=rax*[num] dec qword [num] jmp while fin: mov qword [resultado], rax
5.3.4.Estructura do-while
do { bloque de sentencias que ejecutar } while (condiciones);
resultado = 1; do { resultado = resultado * num; num--; } while (num > 1)
mov rax, 1 ;rax será [resultado] mov rbx, qword [num] ;Se carga la variable en un registro while: imul rax, rbx dec rbx cmp rbx, 1 ;Se hace la comparación jg while ;Si se cumple la condición salta a while mov qword [resultado], rax mov qword [num], rbx
5.3.5.Estructura for
for (valores iniciales; condiciones; actualización) { bloque de sentencias que ejecutar }
resultado=1; for (i = num; i > 1; i--) { resultado=resultado*i; }
mov rax, 1 ;rax será [resultado] mov rcx, qword [num] ;rcx será [i] que inicializamos con [num] for: cmp rcx, 1 ;Se hace la comparación jg cierto ;Si se cumple la condición, salta a cierto jmp fin cierto: imul rax,rcx dec rcx jmp for fin: mov qword [resultado], rax mov qword [i], rcx
5.3.6.Estructura switch-case
switch (variable) { case valor1: sentencia1; break; case valor2: sentencia2; ... break; ... default: sentenciaN; }
switch (var) { case 1: a=a+b; break; case 2: a=a-b; break; case 3: a=a*b; break; default: a=-a; }
switch: mov rax, qword [var] cmp rax, 1 jne case2 mov rbx, qword [b] add qword [a], rbx jmp end_s... case2: cmp rax, 2 jne case3 mov rbx, qword [b] sub qword [a], rbx jmp end_s case3: cmp rax, 3 jne default mov rbx, qword [b] mul qword [a], rbx jmp end_s default: neg qword [a] end_s:
5.4.Subrutinas y paso de parámetros
-
Definición de subrutinas en ensamblador.
-
Llamada y retorno de subrutina.
-
Paso de parámetros a la subrutina y retorno de resultados.
5.4.1.Definición de subrutinas en ensamblador
subrutina: ; ; Instrucciones de la subrutina ; ret
-
Debemos almacenar los registros modificados dentro de la subrutina para dejarlos en el mismo estado en el que se encontraban en el momento de hacer la llamada a la subrutina, salvo los registros que se utilicen para devolver un valor. Para almacenar los registros modificados utilizaremos la pila.
-
Para mantener la estructura de una subrutina y para que el programa funcione correctamente, no se pueden efectuar saltos a instrucciones de la subrutina; siempre finalizaremos la ejecución de la subrutina con la instrucción ret.
subrutina: ; Almacenar en la pila ; los registros modificados dentro de la subrutina. ; ; Instrucciones de la subrutina. ; ; Restaurar el estado de los registros modificados ; recuperando su valor inicial almacenado en la pila. ret
factorial: push rax ; Almacenamos en la pila los registros push rbx ; modificados dentro de la subrutina ; Instrucciones de la subrutina mov rax, 1 ; rax será el resultado mov rbx, 5 ; Calculamos el factorial del valor de rbx (=5). while: cmp rbx, 1 ; Hacemos la comparación jle fi ; Si se cumple la condición saltamos a fin imul rax, rbx dec rbx jmp while ; Salta a while fin: ; En rax tendremos el valor del factorial de 5 (=120) mov qword[result], rax ; Almacenamos el resultado en la variable 'result' pop rbx ; Restauramos el valor inicial de los registros pop rax ; en orden inverso a como los hemos almacenado ret ; Finaliza la ejecución de la subrutina
5.4.2.Llamada y retorno de subrutina en ensamblador
call factorial
sub rsp, 8 mov qword[rsp], rip mov rip, factorial
mov rip, qword[rsp] add rsp, 8
push rbp ; Almacenar el registro rbp en la pila mov rbp, rsp ; Asignar a rbp el valor del registro apuntador rsp ... mov rsp, rbp ; Restauramos el valor inicial de rsp pop rbp ; Restauramos el valor inicial de rbp ret
section .data x dq 5 ; Declaramos las variables que utilizaremos result dq 0 section .text global main ; Hacemos visible la etiqueta main factorial: push rbp ; Almacenar el registro que usaremos de apuntador ; a la pila rbp mov rbp, rsp ; Asignar a rbp el valor del registro apuntador rsp push rax ; Almacenamos en la pila los registros push rbx ; modificados dentro de la subrutina mov rax, 1 ; rax será el resultado mov rbx, qword[x] ; Calculamos el factorial de la variable 'x' (=5). while: cmp rbx, 1 ; Hacemos la comparación jle fi ; Si se cumple la condición saltamos a fin imul rax, rbx dec rbx jmp while ; Salta a while fin: ; En rax tendremos el valor del factorial de 'x' (=120) mov qword[result], rax ; Almacenamos el resultado en la variable 'result' pop rbx ; Restauramos el valor inicial de los registros pop rax ; en orden inverso a como los hemos almacenado mov rsp, rbp ; Restauramos el valor inicial de rsp con rbp pop rbp ; Restauramos el valor inicial de rbp ret ; Finaliza la ejecución de la subrutina main: call factorial ; Llamamos a la subrutina factorial mov rax, 1 mov rbx, 0 int 80h ; Finaliza la ejecución del programa
5.4.3.Paso de parámetros a la subrutina y retorno de resultados
Paso de parámetros y retorno de resultado por medio de registros
mov rbx, qword[x] ; Ponemos en el registro rbx el valor de la variable 'x' ; como parámetro de entrada call factorial ; Llamamos a la subrutina factorial ; En rax tendremos el valor del factorial de 'x' como mov qword[result],rax ; parámetro de salida y lo asignamos a la variable 'result'
factorial: push rbp ; Almacenar el registro que usaremos de apuntador a la pila rbp mov rbp, rsp ; Asignar a rbp el valor del registro apuntador rsp push rbx ; Almacenar en la pila el registro que modificamos ; y que no se utiliza para retornar el resultado mov rax, 1 ; rax será el resultado while: cmp rbx, 1 ; Hacemos la comparación jle fi ; Si se cumple la condición saltamos a fin imul rax, rbx dec rbx jmp while ; Salta a while fin: ; En rax tendremos el valor del factorial de rbx pop rbx ; Restauramos el valor inicial del registro mov rsp, rbp ; Restauramos el valor inicial de rsp con rbp pop rbp ; Restauramos el valor inicial de rbp ret
Paso de parámetros y retorno de resultado por medio de la pila
push rbp ; Almacenar el registro que usaremos de apuntador a la pila rbp mov rbp, rsp ; Asignar a rbp el valor del registro apuntador rsp ... mov rsp, rbp ; Restauramos el valor inicial de rsp con rbp pop rbp ; Restauramos el valor inicial de rbp
sub rsp,8 ; Reservamos 8 bytes para el resultado que retornamos push qword [x] ; Introducimos como parámetro de entrada a la pila ; el valor de la variable 'x' call factorial ; Llamamos a la subrutina factorial ; En la pila tendremos el valor del factorial de 'x' add rsp,8 ; Liberamos el espacio utilizado por el parámetro de entrada pop qword[result] ; Recuperamos el resultado retornado sobre la variable 'result'
factorial: push rbp ; Almacenar el registro que usaremos de apuntador a la pila rbp mov rbp, rsp ; Asignar a rbp el valor del registro apuntador rsp push rax ; Almacenar en la pila los registros que push rbx ; modificamos dentro de la subrutina ; Instrucciones de la subrutina mov rax, 1 ; rax será el resultado mov rbx, [rbp+16]; [rbp+16] referencia al parámetro de entrada while: cmp rbx, 1 ; Hacemos la comparación jle fi ; Si se cumple la condición saltamos a fin imul rax, rbx dec rbx jmp while ; Salta a while fin: ; En rax tendremos el valor del factorial de rbx mov [rbp+24], rax; [rbp+24] espacio que hemos reservado en la pila ; para retornar el resultado pop rbx ; Restauramos el valor inicial del registro pop rax mov rsp, rbp ; Restauramos el valor inicial de rsp con rbp pop rbp ; Restauramos el valor inicial de rbp ret
Dirección |
Estado inicial |
sub rsp,8 |
push qword[x] |
call factorial |
||||
---|---|---|---|---|---|---|---|---|
apuntadores |
pila |
apuntadores |
pila |
apuntadores |
pila |
apuntadores |
pila |
|
@ - 48 |
||||||||
@ - 40 |
||||||||
@ - 32 |
||||||||
@ - 24 |
rsp→ |
@retorn |
||||||
@ - 16 |
rsp→ |
5 |
5 |
|||||
@ - 8 |
rsp→ |
---- |
---- |
---- |
||||
@ |
rsp→ |
---- |
---- |
---- |
---- |
push rbp mov rbp, rsp |
push rax push rbx |
mov rbx, [rbp+16] |
mov [rbp+24], rax |
|||||
---|---|---|---|---|---|---|---|---|
apuntadores |
pila |
apuntadores |
pila |
apuntadores |
pila |
apuntadores |
pila |
|
@ - 48 |
rsp→ |
rbx |
rsp→ |
rbx |
rsp→ |
rbx |
||
@ - 40 |
rax |
rax |
rax |
|||||
@ - 32 |
rsp, rbp→ |
rbp |
rbp→ |
rbp |
rbp→ |
rbp |
rbp→ |
rbp |
@ - 24 |
@retorn |
@retorn |
@retorn |
@retorn |
||||
@ - 16 |
5 |
5 |
rbp+16→ |
5 |
5 |
|||
@ - 8 |
---- |
---- |
---- |
rbp+24→ |
120 |
|||
@ |
---- |
---- |
---- |
---- |
pop rbx pop rax mov rsp,rbp pop rbp |
ret |
add rsp,8 |
pop qword[result] |
|||||
---|---|---|---|---|---|---|---|---|
apuntadores |
pila |
apuntadores |
pila |
apuntadores |
pila |
apuntadores |
pila |
|
@ - 48 |
||||||||
@ - 40 |
||||||||
@ - 32 |
||||||||
@ - 24 |
rsp→ |
@retorn |
||||||
@ - 16 |
5 |
rsp→ |
5 |
---- |
||||
@ - 8 |
120 |
120 |
rsp→ |
120 |
---- |
|||
@ |
---- |
---- |
---- |
rsp→ |
---- |
mov rsp, rbp ;Restauramos el valor inicial de RSP con RBP pop rbp ;Restauramos el valor inicial de RBP
pop rax ;rax contendrá la dirección de retorno pop rdx ;recuperamos los parámetros. pop rcx push rax ;Volvemos a introducir la dirección de retorno
add rsp,8 ;liberamos el espacio utilizado por el parámetro de entrada
ret 8
5.4.4.Variables locales en ensamblador
long int factorial (long int x) { long int j, result; result=1; for (j = x; j > 1; j--){ result=result*j; } return result; } |
factorial: push rbp mov rbp, rsp push rbx mov rax, 1 mov rbx, rdi for: cmp rbx, 1 jle fi imul rax, rbx dec rbx jmp for fi: pop rbx mov rsp, rbp pop rbp ret |
subrutina: push rbp ; Almacenar el registro que utilizaremos ; de apuntador a la pila rbp mov rbp, rsp ; Asignar a RBP el valor del registro apuntador RSP sub rsp, n ; n indica el número de bytes reservados para las ; variables locales ; ; Instrucciones de la subrutina ; mov rsp, rbp ; Restauramos el valor inicial de RSP con RBP pop rbp ; Restauramos el valor inicial de RBP ret
push rbp ;Almacenar el registro que utilizaremos ;de apuntador a la pila rbp mov rbp, rsp ;Asignar a RBP el valor del registro apuntador RSP sub rsp, 8 ;reservamos 8 bytes para variables locales mov al, byte[RBP-1] ;accedemos a 1 byte de almacenamiento local mov ax, word[RBP-2] ;accedemos a 2 bytes de almacenamiento local mov eax, dword[RBP-4] ;accedemos a 4 bytes de almacenamiento local mov rax, qword[RBP-8] ;accedemos a 8 bytes de almacenamiento local
5.4.5.Llamadas a subrutinas y paso de parámetros desde C
Definición e implementación de funciones en ensamblador
section .text ;Declararemos con global el nombre de las subrutinas a las que ;queremos acceder desde otros ficheros de código fuente global subrutina1, ..., subrutina ;Declaramos con extern las variables globales de C a las que ;queremos acceder desde ensamblador extern variable1, ..., variableN subrutina1: push rbp ;Almacenar el registro rbp en la pila mov rbp,rsp ;Asignar el valor del registro apuntador RSP sub rsp, n ;Reservamos n bytes para variables locales ; ; código de la subrutina ; mov rsp, rbp ;Restauramos el valor inicial de RSP con RBP pop rbp ;Restauramos el valor inicial de RBP ret ... subrutinaN: push rbp ;Almacenar el registro rbp en la pila mov rbp,rsp ;Asignar el valor del registro apuntador RSP sub rsp, n ;Reservamos n bytes para variables locales ; ; código de la subrutina ; mov rsp, rbp ;Restauramos el valor inicial de RSP con RBP pop rbp ;Restauramos el valor inicial de RBP ret
.text global factorial ; Declaramos con global la subrutina de ensamblador ; que queremos hacer visible en el código C extern x, result ; Declaramos con extern las variables globales de C ; a las que queremos acceder desde ensamblador factorial: push rbp mov rbp, rsp push rax ; Almacenamos en la pila los registros push rbx ; modificados dentro de la subrutina mov rax, 1 ; rax será el resultado mov rbx, [x] ; Calculamos el factorial de la variable 'x' (=5) while: cmp rbx, 1 ; Hacemos la comparación jle fi ; Si se cumple la condición saltamos a fin imul rax, rbx ; En rax tendremos el valor del factorial de 'x' (=120) dec rbx jmp while ; Salta a while fin: mov [result], rax ; Almacenamos el resultado en la variable result pop rbx ; Restauramos el valor inicial de los registros pop rax ; en orden inverso a como los hemos almacenado mov rsp, rbp pop rbp ret
long int x, result; //Variables globales a las cuales se accede desde ensamblador int main() { x=5; factorial(); //Llamada a la subrutina de ensamblador return 0; }
;Fichero funciones.asm section .text global suma, producto, factorial suma: push rbp mov rbp, rsp ;2 parámetros de entrada: rdi, rsi mov rax, rdi add rax, rsi mov rsp, rbp pop rbp ret ;retorno de resultado por medio de rax, rax=rdi+rsi producto: push rbp mov rbp, rsp ;2 parámetros de entrada: rdi, rsi mov rax, rdi imul rax, rsi ;rax=rax*rsi=rdi*rsi mov rsp, rbp pop rbp ret ;retorno de resultado por medio de rax factorial: push rbp mov rbp, rsp ;1 parámetro de entrada: rdi ;no hay variables locales push rdi ;rdi es modificado por la subrutina mov rax, 1 ;rax será el resultado while: cmp rdi, 1 ; Hacemos la comparación jle fi ; Si se cumple la condición saltamos a fin imul rax, rdi dec rdi jmp while ; Salta a while fin: ; En rax tendremos el valor del factorial de rbx pop rdi ;restauramos el valor de rdi mov rsp, rbp pop rbp ret
//Fichero principal.c #include <stdio.h> int main() { int x, y, result; printf("\nIntroduce el valor de x: "); scanf("%d", &x); printf("Introduce el valor de y: "); scanf("%d", &y); result = suma(x, y); // Llamamos a la subrutina suma pasando las variables // x e y como parámetros y retornando // el resultado sobre la variable result printf ("La suma de x e y es %d\n", result); result = producto(x, y); // Llamamos a la subrutina producto pasando las // variables x e y como parámetros y retornando // el resultado sobre la variable result printf ("El producto de x e y es %d\n", result); result = factorial(x); // Llamamos a la subrutina factorial pasando la // variable x como parámetro y retornando // el resultado sobre la variable result printf ("El factorial de x es %d\n", result); return 0; }
Parámetros por referencia
section .text global inivec, getvec inivec: push rbp mov rbp, rsp ; rdi: primer parámetro, dirección del vector ; rsi: segundo parámetro, índice del vector mov byte [rdi+rsi], 0 ; se pone a cero el elemento del vector mov rsp, rbp pop rbp ret getvec: push rbp mov rbp, rsp ; rdi: primer parámetro, dirección del vector ; rsi: segundo parámetro, índice del vector ; rdx: tercer parámetro, variable pasada por ; referencia mov byte al, [rdi+rsi] ; se asigna al registro 'al' el elemento del vector mov byte [rdx], al ; se pasa el elemento del vector a la variable recibida ; como parámetro mov rsp, rbp pop rbp ret
int main(){ int j; char v[5],x; for(j=0;j<5;j++){ inivec(v,j); // v se pasa por referencia, se modifica dentro de la // subrutina. El nombre de una variable de tipo vector // representa la dirección del primer elemento del vector. // j es el índice del vector al cual queremos acceder // y se pasa por valor } j=0; getvec(v, j, &x) // x se pasa por referencia, se modifica dentro de la // subrutina con el valor del elemento que ocupa la // posición j dentro del vector v. v se pasa por // referencia y j se pasa por valor }
5.5.Entrada/salida
5.5.1.E/S programada
02: minutos
04: hora
08: mes del año
//Fichero: ioprog.c #include <stdio.h> #include <stdlib.h> #include <sys/io.h> #define RTC_ADDR 0x70 // registro de direcciones del RTC #define RTC_DATA 0x71 // registro de datos del RTC int main() { char month, hh, mm; // Se define cada mes del año de 10 caracteres // 'septiembre', 'noviembre' y 'diciembre' son los más largos // y ocupan 10 caracteres incluido un \0 al final char meses[12][10]={"enero", "febrero", "marzo", "abril", "mayo", "junio", "julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre"}; /* Se proporciona permiso para acceder a los puertos de E/S * RTC_ADDR dirección inicial a partir de la cual se quiere acceder * 2: se pide acceso a 2 bytes * 1: se activa el permiso de acceso */ if (ioperm(RTC_ADDR, 2, 1)) { perror("ioperm"); exit(1); } outb(8, RTC_ADDR); // Se escribe en el registro de direcciones la dirección 8: mes month=inb(RTC_DATA); // Se lee del puerto de datos el mes del año // El valor obtenido es el número de mes entre 1 y 12, pero el índice // del vector meses está entre 0 y 11 y hay que restar 1 printf("Mes año RTC: %d %s\n", month, meses[month-1]); outb(4, RTC_ADDR); // Se escribe en el registro de direcciones la dirección 4: hora hh=inb(RTC_DATA); // Se lee del puerto de datos la hora printf("Hora RTC: %0x:", hh); outb(2, RTC_ADDR); // Se escribe en el registro de direcciones la dirección 2: minutos mm=inb(RTC_DATA); // Se leen del puerto de datos los minutos printf("%0x\n", mm); // Se desactivan los permisos para acceder a los puertos poniendo un 0 if (ioperm(RTC_ADDR, 2, 0)) { perror("ioperm"); exit(1); } return 0; }
#include <stdio.h> #include <stdio.h> #include <stdlib.h> #include <sys/io.h> #define KBD_DATA 0x60 int main(){ int salir=0; char data; if (ioperm(KBD_DATA, 1, 1)) { perror("ioperm"); exit(1); } while (salir==0){ data=inb(KBD_DATA); // se muestra el código de la tecla pulsada, scancode // se hace una AND lógica ('&') para tomar solo los // 7 bits menos significativos del dato leído printf("tecla: %0x\n", data & 0b01111111); // se pulsa ESC para salir del bucle, scancode=1 if ((data & 0b01111111) == 1) salir=1; } if (ioperm(KBD_DATA, 1, 0)) { perror("ioperm"); exit(1); } return 0; }
5.6.Controlar la consola
ESC[F;CH
section .data escSeq db 27,"[05;10H" escLen equ 8 section .text mov rax,4 mov rbx,1 mov rcx, escSeq mov rdx, escLen int 80h
section .data escSeq db 27,"[2J" ;ESC[2J escLen equ 4 ; tamaño de la cadena escSeq section .text mov rax,4 mov rbx,1 mov rcx, escSeq mov rdx, escLen int 80h
#include <stdio.h> int main(){ printf("\x1B[2J"); //Borra la pantalla printf("\x1B[5;10H"); //Sitúa el cursor en la posición (5,10) }
5.7.Funciones del sistema operativo (system calls)
-
Lectura del teclado.
-
Escritura por pantalla.
-
Retorno al sistema operativo.
5.7.1.Lectura de una cadena de caracteres desde el teclado
-
RAX = 3
-
RBX = 0, descriptor correspondiente a la entrada estándar (teclado)
-
RCX = dirección de la variable de memoria donde se guardará la cadena leída
-
RDX = número máximo de caracteres que se leerán
-
RAX = número de caracteres leídos
-
La variable indicada se llena con los caracteres leídos.
-
RAX = 0
-
RDI = 0, descriptor correspondiente a la entrada estándar (teclado)
-
RSI = dirección de la variable de memoria donde se guardará la cadena leída
-
RDX = número máximo de caracteres que se leerán
-
RAX = número de caracteres leídos
section .bss buffer resb 10 ;se reservan 10 bytes para hacer la lectura del teclado section .text ;lectura utilizando int 80h mov rax, 3 mov rbx, 0 mov rcx, buffer ; se carga la dirección de buffer en rcx mov rdx,10 ; número máximo de caracteres que se leerán int 80h ; se llama al kernel ;lectura utilizando syscall mov rax, 0 mov rdi, 0 mov rsi, buffer ; se carga la dirección de buffer en rsi mov rdx,10 ; número máximo de caracteres que se leerán syscall ; se llama al kernel
5.7.2.Escritura de una cadena de caracteres por pantalla
-
RAX = 4
-
RBX = 1, descriptor correspondiente a la salida estándar (pantalla)
-
RCX = dirección de la variable de memoria que queremos escribir, la variable ha de estar definida con un byte 0 al final
-
RDX = tamaño de la cadena que queremos escribir en bytes, incluido el 0 del final
-
RAX = número de caracteres escritos
-
RAX = 1
-
RDI = 1, descriptor correspondiente a la salida estándar (pantalla)
-
RSI = dirección de la variable de memoria que queremos escribir, la variable ha de estar definida con un byte 0 al final
-
RDX = tamaño de la cadena que queremos escribir en bytes, incluido el 0 del final
-
RAX = número de caracteres escritos
Section .data msg db "Hola!",0 msgLen db 6 section .text ; escritura utilizando int 80h mov rax,4 mov rbx,1 mov rcx, msg ; se pone la dirección de msg1 en rcx mov rdx, msgLen ; tamaño de msg int 80h ; se llama al kernel ;escritura utilizando syscall mov rax, 1 mov rdi, 1 mov rsi, msg ; se carga la dirección de msg en rsi mov rdx, msglen ; tamaño de msg syscall ; se llama al kernel
section .data msg db "Hola!",0 msgLen db equ $ - msg ; $ indica la dirección de la posición actual
5.7.3.Retorno al sistema operativo (exit)
-
RAX = 1
-
RBX = valor de retorno del programa
-
RAX = 60
-
RDI = valor de retorno del programa
;retorna al sistema operativo utilizando int 80h mov rax,1 mov rbx,0 ;valor de retorno 0 int 80h ; se llama al kernel ;retorna al sistema operativo utilizando syscall mov rax,60 mov rbx,0 ;valor de retorno 0 syscall ;se llama al kernel
6.Anexo: manual básico del juego de instrucciones
-
OF: Overflow flag (bit de desbordamiento)
-
TF: Trap flag (bit de excepción)
-
AF: Aux carry (bit de transporte auxiliar)
-
DF: Direction flag (bit de dirección)
-
SF: Sign flag (bit de signo)
-
PF: Parity flag (bit de paridad)
-
IF: Interrupt flag (bit de interrupción)
-
ZF: Cero flag (bit de cero)
-
CF: Carry flag (bit de transporte)
-
Inmediato de 8 bits: sin signo [0, 255], con signo en Ca2 [–128, +127]
-
Inmediato de 16 bits: sin signo [0, 65.535], con signo en Ca2 [–32.768, +32.767]
-
Inmediato de 32 bits: sin signo [0, 4.294.967.295], con signo en Ca2 [–2.147.483.648, +2.147.483.647]
-
sin signo [0, 18.446.744.073.709.551.615],
-
con signo en Ca2 [–9.223.372.036.854.775.808, + 9.223.372.036.854.775.807]
reg8: registro, tiene que ser un registro de 8 bits.
reg16: registro, tiene que ser un registro de 16 bits.
reg32: registro, tiene que ser un registro de 32 bits.
reg64: registro, tiene que ser un registro de 64 bits.
-
BYTE: posición de memoria de 8 bits
-
WORD: posición de memoria de 16 bits
-
DWORD: posición de memoria de 32 bits
-
QWORD: posición de memoria de 64 bits
6.1.ADC: suma aritmética con bit de transporte
ADC R9,RAX ADC RAX, QWORD [variable] ADC DWORD [variable],EAX ADC RAX,0x01020304 ADC BYTE [vector+RAX], 5
6.2.ADD: suma aritmética
ADD R9,RAX ADD RAX, QWORD [variable] ADD DWORD [variable],EAX ADD RAX,0x01020304 ADD BYTE [vector+RAX], 5
6.3.AND: Y lógica
x y x AND y --- --- ------- 0 0 0 0 1 0 1 0 0 1 1 1
AND R9,RAX AND RAX, QWORD [variable] AND DWORD [variable],EAX AND RAX,0x01020304 AND BYTE [vector+RAX], 5
6.4.CALL: llamada a subrutina
M[RSP]← RIP
RIP ← dirección_etiqueta
CALL subrutina1
6.5.CMP: comparación aritmética
CMP R9,RAX CMP RAX, QWORD [variable] CMP DWORD [variable],EAX CMP RAX,0x01020304 CMP BYTE [vector+RAX], 5
6.6.DEC: decrementa el operando
DEC EAX DEC R9 DEC DWORD [R8] DEC QWORD [variable]
6.7.DIV: división entera sin signo
DIV R8B ; AX / R8B => Cociente en AL; resto en AH DIV R8W ; DX:AX / R8W => Cociente en AX; resto en DX DIV ECX ; EDX:EAX / ECX => Cociente en EAX; resto en EDX DIV QWORD [R9] ; RDX:RAX / QWORD [R9] => Cociente en RAX, resto en RDX
6.8.IDIV: división entera con signo
IDIV CH ; AX / CH => Cociente en AL; resto en AH IDIV BX ; DX:AX / BX => Cociente en AX; resto en DX IDIV ECX ; EDX:EAX / ECX => Cociente en EAX; resto en EDX IDIV QWORD [R9] ; RDX:RAX / [R9] => Cociente en RAX, resto en RDX
6.9.IMUL: multiplicación entera con signo
IMUL destino, fuente
6.9.1.IMUL fuente: un operando explícito
IMUL ECX ; EAX * ECX => EDX:EAX IMUL QWORD [RBX] ; AX * [RBX] => RDX:RAX
6.9.2.IMUL destino, fuente: dos operandos explícitos
IMUL EAX, 4 IMUL RAX, R9 IMUL RAX, QWORD [var]
6.10.IN: lectura de un puerto de entrada/salida
-
AL se lee un byte del puerto
-
AX se leen dos bytes
-
EAX se leen cuatro bytes
IN AL, 60 h IN AL, DX
6.11.INC: incrementa el operando
INC AL INC R9 INC BYTE [RBP] INC QWORD [var1]
6.12.INT: llamada a una interrupción software
M(RSP) ← EFLAGS
RSP=RSP-8
M(RSP) ← RIP
RIP← dirección rutina de servicio
INT 80h
6.13.IRET: retorno de interrupción
RSP=RSP+8
EFLAGS ← M(RSP)
RSP=RSP+8
IRET
6.14.Jxx: salto condicional
Instrucción Descripción Condición ---------- ------------------------------------------ -------------- JA/JNBE (Jump If Above/Jump If Not Below or Equal) CF=0 y ZF=0 JAE/JNB (Jump If Above or Equal/Jump If Not Below) CF=0 JB/JNAE (Jump If Below/Jump If Not Above or Equal) CF=1 JBE/JNA (Jump If Below or Equal/Jump If Not Above) CF=1 o ZF=1
Instrucción Descripción Condición ---------- ------------------------------------------ -------------- JE/JZ (Jump If Equal/Jump If Zero) ZF=1 JNE/JNZ (Jump If Not Equal/Jump If Not Zero) ZF=0 JG/JNLE (Jump If Greater/Jump If Not Less or Equal) ZF=0 y SF=OF JGE/JNL (Jump If Greater or Equal/Jump If Not Less) SF=OF JL/JNGE (Jump If Less/Jump If Not Greater or Equal) S≠FOF JLE/JNG (Jump If Less or Equal/Jump If Not Greater) ZF=1 o S≠FOF
Instrucción Descripción Condición ---------- ------------------------------------------ -------------- JC (Jump If Carry flag set) CF=1 JNC (Jump If Carry flag Not set) CF=0 JO (Jump If Overflow flag set) OF=1 JNO (Jump If Overflow flag Not set) OF=0 JS (Jump If Sign flag set) SF=1 JNS (Jump If Sign flag Not set) SF=0
JE etiqueta1 ;salta si Z=1 JG etiqueta2 ;salta si Z=0 y SF=OF JL etiqueta3 ;salta si S≠FOF
6.15.JMP: salto incondicional
JMP bucle
6.16.LOOP: bucle hasta RCX=0
JNE etiqueta
MOV RCX, 10 bucle: ; ;Las instrucciones se repetirán 10 veces ; LOOP bucle
6.17.MOV: transferir un dato
MOV RAX,R9 MOV RAX,QWORD [variable] MOV QWORD [variable], RAX MOV RAX,0102030405060708h MOV WORD [RAX],0B80h
6.18.MOVSX/MOVSXD: transferir un dato con extensión de signo
MOVSXD destino, fuente
MOVSX reg16, BYTE imm/mem
MOVSX reg32, BYTE mem
;reg8 puede ser cualquier registro de 8 bits excepto: AH, BH, CH, DH
MOVSX reg64, BYTE mem
MOVSX reg32, WORD mem
MOVSX reg64, WORD mem
MOVSXD reg64, DWORD mem
MOVSX AX,BL ;extensión de signo de 8 bits a 16 bits MOVSX EAX,BL ;extensión de signo de 8 bits a 32 bits MOVSX RAX,BL ;extensión de signo de 8 bits a 64 bits MOVSX RAX, BYTE [var] MOVSX EAX,BX ;extensión de signo de 16 bits a 32 bits MOVSX RAX,BX ;extensión de signo de 16 bits a 64 bits MOVSXD RAX, EBX ;extensión de signo de 32 bits a 64 bits MOVSXD RAX, DWORD [var]
6.19.MOVZX: transferir un dato añadiendo ceros
MOVZX reg16, BYTE mem
MOVZX reg32, BYTE mem
;reg8 puede ser cualquier registro de 8 bits excepto: AH, BH, CH, DH
MOVZX reg64, BYTE mem
MOVZX reg32, WORD mem
MOVZX reg64, WORD mem
MOVZX AX,BL ;extensión con ceros de 8 bits a 16 bits MOVZX EAX,BL ;extensión con ceros de 8 bits a 32 bits MOVZX RAX,BL ;extensión con ceros de 8 bits a 64 bits MOVZX RAX, BYTE [var] MOVZX EAX,BX ;extensión con ceros de 16 bits a 32 bits MOVZX RAX,BX ;extensión con ceros de 16 bits a 64 bits MOVZX RAX, WORD [var]
6.20.MUL: multiplicación entera sin signo
MUL CH ; AL * CH --> AX MUL BX ; AX * BX --> DX:AX MUL RCX ; RAX * RCX --> RDX:RAX MUL WORD [BX+DI] ; AX * [BX+DI]--> DX:AX
6.21.NEG: negación aritmética en complemento a 2
NEG RCX NEG DWORD [variable]
6.22.NOT: negación lógica (negación en complemento a 1)
NOT RAX NOT QWORD [variable]
6.23.OUT: escritura en un puerto de entrada/salida
-
AL se escribe un byte
-
AX se escriben dos bytes
-
EAX se escriben cuatro bytes
OUT 60h, AL OUT DX, AL
6.24.OR: o lógica
x y x OR y --- --- ------ 0 0 0 0 1 1 1 0 1 1 1 1
OR R9, RAX OR RAX, QWORD [variable] OR DWORD [variable], EAX OR RAX, 0x01020304 OR BYTE [vector+RAX], 5
6.25.POP: extraer el valor de la cima de la pila
MOV destino, [RSP] ADD RSP, <tamaño del operando>
POP RAX
MOV RAX, [RSP] ADD RSP, 8
POP AX POP RAX POP WORD [variable] POP QWORD [RBX]
6.26.PUSH: introducir un valor en la pila
SUB RSP, <tamaño del operando> MOV [RSP], fuente
PUSH RAX
SUB RSP, 8 MOV [RSP], RAX
PUSH AX PUSH RAX PUSH WORD [variable] PUSH QWORD [RBX] PUSH 0Ah PUSH 0A0Bh PUSH 0A0B0C0Dh
6.27.RET: retorno de subrutina
RSP = RSP + 8
RET
6.28.ROL: rotación a la izquierda
ROL RAX,CL ROL RAX,1 ROL DWORD [RBX],CL ROL QWORD [variable],4
6.29.ROR: rotación a la derecha
ROR RAX,CL ROR RAX,1 ROR DWORD [RBX],CL ROR QWORD [variable],4
6.30.SAL: desplazamiento aritmético (o lógico) a la izquierda
SAL RAX,CL SAL RAX,1 SAL DWORD [RBX],CL SAL QWORD [variable],4
6.31.SAR: desplazamiento aritmético a la derecha
SAR RAX,CL SAR RAX,1 SAR DWORD [RBX],CL SAR QWORD [variable],4
6.32.SBB: resta con transporte (borrow)
SBB R9,RAX SBB RAX, QWORD [variable] SBB DWORD [variable],EAX SBB RAX,0x01020304 SBB BYTE [vector+RAX], 5
6.33.SHL: desplazamiento lógico a la izquierda
6.34.SHR: desplazamiento lógico a la derecha
SHR RAX,CL SHR RAX,1 SHR DWORD [RBX],CL SHR QWORD [variable],4
6.35.SUB: resta sin transporte
SUB R9,RAX SUB RAX, QWORD [variable] SUB DWORD [variable],EAX SUB RAX,0x01020304 SUB BYTE [vector+RAX], 5
6.36.TEST: comparación lógica
TEST R9,RAX TEST RAX, QWORD [variable] TEST DWORD [variable],EAX TEST RAX,0x01020304 TEST BYTE [vector+RAX], 5
6.37.XCHG: intercambio de operandos
XCHG R9, RAX XCHG RAX, QWORD [variable] XCHG DWORD [variable], EAX
6.38.XOR: o exclusiva
x y x XOR y --- --- ------- 0 0 0 0 1 1 1 0 1 1 1 0
XOR R9, RAX XOR RAX, QWORD [variable] XOR DWORD [variable], EAX XOR RAX, 01020304 h XOR BYTE [vector+RAX], 5