0% encontró este documento útil (0 votos)
75 vistas31 páginas

Assembly by Joseph

Assessment

Cargado por

Joshep Torres
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
75 vistas31 páginas

Assembly by Joseph

Assessment

Cargado por

Joshep Torres
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 31

Assembly Language

#assembly
Tabla de contenido
Acerca de 1

Capítulo 1: Empezando con el lenguaje ensamblador 2

Observaciones 2

Examples 2

Introducción 2

Codigo de maquina 4

Hola mundo para Linux x86_64 (Intel 64 bit) 5

Hola mundo para OS X (x86_64, gas de sintaxis de Intel) 6

Ejecutando ensamblaje x86 en Visual Studio 2015 7

Capítulo 2: Control de flujo 12

Introducción 12

Examples 12

Trivial IF-THEN-ELSE en m68k Assembly 12

PARA ... SIGUIENTE en Z80 Asamblea 12

If-statement en ensamblaje de sintaxis Intel 13

La condición de bucle while es verdadera en el ensamblaje de sintaxis Intel 14

Capítulo 3: Ejemplos de Linux elf64 que no usan glibc 15

Examples 15

Interfaz de usuario 15

Subrtx.asm 15

Generic.asm 17

Makefile 18

Capítulo 4: Interrupciones 20

Observaciones 20

Examples 20

Trabajando con interrupciones en la Z80: 20

Capítulo 5: La pila 21

Observaciones 21

Examples 21

Zilog Z80 Stack 21


Capítulo 6: Registros 23

Observaciones 23

Examples 23

Zilog Z80 se registra 24

x86 registros 25

x64 registros 26

Creditos 28
Acerca de
You can share this PDF with anyone you feel could benefit from it, downloaded the latest version
from: assembly-language

It is an unofficial and free Assembly Language ebook created for educational purposes. All the
content is extracted from Stack Overflow Documentation, which is written by many hardworking
individuals at Stack Overflow. It is neither affiliated with Stack Overflow nor official Assembly
Language.

The content is released under Creative Commons BY-SA, and the list of contributors to each
chapter are provided in the credits section at the end of this book. Images may be copyright of
their respective owners unless otherwise specified. All trademarks and registered trademarks are
the property of their respective company owners.

Use the content presented in this book at your own risk; it is not guaranteed to be correct nor
accurate, please send your feedback and corrections to [email protected]

https://riptutorial.com/es/home 1
Capítulo 1: Empezando con el lenguaje
ensamblador
Observaciones
Ensamblaje es un nombre general que se usa para muchas formas de código de máquina legibles
por humanos. Naturalmente, difiere mucho entre diferentes CPU (unidad central de
procesamiento), pero también en una sola CPU pueden existir varios dialectos de ensamblaje
incompatibles, cada uno compilado por un ensamblador diferente, en el código de máquina
idéntico definido por el creador de la CPU.

Si desea hacer una pregunta sobre su propio problema de ensamblaje, siempre indique qué HW y
qué ensamblador está utilizando, de lo contrario será difícil responder a su pregunta en detalle.

El Ensamblaje de Aprendizaje de una sola CPU en particular ayudará a aprender lo básico sobre
diferentes CPU, pero cada arquitectura de HW puede tener diferencias considerables en los
detalles, por lo que aprender ASM para una nueva plataforma puede estar cerca de aprenderlo
desde cero.

Campo de golf:

Wikilibro de la Asamblea X86

Examples
Introducción

El lenguaje ensamblador es una forma humana legible de lenguaje de máquina o código de


máquina que es la secuencia real de bits y bytes en los que opera la lógica del procesador. En
general, es más fácil para los humanos leer y programar en mnemotécnicas que en binario, octal
o hexadecimal, por lo que los humanos suelen escribir código en lenguaje ensamblador y luego
usar uno o más programas para convertirlo al formato de lenguaje de máquina que entiende el
procesador.

EJEMPLO:

mov eax, 4
cmp eax, 5
je point

Un ensamblador es un programa que lee el programa de lenguaje ensamblador, lo analiza y


produce el lenguaje de máquina correspondiente. Es importante comprender que, a diferencia de
un lenguaje como C ++ que es un lenguaje único definido en un documento estándar, existen
muchos lenguajes ensambladores diferentes. Cada arquitectura de procesador, ARM, MIPS, x86,
etc. tiene un código de máquina diferente y, por lo tanto, un lenguaje ensamblador diferente.

https://riptutorial.com/es/home 2
Además, a veces hay varios lenguajes de ensamblaje diferentes para la misma arquitectura de
procesador. En particular, la familia de procesadores x86 tiene dos formatos populares que a
menudo se denominan sintaxis de gas ( gas es el nombre del ejecutable para el ensamblador
GNU) y la sintaxis Intel (denominada así por el autor de la familia de procesadores x86). Son
diferentes pero equivalentes, ya que uno puede escribir cualquier programa dado en cualquier
sintaxis.

En general, el inventor del procesador documenta el procesador y su código de máquina y crea


un lenguaje ensamblador. Es común que ese lenguaje ensamblador en particular sea el único
utilizado, pero a diferencia de los escritores de compiladores que intentan ajustarse a un estándar
de lenguaje, el lenguaje ensamblador definido por el inventor del procesador es usualmente la
versión que usan las personas que escriben ensambladores. .

Hay dos tipos generales de procesadores:

• CISC (Computadora de conjunto de instrucciones complejas): tiene muchas instrucciones


de lenguaje de máquina diferentes ya menudo complejas

• RISC (Computadoras de conjunto de instrucciones reducidas): por el contrario, tiene menos


instrucciones y más simples

Para un programador en lenguaje ensamblador, la diferencia es que un procesador CISC puede


tener muchas instrucciones para aprender, pero a menudo hay instrucciones adecuadas para una
tarea en particular, mientras que los procesadores RISC tienen menos instrucciones y más
simples, pero cualquier operación dada puede requerir el programador en lenguaje ensamblador.
Escribir más instrucciones para hacer lo mismo.

Los compiladores de otros lenguajes de programación a veces producen el ensamblador primero,


que luego se compila en un código de máquina llamando a un ensamblador. Por ejemplo, gcc
utiliza su propio ensamblador de gas en la etapa final de compilación. El código de máquina
producido a menudo se almacena en archivos de objetos , que pueden vincularse a ejecutables
mediante el programa enlazador.

Una "cadena de herramientas" completa a menudo consiste en un compilador, ensamblador y


enlazador. Entonces se puede usar ese ensamblador y enlazador directamente para escribir
programas en lenguaje ensamblador. En el mundo GNU, el paquete binutils contiene el
ensamblador y el enlazador y las herramientas relacionadas; aquellos que están interesados
únicamente en la programación en lenguaje ensamblador no necesitan gcc u otros paquetes de
compilación.

Los microcontroladores pequeños a menudo se programan únicamente en lenguaje ensamblador


o en una combinación de lenguaje ensamblador y uno o más lenguajes de nivel superior como C
o C ++. Esto se hace porque a menudo se pueden usar los aspectos particulares de la
arquitectura del conjunto de instrucciones para que dichos dispositivos escriban códigos más
compactos y eficientes de lo que sería posible en un lenguaje de nivel superior y tales dispositivos
a menudo tienen memoria y registros limitados. Muchos microprocesadores se utilizan en
sistemas integrados que son dispositivos distintos de las computadoras de propósito general
que tienen un microprocesador en su interior. Ejemplos de tales sistemas integrados son

https://riptutorial.com/es/home 3
televisores, hornos de microondas y la unidad de control del motor de un automóvil moderno.
Muchos de estos dispositivos no tienen teclado o pantalla, por lo que un programador
generalmente escribe el programa en una computadora de propósito general, ejecuta un
ensamblador cruzado (llamado así porque este tipo de ensamblador produce código para un
tipo de procesador diferente al de aquel en el que se ejecuta). ) y / o un compilador cruzado y
un enlazador cruzado para producir código de máquina.

Existen muchos proveedores para tales herramientas, que son tan variados como los
procesadores para los que producen código. Muchos, pero no todos los procesadores también
tienen una solución de código abierto como GNU, sdcc, llvm u otros.

Codigo de maquina

El código de máquina es un término para los datos en un formato de máquina nativo particular,
que son procesados directamente por la máquina, generalmente por el procesador llamado CPU
(Unidad central de procesamiento).

La arquitectura de computadora común (arquitectura de von Neumann ) consiste en un


procesador de propósito general (CPU), memoria de propósito general, que almacena tanto el
programa (ROM / RAM) como los datos procesados y los dispositivos de entrada y salida
(dispositivos de E / S).

La principal ventaja de esta arquitectura es la relativa simplicidad y universalidad de cada uno de


los componentes, en comparación con las máquinas informáticas anteriores (con el programa de
cableado en la construcción de la máquina), o las arquitecturas competidoras (por ejemplo, la
arquitectura de Harvard que separa la memoria del programa de la memoria de datos).
Desventaja es un poco peor el rendimiento general. A largo plazo, la universalidad permitió un
uso flexible, que generalmente superaba el costo de rendimiento.

¿Cómo se relaciona esto con el código de máquina?

El programa y los datos se almacenan en estas computadoras como números, en la memoria. No


hay una forma genuina de diferenciar el código de los datos, por lo que los sistemas operativos y
los operadores de la máquina le dan sugerencias a la CPU, en la que el punto de entrada de la
memoria inicia el programa, después de cargar todos los números en la memoria. Luego, la CPU
lee la instrucción (número) almacenada en el punto de entrada y la procesa de manera rigurosa,
leyendo secuencialmente los siguientes números como instrucciones adicionales, a menos que el
propio programa le indique a la CPU que continúe con la ejecución en otro lugar.

Por ejemplo, dos números de 8 bits (8 bits agrupados son iguales a 1 byte, es un número entero
sin signo dentro del rango de 0-255): 60 201 , cuando se ejecuta como código en la CPU Zilog Z80
se procesará como dos instrucciones: INC a (incrementando el valor en el registro a por uno) y RET
(regresando de la sub-rutina, apuntando a la CPU para ejecutar instrucciones desde diferentes
partes de la memoria).

Para definir este programa, un humano puede ingresar esos números mediante algún editor de
memoria / archivo, por ejemplo en hex-editor como dos bytes: 3C C9 (números decimales 60 y 201
escritos en codificación de base 16). Eso sería la programación en código de máquina .

https://riptutorial.com/es/home 4
Para facilitar la tarea de la programación de la CPU para los humanos, se crearon programas de
ensamblador , capaces de leer un archivo de texto que contiene algo como:

subroutineIncrementA:
INC a
RET

dataValueDefinedInAssemblerSource:
DB 60 ; define byte with value 60 right after the ret

La secuencia de números hexadecimales de byte de salida 3C C9 3C envolvió con números


adicionales opcionales específicos para la plataforma de destino: marcando qué parte de dicho
binario es un código ejecutable, donde está el punto de entrada para el programa (la primera
instrucción de la misma), qué partes están codificadas Datos (no ejecutables), etc.

Observe cómo el programador especificó el último byte con el valor 60 como "datos", pero desde
la perspectiva de la CPU no difiere en modo alguno de INC a byte. Depende del programa en
ejecución navegar correctamente en la CPU sobre los bytes preparados como instrucciones y
procesar los bytes de datos solo como datos para las instrucciones.

Dicha salida generalmente se almacena en un archivo en el dispositivo de almacenamiento, que


se carga más tarde por el sistema operativo ( Sistema operativo: un código de máquina que ya se
está ejecutando en la computadora, lo que ayuda a manipular la computadora ) en la memoria
antes de ejecutarla y, finalmente, apunta la CPU en la memoria. Punto de entrada del programa.

La CPU puede procesar y ejecutar solo el código de la máquina, pero cualquier contenido de la
memoria, incluso uno aleatorio, puede procesarse como tal, aunque el resultado puede ser
aleatorio, desde el " fallo " detectado y manejado por el SO hasta el borrado accidental de los
datos de I / O dispositivos, o daños en equipos sensibles conectados a la computadora (no es un
caso común para las computadoras en el hogar :)).

El proceso similar es seguido por muchos otros lenguajes de programación de alto nivel,
compilando la fuente (forma de programa de texto legible por humanos) en números, ya sea
representando el código de máquina (instrucciones nativas de la CPU), o en el caso de lenguajes
híbridos interpretados en algunos código de máquina virtual específico del idioma, que se
descodifica en código de máquina nativo durante la ejecución por intérprete o máquina virtual.

Algunos compiladores usan el Ensamblador como etapa intermedia de compilación, primero


traducen la fuente a la forma Ensamblador, luego ejecutan la herramienta del ensamblador para
obtener el código final de la máquina (ejemplo GCC: ejecute gcc -S helloworld.c para obtener
una versión del ensamblador del programa C helloworld.c ).

Hola mundo para Linux x86_64 (Intel 64 bit)

section .data
msg db "Hello world!",10 ; 10 is the ASCII code for a new line (LF)

section .text
global _start

https://riptutorial.com/es/home 5
_start:
mov rax, 1
mov rdi, 1
mov rsi, msg
mov rdx, 13
syscall

mov rax, 60
mov rdi, 0
syscall

Si desea ejecutar este programa, primero necesita el Ensamblador nasm , nasm , porque este
código usa su sintaxis. Luego use los siguientes comandos (asumiendo que el código está en el
archivo helloworld.asm ). Son necesarios para ensamblar, enlazar y ejecutar, respectivamente.

• nasm -felf64 helloworld.asm


• ld helloworld.o -o helloworld
• ./helloworld

El código hace uso de sys_write syscall de Linux. Aquí puede ver una lista de todas las llamadas
al sistema para la arquitectura x86_64. Cuando también tiene en cuenta las páginas del manual
de escritura y salida , puede traducir el programa anterior en uno que hace lo mismo y es mucho
más legible:

#include <unistd.h>

#define STDOUT 1

int main()
{
write(STDOUT, "Hello world!\n", 13);
_exit(0);
}

Aquí solo se necesitan dos comandos para compilar y vincular (el primero) y ejecutar:

• gcc helloworld_c.c -o helloworld_c .


• ./helloworld_c

Hola mundo para OS X (x86_64, gas de sintaxis de Intel)

.intel_syntax noprefix

.data

.align 16
hello_msg:
.asciz "Hello, World!"

.text

.global _main
_main:
push rbp

https://riptutorial.com/es/home 6
mov rbp, rsp

lea rdi, [rip+hello_msg]


call _puts

xor rax, rax


leave
ret

Montar:

clang main.s -o hello


./hello

Notas:

• Se desaconseja el uso de llamadas al sistema, ya que la API de llamadas al sistema en OS


X no se considera estable. En su lugar, utilice la biblioteca C. ( Referencia a una pregunta
de desbordamiento de pila )
• Intel recomienda que las estructuras más grandes que una palabra comiencen en un límite
de 16 bytes. ( Referencia a la documentación de Intel )
• Los datos del pedido se pasan a las funciones a través de los registros: rdi, rsi, rdx, rcx, r8 y
r9. ( Referencia al sistema V ABI )

Ejecutando ensamblaje x86 en Visual Studio 2015

Paso 1 : Crear un proyecto vacío a través de Archivo -> Nuevo proyecto .

https://riptutorial.com/es/home 7
Paso 2 : haga clic con el botón derecho en la solución del proyecto y seleccione Crear
dependencias -> Crear personalizaciones .

Paso 3 : Marque la casilla de verificación ".masm" .

https://riptutorial.com/es/home 8
Paso 4 : Presiona el botón "ok" .

Paso 5 : Crea tu archivo de ensamblaje y escribe esto:

https://riptutorial.com/es/home 9
.386
.model small
.code

public main
main proc

; Ends program normally

ret
main endp
end main

Paso 6 : ¡Compila!

https://riptutorial.com/es/home 10
Lea Empezando con el lenguaje ensamblador en línea:
https://riptutorial.com/es/assembly/topic/1358/empezando-con-el-lenguaje-ensamblador

https://riptutorial.com/es/home 11
Capítulo 2: Control de flujo
Introducción
Cada pieza de software no trivial necesita estructuras de control de flujo para desviar el flujo del
programa según las condiciones. El ensamblaje, siendo el lenguaje de programación de nivel más
bajo, proporciona solo primitivas para estructuras de control. Normalmente, las operaciones de la
máquina afectan a los indicadores en la CPU, y las ramas / saltos condicionales implementan el
control de flujo. En el ensamblaje, todas las estructuras de control de nivel superior deben
construirse a partir de dichas primitivas.

Examples
Trivial IF-THEN-ELSE en m68k Assembly

; IF d0 == 10 GO TO ten, ELSE GO TO other


CMP #10,d0 ; compare register contents to immediate value 10
; instruction affects the zero flag
BEQ ten ; branch if zero flag set
other:
; do whatever needs to be done for d0 != 10
BRA afterother ; unconditionally jump across IF case
ten:
; do whatever needs to be done for d0 == 10
afterother:
; continue normal common program flow

Las instrucciones que afectan a qué banderas y qué ramas condicionales (que también podrían
estar basadas en combinaciones específicas de banderas ) están disponibles, dependen en gran
medida de la CPU elegida y deben consultarse en los manuales.

PARA ... SIGUIENTE en Z80 Asamblea

El Z80 tiene una instrucción específica para implementar conteos en bucle: DJNZ significa "registro
B decremento y salta si no es cero". Entonces, B es el registro de elección para implementar
bucles en este procesador. FOR ... NEXT debe implementarse "hacia atrás", porque el registro
cuenta atrás hasta cero. Otras CPU (como la 8086, esta CPU usa el registro CX como contador
de bucle) podrían tener registros e instrucciones de contador de bucle específicos similares,
algunas otras CPU permiten comandos de bucle con registros arbitrarios (m68k tiene una
instrucción DBRA que funciona con cualquier registro de datos).

; Trivial multiplication (by repeated adding, ignores zero in factors, so


; not recommended for general use)
;
; inputs: A = Factor 1
; B = Factor 2
;
; output: A = Factor 1 * Factor 2

https://riptutorial.com/es/home 12
;
; Pseudo code
; C = A : A = 0 : FOR B = Factor 2 DOWNTO 0 : A = A + C : NEXT B

mul:
LD C,A ; Save Factor 1 in C register
XOR A ; Clear accumulator
mLoop:
ADD A,C ; Add Factor 1 to accumulator
DJNZ mLoop ; Do this Factor 2 times
RET ; return to caller

If-statement en ensamblaje de sintaxis Intel

section .data
msg_eq db 'Equal', 10
len_eq equ $ - msg_eq

msg_le db 'Less than', 10


len_le equ $ - msg_le

msg_gr db 'Greater than', 10


len_gr equ $ - msg_gr ; Length of msg_gr
section .text
global _main ; Make the _main label global for linker
_main:
cmp 4, 5 ; Compare 4 and 5
je _equal ; je = jump if equal
jl _less ; jl = jump if less
jg _greater ; jg = jump if greater
exit:
ret ; Return
_equal:
; Whatever code here
mov rax, 0x2000004 ; sys_write, 4 for linux
mov rdi, 1 ; STDOUT
mov rsi, msg_eq
mov rdi, len_eq

syscall

jmp exit ; Exit


_less:
; Whatever code here
mov rax, 0x2000004
mov rdi, 1
mov rsi, msg_le
mov rdi, len_le

syscall

jmp exit
_greater:
; Whatever code here

mov rax, 0x2000004


mov rdi, 1
mov rsi, msg_gr
mov rdi, len_gr

https://riptutorial.com/es/home 13
syscall
jmp exit

La condición de bucle while es verdadera en el ensamblaje de sintaxis Intel

section .data
msg db 'Hello, world!', 0xA
len equ $ - msg
section .text
global _main
_main:
mov rax, 0 ; This will be the current number
mov rcx, 10 ; This will be the last number

_loop:
cmp rax, rcx
jl .loopbody ; Jump to .loopbody if rax < rcx
jge _exit ; Jump to _exit if rax ≥ rcx
.loopbody:
push rax ; Store the rax value for later use

mov rax, 0x2000004 ; 4 for Linux


mov rdi, 1 ; STDOUT
mov rsi, msg
mov rdx, len

syscall

pop rax ; Take it back to rax

inc rax ; Add 1 to rax. This is required since the loop must have an ending.

jmp _loop ; Back to loop


_exit:
ret ; Return

Esto ejecutará .loopbody mientras rax < rcx .

Lea Control de flujo en línea: https://riptutorial.com/es/assembly/topic/8172/control-de-flujo

https://riptutorial.com/es/home 14
Capítulo 3: Ejemplos de Linux elf64 que no
usan glibc
Examples
Interfaz de usuario

Me atrevería a decir que el 80% del procesamiento que se lleva a cabo en los sistemas
informáticos modernos no requiere la interacción del usuario, como el código del kernel para
Linux, OSX y Windows. Para aquellos que lo hacen, hay dos aspectos fundamentales que son la
interactividad a través del teclado ( dispositivos señaladores ) y la consola. Este y otros ejemplos
de mi serie están orientados alrededor de la consola basada en texto (emulación VT100) y el
teclado.

En sí mismo, este ejemplo es muy simple, pero es un elemento esencial para la creación de
algoritmos más complejos.

Subrtx.asm
STDIN equ 0
STDOUT equ 1

SYS_READ equ 0
SYS_WRITE equ 1

global gets, strlen, print, atoq

section .text

Como esto está destinado exclusivamente para el teclado, la probabilidad de errores es casi nula.
Me imagino que la mayoría de las veces, el programa podrá contemplar el tamaño del búfer para
evitar el desbordamiento del búfer, pero eso no está garantizado debido a la indirección.

; =============================================================================
; Accept canonical input from operator for a maximum of EDX bytes and replace
; terminating CR with NULL.

; ENTER: RSI = Pointer to input buffer


; EDX = Maximum number of characters

; LEAVE: EAX = Number of characters entered


; R11 = Modified by syscall, all others preserved.

; FLAGS: ZF = Null entry, NZ otherwise.


; _____________________________________________________________________________

gets: push rcx


push rdi

https://riptutorial.com/es/home 15
xor eax, eax ; RAX = SYS_READ
mov edi, eax ; RDI = STDIN
syscall

; TODO: Should probably do some error trapping here, especially for


; buffer overrun, but I'll see if it becomes an issue over time.

dec eax ; Bump back to CR


mov byte [rsi+rax], 0 ; Replace it with NULL

pop rdi
pop rcx
ret

Para empezar, se pretendía evitar la necesidad de codificar o calcular manualmente la longitud de


las cadenas para escritura (2). Entonces decidí incorporar un delimitador, ahora se puede usar
para buscar cualquier carácter (0 - FF). Esto abre la posibilidad de texto de ajuste de palabras,
por ejemplo, por lo que la etiqueta strlen es un nombre poco apropiado, ya que uno pensaría
que el resultado será el número de caracteres visibles.

; =============================================================================
; Determine length, including terminating character EOS. Result may include
; VT100 escape sequences.

; ENTER: RDI = Pointer to ASCII string.


; RCX Bits 31 - 08 = Max chars to scan (1 - 1.67e7)
; 07 - 00 = Terminating character (0 - FF)

; LEAVE: RAX = Pointer to next string (optional).

; FLAGS: ZF = Terminating character found, NZ otherwise (overrun).


; _____________________________________________________________________________

strlen: push rcx ; Preserve registers used by proc so


push rdi ; it's non-destructive except for RAX.

mov al, cl ; Byte to scan for in AL.


shr ecx, 8 ; Shift max count into bits 23 - 00

; NOTE: Probably should check direction flag here, but I always set and
; reset DF in the process that is using it.

repnz scasb ; Scan for AL or until ECX = 0


mov rax, rdi ; Return pointer to EOS + 1

pop rdi ; Original pointer for proglogue


jz $ + 5 ; ZF indicates EOS was found
mov rax, rdi ; RAX = RDI, NULL string
pop rcx

ret

La intención de este procedimiento es simplificar el diseño de bucle en el procedimiento de


llamada.

; =============================================================================

https://riptutorial.com/es/home 16
; Display an ASCIIZ string on console that may have embedded VT100 sequences.

; ENTER: RDI = Points to string

; LEAVE: RAX = Number of characters displayed, including EOS


; = Error code if SF
; RDI = Points to byte after EOS.
; R11 = Modified by syscall all others preserved

; FLAGS: ZF = Terminating NULL was not found. NZ otherwise


; SF = RAX is negated syscall error code.
;______________________________________________________________________________

print: push rsi


push rdx
push rcx

mov ecx, -1 << 8 ; Scan for NULL


call strlen
push rax ; Preserve point to next string
sub rax, rdi ; EAX = End pntr - Start pntr
jz .done

; size_t = write (int STDOUT, char *, size_t length)

mov edx, eax ; RDX = length


mov rsi, rdi ; RSI = Pointer
mov eax, SYS_WRITE
mov edi, eax ; RDI = STDOUT
syscall
or rax, rax ; Sets SF if syscall error

; NOTE: This procedure is intended for console, but in the event STDOUT is
; redirected by some means, EAX may return error code from syscall.

.done: pop rdi ; Retrieve pointer to next string.


pop rcx
pop rdx
pop rsi

ret

Finalmente un ejemplo de cómo se pueden usar estas funciones.

Generic.asm
global _start

extern print, gets, atoq

SYS_EXIT equ 60
ESC equ 27

BSize equ 96

section .rodata
Prompt: db ESC, '[2J' ; VT100 clear screen
db ESC, '[4;11H' ; " Position cursor to line 4 column 11

https://riptutorial.com/es/home 17
db 'ASCII -> INT64 (binary, octal, hexidecimal, decimal), '
db 'Packed & Unpacked BCD and floating point conversions'
db 10, 10, 0, 9, 9, 9, '=> ', 0
db 10, 9, 'Bye'
db ESC, '[0m' ; VT100 Reset console
db 10, 10, 0

section .text
_start: pop rdi
mov rsi, rsp
and rsp, byte 0xf0 ; Align stack on 16 byte boundary.

call main
mov rdi, rax ; Copy return code into ARG0

mov eax, SYS_EXIT


syscall

; int main ( int argc, char *args[] )

main: enter BSize, 0 ; Input buffer on stack


mov edi, Prompt
call print
lea rsi, [rbp-BSize] ; Establish pointer to input buffer
mov edx, BSize ; Max size for read(2)

.Next: push rdi ; Save pointer to "=> "


call print
call gets
jz .done

call atoq ; Convert string pointed to by RSI

pop rdi ; Restore pointer to prompt


jmp .Next

.done: call print ; RDI already points to "Bye"


xor eax, eax
leave
ret

Makefile
OBJECTS = Subrtx.o Generic.o

Generic : $(OBJECTS)
ld -oGeneric $(OBJECTS)
readelf -WS Generic

Generic.o : Generic.asm
nasm -g -felf64 Generic.asm

Subrtx.o : Subrtx.asm
nasm -g -felf64 Subrtx.asm

clean:
rm -f $(OBJECTS) Generic

https://riptutorial.com/es/home 18
Lea Ejemplos de Linux elf64 que no usan glibc en línea:
https://riptutorial.com/es/assembly/topic/7059/ejemplos-de-linux-elf64-que-no-usan-glibc

https://riptutorial.com/es/home 19
Capítulo 4: Interrupciones
Observaciones
¿Por qué necesitamos interrupciones?

Imaginemos: nuestra computadora está conectada a un teclado. Queremos entrar en algo.


Cuando presionamos la tecla, no pasa nada porque la computadora está lidiando con cosas
diferentes y no se da cuenta de que queremos algo de él. Necesitamos interrupciones!

Las interrupciones son activadas por el software ( INT 80h) o el hardware (pulsación de tecla), se
comportan como una llamada (saltan a una ubicación específica, ejecutan el código y vuelven a
saltar).

Examples
Trabajando con interrupciones en la Z80:

El Z80 no tiene mesa de interrupción como procesadores modernos. Las Interrupciones todas
ejecutan el mismo código. En el modo de interrupción 1, ejecutan el código en una ubicación
inmutable específica. En el modo de interrupción 2, ejecutan el código del registro de puntero al
que apunto. El Z80 tiene un temporizador que dispara la Interrupción todos ~ 0.007s.

EI ;enables Interrupts
DI ;disables Interrupts
IM 1 ;sets the Normal Interrupt Mode

IM 2 ;sets the Advanced Interrupt Mode


LD I,$99;sets the Interrupt Pointer to $99 (just possible in IM 2)

Lea Interrupciones en línea: https://riptutorial.com/es/assembly/topic/6555/interrupciones

https://riptutorial.com/es/home 20
Capítulo 5: La pila
Observaciones
La pila de computadoras es como una pila de libros. PUSH agrega uno a la parte superior y POP
quita lo máximo. Al igual que en la vida real, la pila no puede ser infinita, por lo que tiene el
tamaño máximo. La pila se puede usar para ordenar algoritmos, para manejar una mayor
cantidad de datos o para valores seguros de registros mientras se realiza otra operación.

Examples
Zilog Z80 Stack

El registro sp se utiliza como puntero de pila , apuntando al último valor almacenado en pila
("parte superior" de pila). Entonces EX (sp),hl intercambiará el valor de hl con el valor en la parte
superior de la pila.

Contrariamente a la palabra "top", la pila crece en la memoria al disminuir los valores de sp , y


libera ("pops") al aumentar sp .

Para sp = $4844 con los valores 1 , 2 , 3 almacenados en la pila (los 3 se colocan en la pila como
último valor, por lo que están en la parte superior de la misma), la memoria se verá así:

| address | value bytes | comment (btw, all numbers are in hexadecimal)


| ---------- | ----------- | ---------------------------------
| 4840 | ?? ?? | free stack spaces to be used by next push/call
| 4842 | ?? ?? | or by interrupt call! (don't expect values to stay here)
| sp -> 4844 | 03 00 | 16 bit value "3" on top of stack
| 4846 | 02 00 | 16 bit value "2"
| 4848 | 01 00 | 16 bit value "1"
| 484A | ?? ?? | Other values in stack (up to it's origin)
| 484C | ?? ?? | like for example return address for RET instruction

Ejemplos, cómo funcionan las instrucciones con la pila:

LD hl,$0506
EX (sp),hl ; $0003 into hl, "06 05" bytes at $4844
POP bc ; like: LD c,(sp); INC sp; LD b,(sp); INC sp
; so bc is now $0506, and sp is $4846
XOR a ; a = 0, sets zero and parity flags
PUSH af ; like: DEC sp; LD (sp),a; DEC sp; LD (sp),f
; so at $4844 is $0044 (44 = z+p flags), sp is $4844
CALL $8000 ; sp is $4842, with address of next ins at top of stack
; pc = $8000 (jumping to sub-routine)
; after RET will return here, the sp will be $4844 again
LD (L1+1),sp ; stores current sp into LD sp,nn instruction (self modification)
DEC sp ; sp is $4843
L1 LD sp,$1234 ; restores sp to $4844 ($1234 was modified)
POP de ; de = $0044, sp = $4846
POP ix ; ix = $0002, sp = $4848

https://riptutorial.com/es/home 21
...

...
ORG $8000
RET ; LD pc,(sp); INC sp; INC sp
; jumps to address at top of stack, "returning" to caller

Resumen : PUSH almacenará el valor en la parte superior de la pila, POP obtendrá el valor de la
parte superior de la pila, es una cola LIFO (último en entrar, primero en salir). CALL es igual que JP
, pero también empuja la dirección de la siguiente instrucción después de CALL en la parte superior
de la pila. RET es similar a JP también, sacando la dirección de la pila y saltando a ella.

Advertencia : cuando las interrupciones están habilitadas, el sp debe ser válido durante la señal
de interrupción, con suficiente espacio libre reservado para la rutina del manejador de
interrupciones, ya que la señal de interrupción almacenará la dirección de retorno ( pc real) antes
de llamar a la rutina del manejador, que puede almacenar más datos en apilar también. Cualquier
valor por delante de sp puede ser modificado así "inesperadamente", si ocurre una interrupción.

Truco avanzado : en Z80 con PUSH tomó 11 ciclos de reloj (11t) y POP tomó 10t, el POP / PUSH
desenrollado a través de todos los registros, incluyendo EXX para las variantes de sombra, fue la
forma más rápida de copiar bloques de memoria, incluso más rápido que el LDI desenrollado.
Pero tuvo que programar la copia entre las señales de interrupción para evitar la corrupción de la
memoria. También el PUSH enrollado fue la forma más rápida de llenar la memoria con un valor
particular en el ZX Spectrum (nuevamente, con el riesgo de corrupción por Interrupción, si no se
cronometra correctamente, o se realiza bajo DI ).

Lea La pila en línea: https://riptutorial.com/es/assembly/topic/4957/la-pila

https://riptutorial.com/es/home 22
Capítulo 6: Registros
Observaciones
¿Qué son los registros?

El procesador puede funcionar con valores numéricos (números), pero estos deben almacenarse
en algún lugar primero. Los datos se almacenan principalmente en la memoria, o dentro del
código de operación de la instrucción (que también se almacena generalmente en la memoria), o
en la memoria especial en chip colocada directamente en el procesador, que se denomina
registro .

Para trabajar con valor en el registro, no es necesario que lo dirija por dirección, pero se utilizan
"nombres" mnemónicos especiales, como por ejemplo ax en x86, A en Z80 o r0 en ARM.

Algunos procesadores están construidos de una manera, donde casi todos los registros son
iguales y se pueden usar para todos los propósitos (a menudo el grupo de procesadores RISC),
otros tienen especialización distinta, cuando solo algunos registros se pueden usar para
aritmética ( "acumulador" en CPU tempranas) ) y otros registros para direccionamiento de
memoria solamente, etc.

Esta construcción que utiliza la memoria directamente en el chip del procesador tiene una gran
implicación en el rendimiento, ya que agregar dos números de los registros que lo guardan para
registrarse se realiza generalmente en el menor tiempo posible por ese procesador (Ejemplo en el
procesador ARM: ADD r2,r0,r1 establece r2 en (r0 + r1) valor, en ciclo de procesador único).

Por el contrario, cuando uno de los operandos hace referencia a una ubicación de memoria, el
procesador puede detenerse durante algún tiempo, esperando que llegue el valor del chip de
memoria (en x86, esto puede ir desde cero, esperar los valores en caché L0 hasta cientos de Los
ciclos de la CPU cuando el valor no está en ningún caché y se debe leer directamente desde el
chip de memoria DRAM).

Entonces, cuando la programadora está creando algún código de procesamiento de datos,


generalmente quiere tener todos los datos durante el procesamiento en registros para obtener el
mejor rendimiento. Si eso no es posible, y se requieren lecturas / escrituras de memoria, entonces
deben minimizarse y formar un patrón que coopere con las cachés / arquitectura de memoria de
la plataforma en particular.

El tamaño nativo del registro en bits se usa a menudo para agrupar procesadores, como Z80 es
"procesador de 8 bits" y 80386 es "procesador de 32 bits" , aunque esa agrupación rara vez es
clara. Por ejemplo, Z80 opera también con pares de registros, formando un valor nativo de 16
bits, y la CPU de 32 bits 80686 tiene instrucciones MMX para trabajar de forma nativa con
registros de 64 bits.

Examples

https://riptutorial.com/es/home 23
Zilog Z80 se registra

Registros: 8 bits: A , B , C , D , E , H , L , F , I , R , 16 bits: SP , PC , IX , IY y sombras de algunos


registros 8b: A' , B' , C' , D' , E' , H' , L' y F' .

La mayoría de los registros de 8 bits se pueden usar también en pares como registros de 16 bits:
AF , BC , DE y HL .

SP es el puntero de pila , que marca la parte inferior de la memoria de pila (utilizada por las
instrucciones PUSH / POP / CALL / RET ).
PC es un contador de programa , que apunta a la instrucción actualmente ejecutada.
I es el registro de interrupción , que proporciona un byte alto de la dirección de la tabla de
vectores para el modo de interrupción de IM 2 .
R es el registro de actualización , se incrementa cada vez que la CPU busca un código de
operación (o prefijo de código de operación).
Algunas instrucciones no oficiales existen en algunos procesadores Z80 para manipular partes de
IYH:IYL de IX como IXH:IXL e IY como IYH:IYL .

No se puede acceder directamente a las variantes de sombra mediante ninguna instrucción, la


instrucción EX AF,AF' cambiará entre AF y AF' , y la instrucción EXX intercambiará BC,DE,HL con
BC',DE',HL' .

Cargando valor en un registro:

; from other register


LD I,A ; copies value in A into I (8 bit)
LD BC,HL ; copies value in HL into BC (16 bit)
; directly with value encoded in instruction machine code
LD B,d8 ; 8b value d8 into B
LD DE,d16 ; 16b value d16 into DE
; from a memory (ROM/RAM)
LD A,(HL) ; value from memory addressed by HL into A
LD A,(a16) ; value from memory with address a16 into A
LD HL,(a16) ; 16b value from memory with address a16 into HL
POP IX ; 16b value popped from stack into IX
LD A,(IY+a8) ; IX and IY allows addressing with 8b offset
; from I/O port (for writing value at I/O port use "OUT")
IN A,(C) ; reads I/O port C, value goes to A

Las combinaciones correctas de posibles operandos de origen y destino están limitadas (por
ejemplo, LD H,(a16) no existe).

Almacenamiento de valor en una memoria:

LD (HL),D ; value D stored into memory addressed by HL


LD (a16),A ; value A into memory with address a16
LD (a16),HL ; value HL into 16b of memory with address a16
LD (IX+a8),d8 ; value d8 into memory at address IX+a8
LD (IY+a8),B ; value B into memory at address IY+a8
; specials ;)

https://riptutorial.com/es/home 24
PUSH DE ; 16b value DE pushed to stack
CALL a16 ; while primarily used for execution branching
; it also stores next instruction address into stack

x86 registros

En el mundo de 32 bits, los registros de propósito general se dividen en tres clases generales: los
registros de propósito general de 16 bits, los registros de propósito general extendidos de 32 bits
y las mitades de registro de 8 bits. Estas tres clases no representan tres conjuntos de registros
completamente distintos. Los registros de 16 bits y 8 bits son en realidad nombres de regiones
dentro de los registros de 32 bits. El crecimiento de los registros en la familia de CPU x86 se ha
producido al extender los registros existentes en las CPU más antiguas

Hay ocho registros de propósito general de 16 bits: AX, BX, CX, DX, BP, SI, DI y SP; y puede
colocar cualquier valor en ellos que pueda expresarse en 16 bits o menos.

Cuando Intel expandió la arquitectura x86 a 32 bits en 1986, duplicó el tamaño de los ocho
registros y les dio nuevos nombres prefijando una E delante de cada nombre de registro, dando
como resultado EAX, EBX, ECX, EDX, EBP, ESI, EDI, y ESP.

Con x86_64 vino otra duplicación del tamaño del registro, así como la adición de algunos nuevos
registros. Estos registros tienen 64 bits de ancho y se denominan (barra diagonal utilizada para
mostrar el nombre del registro alternativo): RAX / r0, RBX / r3, RCX / r1, RDX / r2, RBP / r5, RSI /
r6, RDI / r7, RSP / r4 , R8, R9, R10, R11, R12, R13, R14, R15.

Si bien los registros de propósito general se pueden usar técnicamente para cualquier cosa, cada
registro también tiene un propósito alternativo / principal:

• AX (acumulador) se utiliza en operaciones aritméticas.


• CX (contador) se usa en las instrucciones de cambio y rotación, y se usa para bucles.
• DX (datos) se utiliza en operaciones aritméticas y de E / S.
• BX (base) se usa como puntero a los datos (específicamente como un desplazamiento al
registro de segmento DS cuando está en modo segmentado).
• SP (pila) apunta a la parte superior de la pila.
• BP (base de pila) apunta a la base de la pila.
• SI (fuente) apunta a una fuente en memoria para operaciones de transmisión (por ejemplo,
lodsb ).
• DI (destino) apunta a un destino en la memoria para operaciones de transmisión (por
ejemplo, stosb ).

Los registros de segmentos, utilizados en el modo segmentado, apuntan a diferentes segmentos


en la memoria. Cada registro de segmento de 16 bits da una vista a 64k (16 bits) de datos.
Después de configurar un registro de segmento para que apunte a un bloque de memoria, los
registros (como BX , SI y DI ) se pueden usar como compensaciones del registro de segmento
para que se pueda acceder a ubicaciones específicas en el espacio de 64k.

Los seis registros de segmento y sus usos son:

https://riptutorial.com/es/home 25
Registro Nombre completo Descripción

SS Segmento de pila Apunta a la pila

CS Segmento de código Utilizado por la CPU para recuperar el código.

DS Segmento de datos Registro predeterminado para operaciones MOV

ES Segmento Extra Segmento de datos extra

FS Segmento Extra Segmento de datos extra

GS Segmento Extra Segmento de datos extra

x64 registros

La arquitectura x64 es la evolución de la arquitectura x86 anterior, mantuvo la compatibilidad con


su predecesora (los registros x86 todavía están disponibles) pero también introdujo nuevas
características:

• Los registros tienen ahora una capacidad de 64 bits;


• Hay 8 registros más de propósito general;
• Los registros de segmento están forzados a 0 en modo de 64 bits;
• Los 32, 16 y 8 bits más bajos de cada registro ahora están disponibles.

Propósito general

Registro Nombre Subregistros (bits)

RAX Acumulador EAX (32), AX (16), AH (8), AL (8)

RBX Base EBX (32), BX (16), BH (8), BL (8)

RCX Mostrador ECX (32), CX (16), CH (8), CL (8)

RDX Datos EDX (32), DX (16), DH (8), DL (8)

RSI Fuente ESI (32), SI (16), SL (8)

RDI Destino EDI (32), DI (16), DL (8)

RBP Puntero base EBP (32), BP (16), BPL (8)

RSP Puntero de pila ESP (32), SP (16), SPL (8)

R8-R15 Nuevos registros R8D-R15D (32), R8W-R15W (16), R8B-R15B (8)

Nota

Los sufijos utilizados para direccionar los bits más bajos de los nuevos registros representan:

https://riptutorial.com/es/home 26
• Byte B, 8 bits;
• W palabra, 16 bits;
• D doble palabra, 32 bits.

Lea Registros en línea: https://riptutorial.com/es/assembly/topic/4802/registros

https://riptutorial.com/es/home 27
Creditos
S.
Capítulos Contributors
No

Empezando con el Community, Edward, FedeWar, godisgood4, old_timer, Ped7g,


1 lenguaje Pichi Wuana, sigalor, stackptr, TheFrenchPlays Hd Micraftn,
ensamblador Zopesconk

2 Control de flujo SpilledMango, tofro

Ejemplos de Linux
3 elf64 que no usan Shift_Left
glibc

4 Interrupciones Jonas W.

5 La pila Jonas W., Ped7g

FedeWar, godisgood4, Jonas W., Ped7g, Pichi Wuana,


6 Registros
SirPython

https://riptutorial.com/es/home 28

También podría gustarte