/hispatec/ - Tecnologia hispana

Refugio del /t/ de hispa

Página principal Catálogo Archivar Bajar Actualizar
Opciones
Título
Mensaje

Máximo de caracteres: 8001

archivos

Tamaño máximo de archivo: 32.00 MB

Tamaño máximo de archivo en total: 50.00 MB

Numero máximo de archivos: 5

Supported file types: GIF, JPG, PNG, WebM, OGG, and more

Correo
Clave

(usado para eliminar archivos y mensajes)

Misc

Recuerda leer las reglas

The backup domains are located at 8chan.se and 8chan.cc. TOR access can be found here, or you can access the TOR portal from the clearnet at Redchannit 3.0.

Ghost Screen
No olvidó el anuncio global esta semana
Sábado en la noche


8chan.moe is a hobby project with no affiliation whatsoever to the administration of any other "8chan" site, past or present.

Otros tablones: /hisparefugio /ac /arte /av /hisrol /vt /arepa /esp /col /cc /mex /pe /hispol Contacto: drzmx@cock.li SI TU HILO ERA UNA CONSULTA SIMPLE, Y NO LO VES, SE HA FUSIONADO CON EL HILO GENERAL DE PREGUNTAS

(595.61 KB 811x599 2.png)

Ensamblador Anónimo 04/10/2023 (Mie) 12:12:48 656
Negros, hace tiempo que pienso en crear un hilo sobre desarrollo de sistemas operativos, así aprendemos juntos, basado en los libros "Sistemas operativos. Aspectos internos y principios de diseño" de Stallings y "Sistemas Operativos Modernos" de Tanenbaum. Pero creo que primero sería mejor que aprendamos Ensamblador. Por lo que estaba pensando que podríamos usar "Assembly Language for x86 Processors" de Kip Irvine o "Assembly Language Step-By-Step" de Jeff Duntemann. Aun que si es problema el ingles, podríamos aprender de "Lenguaje ensamblador para PC" de Paul A. Carter.
En el método indirecto de direccionamiento, la instrucción contiene un registro el cual contiene un puntero a la ubicación de memoria que contiene el dato a operar. Por ejemplo, si se quiere usar un registro que contiene el valor 4, cualquier valor que contenga la memoria en la posición cuatro va a ser usado en la operación. En cambio, en el método directo, se especifica el valor, no la dirección de memoria que contiene el valor. (((indirecto))) ->> quiero el valor que esta en la posición 100 de la memoria. (((directo))) --->> quiero el valor 100 valor, no dirección. Por ultimo, esta el direccionamiento por puntero base. En este caso se tienen dos valores: un valor de puntero base, y un valor offset un desplazamiento, el cual se suma al anterior para obtener la dirección real de memoria. Siguiendo el ejemplo de la estructura de cliente, suponiendo que queríamos obtener la dirección de la variable que contiene la edad del cliente, y teniendo en cuanta que la dirección de inicio de la estructura 1016 esta en un registro, se puede especificar este registro como puntero base. Luego se puede sumar a este registro puntero base el offset, que en este caso es 102. Dirección de memoria de la edad del cliente = Dirección base (1016) + Offset (102) = 1118 Es muy parecido al método de direccionamiento indexado, pero el offset es constante y el puntero se almacena en un registro. Ósea, a muy grandes rasgos: Inmediato: La dirección se especifica directamente en la (((instrucción))). De registro: La dirección se especifica en un (((registro))). Directo: La dirección se especifica como una (((dirección de memoria))). Indexado: La dirección se especifica como una (((dirección de memoria base más un desplazamiento))). Indirecto: La dirección se especifica como la (((dirección de memoria de un puntero))). Por puntero base: La dirección se especifica como la (((dirección de memoria base más un desplazamiento))). Estos son algunas de las formas de direccionamiento.
///Con esto entramos en los primero ejercicios o problemas planteados por el libro.\\\ Revisión. Conoce los conceptos • Describa el ciclo de búsqueda-ejecución. • ¿Qué es un registro? ¿Por qué sería más difícil calcular sin registros? • ¿Cómo se representan los números mayores que 255? • ¿Qué tamaño tienen los registros de las máquinas que vamos a utilizar? • ¿Cómo sabe un ordenador cómo interpretar un determinado byte o conjunto de bytes de memoria? • ¿Cuáles son los modos de direccionamiento y para qué se utilizan? • ¿Qué hace el puntero de instrucción? Usa los conceptos • ¿Qué datos utilizarías en un registro de empleados? ¿Cómo los distribuirías en memoria? • Si tuviera el puntero al principio del registro de empleados anterior y quisiera acceder a un dato concreto de su interior, ¿Qué modo de direccionamiento utilizaría? • En el modo de direccionamiento por puntero base, si tienes un registro con el valor 3122 y un offset de 20, ¿a qué dirección estarías intentando acceder? • En el modo de direccionamiento indexado, si la dirección base es 6512, el registro índice tiene un 5 y el multiplicador es 4, ¿a qué dirección estaría intentando acceder? • En el modo de direccionamiento indexado, si la dirección base es 123472, el registro índice tiene un 0 y el multiplicador es 4, ¿a qué dirección estarías intentando acceder? • En el modo de direccionamiento indexado, si la dirección base es 9123478, el registro índice tiene un 20 y el multiplicador es 1, ¿a qué dirección estarías intentando acceder? Yendo más lejos • ¿Cuál es el número mínimo de modos de direccionamiento necesarios para el cálculo? • ¿Por qué incluir modos de direccionamiento que no son estrictamente necesarios? • Investiga y describe cómo afecta el pipelining (u otro factor de complicación) al ciclo de búsqueda y ejecución. • Investiga y describe las ventajas y desventajas de las instrucciones de longitud fija y las de longitud variable.
(81.52 KB 624x636 Ejecucion de un programa.png)

Ciclo de búsqueda-ejecución Al comienzo del ciclo, la CPU lee una instrucción, la apuntada por el contador de programa. ///Algo que olvide mencionar es que el contador de programa es un contador incremental, al no ser que se indique una dirección diferente.\\\ Esta instrucción leída se almacena en el registro de instrucción, instrucción que fue traducida a binario. La CPU ejecuta la instrucción, la cual puede tener un efecto entre estos cuatro, o una combinación de estas: • Procesador-Memoria: copia datos de la CPU a la memoria o al revés. • Procesador-E/S: transfiere datos desde o hasta el entorno de la CPU por medio de los dispositivos de E/S. • Procesamiento de Datos: la CPU ha de realizar alguna operación aritmética o lógica con los datos. • Control: alteración de la ejecución. Por ejemplo, cambiar el valor del contador de programa. Además el libro de un buen ejemplo del ciclo de búsqueda-ejecución, en el ejemplo se suma el dato en la dirección 940 con el dato almacenado en la dirección 941 y lo almacena en esta ultima posición. 1. El contador de programa (PC) contiene el valor 300, la dirección de la primera instrucción. Esta instrucción (el valor hexadecimal 1940) se carga en el registro de instrucción (IR). Obsérvese que este proceso implicaría el uso del registro de dirección de memoria (MAR) y el registro de datos de memoria (MBR). Por simplicidad, se han ignorado estos registros intermedios. 2. Los primeros cuatro bits de IR (primer dígito hexadecimal) indican que el acumulador (AC) se va a cargar. Los restantes 12 bits (tres dígitos hexadecimales) especifican la dirección (940) que se va a cargar. 3. El registro PC se incrementa, y se capta la siguiente instrucción (5941) desde la dirección 301. 4. El contenido anterior de AC y el de la posición de memoria 941 se suman, y el resultado se almacena en AC. 5. El registro PC se incrementa, y se capta la siguiente instrucción (294) desde la posición 302. 6. El contenido de AC se almacena en la posición 941. Fuentes: "Organización y arquitectura de computadores" de William Stallings, 7 edición. ISBN 10: 84-8966-082-4 - ISBN 13: 978-84-8966-082-3 También hay un libro que parece muy interesante relacionado a este tema arquitectura de computadoras, "Computer Organization and Design: The Hardware/Software Interface" de David A. Patterson y John L. Hennessy. Si encuentro algo, en este libro, que complemente a lo anterior, lo postearé.
>>761 ¿Qué es un registro? ¿Por qué sería más difícil calcular sin registros? Un registro es una pequeña memoria al servicio del microprocesador. Esta memoria se ubica en lo mas alto de la jerarquía de memorias, por lo que es mas costosa por bit, tiene menos espacio y es mas rápida que las demás memorias en esta jerarquía. Los registros pueden dividirse según si el programador puede acceder y cambiarlos o no. • Registros visibles por el usuario: Permiten al programador minimizar el acceso a la memoria al permitir almacenar datos. • Registros de control y estado: Usados para controlar el funcionamiento del procesador y la ejecución de los programas Aunque esta división varia según el microprocesador. La segunda imagen de un ejemplo de como se puede organizar los registros de tres microprocesadores distintos. ------------------------------------------------------------------------------------------------------------------------------------------- Registros visibles por el usuario Estos tipos de registros pueden referenciarse por medio del lenguaje maquina. Dentro de estos registros hay varias categorías según el uso que se le de: -Uso general -Datos -Direcciones -Códigos de condición Los registros de uso general no tienen una función especifica, como contener el operando de un código de operación, o usarse como direccionamiento. Los registros de datos solo contienen datos y no pueden usarse para ningún tipo de calculo, mientras que los registros de direcciones pueden usarse como pseudo registro de uso general. Por ultimo están los registros de código de condición, estos son parcialmente visibles para el usuario. Estos registros, como el nombre dice, contiene códigos de condición o flags. Estos son bits que fija el microprocesador después de hacer una operación, por ejemplo una operación puede resultar en un numero positivo, negativo, cero o en un desbordamiento sobre pasar la capacidad del registro o memoria. Después este código o flag especifico puede ser usado en un salto condicional. Estos bit pueden ser leídos, pero no alterados directamente por el programador. ------------------------------------------------------------------------------------------------------------------------------------------- Registros de control y estado Estos registros generalmente no son visibles por el usuario, aunque lo pueden ser si se usa una instrucción en modo núcleo. Estos registros son usados por el microprocesador para, por ejemplo, ejecutar una instrucción: Contador de programa: Contiene la dirección de la siguiente instrucción. Registro de instrucción: Contiene la instrucción captada mas reciente. Registro de dirección de memoria: Contiene una dirección de memoria. Registro intermedio de memoria: Contiene la palabra de datos a escribir en la memoria, o la palabra de datos leída mas reciente. Generalmente los microprocesadores contiene un registro especial, llamado PSW o palabra de estado del programa, que contiene los códigos de condición antes mencionados. Algunos de los códigos de condición que puede contener son: -Signo: Contiene el bit según el resultado de la ultima operación. Por ejemplo 1 para un resultado negativo. -Cero: Contiene un uno cuando el resultado de la ultima operación dio como resultado un cero. -Acarreo: Contiene un uno si la ultima operación tuvo acarreo de bit. -Igual: Contiene uno si el resultado de la comparación es igualdad. -Desbordamiento: Indica un desbordamiento aritmético.
[Expand Post] -Interrupciones habilitadas/deshabilitadas: Habilita o inhabilita las interrupciones. -Supervisor: Indica si el procesador esta trabajando en modo núcleo o no. ------------------------------------------------------------------------------------------------------------------------------------------- Entonces se llega a la conclusión de que los registros ayudan no solo a calcular, ya que pueden y son usados como una memoria auxiliar, sino que también son muy importantes para la ejecución y control del programa. Sin los registros sería lo mismo que hacer cálculos sin lápiz y papel, lo que también implica una perdida de la eficiencia del microprocesador. ------------------------------------------------------------------------------------------------------------------------------------------- Fuentes "Organización y arquitectura de computadores" de William Stallings, 7 edición, capitulo 12 - Estructura y funcionamiento del procesador. ISBN 10: 84-8966-082-4 - ISBN 13: 978-84-8966-082-3
Por cierto Negros informáticos, les voy a dejar los dos libros que les mencione sobre organización y estructura de computadoras. "Computer Organization and Design" no lo pude subir por que pesa 198 Mb. "Computer Organization and Design. The HardwareSoftware" -- David A. Patterson, Morgan Kaufmann: https://annas-archive.org/md5/4029a57c779cbed199136b42e553b5ae
>>761¿Cómo se representan los números mayores que 255? Si se quiere representar números mas grandes que 255, el numero mas alto que se puede representar con un byte 2 posibles dígitos elevado a ocho bit, menos uno por que se empieza a contar desde cero, solo se usan combinaciones de bytes. Por ejemplo: 1 byte: 0 -> 255 2 bytes: 0 -> 65535 3 bytes: 0 -> 16777215 4 bytes: 0 -> 4294967295 • ¿Qué tamaño tienen los registros de las máquinas que vamos a utilizar? Los registros de la arquitectura x86 tienen un tamaño de 4 bytes. • ¿Cómo sabe un ordenador cómo interpretar un determinado byte o conjunto de bytes de memoria? La forma en que el procesador diferencia un dato de una instrucción es según si la palabra de memoria es apuntada, ósea esta almacenada la dirección de memoria, en el puntero de instrucción. • ¿Cuáles son los modos de direccionamiento y para qué se utilizan? Los modos de direccionamiento se usan para saber como se va a acceder al dato o instrucción en memoria, se presento seis tipos de direccionamiento >>760: Inmediato De registro Director Indexado Indirecto Por puntero base • ¿Qué hace el puntero de instrucción? Este registro, contador de programa, indica la ubicación en memoria de la siguiente instrucción a ejecutar por el procesador.
Usa los conceptos¿Qué datos utilizarías en un registro de empleados? ¿Cómo los distribuirías en memoria? Creo que al decir distribuir en memoria hace referencia al valor máximo de tamaño que le asignaría a cada dato. Depende la información que se busque almacenar del empleado, pero como base almacenaría: -Nombre -> 35 bytes (35 caracteres) -Apellido -> 50 bytes (50 caracteres) -Celular -> 15 bytes -DNI -> 15 bytes -Edad -> 5 Bytes -Fecha de nacimiento -> 10 bytes -Dirección -> 75 bytes -Telefono 15 bytes -Mail privado -> 50 bytes -Mail de empresa -> 50 bytes • Si tuviera el puntero al principio del registro de empleados anterior y quisiera acceder a un dato concreto de su interior, ¿Qué modo de direccionamiento utilizaría? Los métodos mas convenientes son el indexada, o el puntero base. • En el modo de direccionamiento por puntero base, si tienes un registro con el valor 3122 y un offset de 20, ¿a qué dirección estarías intentando acceder? valor base: 3122 offset: 20 dirección a la que se accede: • En el modo de direccionamiento indexado, si la dirección base es 6512, el registro índice tiene un 5 y el multiplicador es 4, ¿a qué dirección estaría intentando acceder? valor base: 6512 valor índice: 5 valor multiplicador: 4 dirección accedida: • En el modo de direccionamiento indexado, si la dirección base es 123472, el registro índice tiene un 0 y el multiplicador es 4, ¿a qué dirección estarías intentando acceder? valor base: 123472 valor índice: 0 valor multiplicador: 4 dirección accedida: • En el modo de direccionamiento indexado, si la dirección base es 9123478, el registro índice tiene un 20 y el multiplicador es 1, ¿a qué dirección estarías intentando acceder? valor base: 9123478 valor índice: 20 valor multiplicador: 1 dirección accedida:
Capitulo 3. Tu primer programa Con este capitulo vas a aprender el proceso de escribir y compilar programas en lenguaje ensamblador de Linux. También aprenderás la estructura de un programa ensamblador y algunos comandos de este. Mientras se avanza en el capitulo, puede ser útil consultar el apéndice B y F. Estos programas al principio pueden ser densos, pero es necesario que los leas y releas, incluyendo las explicaciones, todas la veces que necesites. También debes probarlos de todas las formas que creas posibles.
Comenzando con el programa. El primer programa es simple, tan solo va a finalizar. Es corto, pero muestra conceptos basicos del lenguaje ensamblador y la programación Linux. Tienes que escribir en un editor de texto, recomendable Vim o GVim y guardarlo con el nombre de archivo exit.s. El programa es el siguiente: #PURPOSE: # Programa simple que finaliza y devuelve un # código de status al kernel, nucleo de # sistema operativo, de Linux. #INPUT(ENTRADA): none # #OUTPUT(SALIDA): Retorna un código de status. Puede ser visto # escribiendo en la consola # # echo $? # # después de ejecutar el programa. # #VARIABLES: # %eax contiene el número de llamada del sistema # %ebx contiene el estado de retorno # .section .data .section .text .globl _start _start: movl $1, %eax # este es el número de comando del kernel Linux # (llamada al sistema) para salir de un programa movl $0, %ebx # Este es el número de estado que devolveremos al # sistema operativo. Cambiando esto se devolverán # cosas diferentes a echo $? int $0x80 # Esto le avisa al kernel que debe # ejecutar el comando de salida # de programa Lo que escribiste se llama código fuente. Este código es la forma entendible para los humanos de un programa. Para transfórmalo en un programa que una computadora pueda ejecutarlo, hay que ensamblarlo y enlazarlo.
Primero se ensambla. Este proceso transforma los comandos anteriores en instrucción que la maquina pueda comprender. La computadora solo entiende conjuntos de números, por lo que el lenguaje ensamblador es mas próximo a los humanos que las computadoras. Para ensamblar el programa debes ejecutar en la consola de comandos: as exit.s -o exit.o as es el comando para ejecutar el ensamblador. exit.s es el archivo fuente y -o exit.o le indica al ensamblador que coloque el archivo de salida en el archivo exit.o, el cual es un archivo objeto. Un archivo objeto es un archivo que esta en código maquina, pero todavía no se termino de crear como un ejecutable como tal. En los programas extenso tendrás mas de un archivo, el enlazador o linker se encarga de juntar estos archivos y agregar la información que el Kernel necesita para cargar y ejecutar el programa. Como en este caso solo hay un archivo, el enlazador solo agrega la información para poder ejecutarlo. Para enlazar un archivo, hay que ejecutar: ld exit.o .o exit ld es el comando para ejecutar el enlazador, exit.o es el archivo que se quiere enlazar y -o exit le indica al enlazador que debe colocar el programa en el archivo llamado exit. Cuando se modifica un programa, o incluso un comando, hay que re-ejecutar estos dos comandos ensamblador y enlazador. Se puede ejecutar el programa exit con el comando ./exit. El ./ le dice al Kernel que el programa no esta en otra carpeta o directorio, sino en la carpeta actual. Cuando se ejecuta el programa exit, solo finaliza, sin embargo, si después de finalizado el programa ejecutas: echo $? va a dar como salida un 0. Esto pasa por que todos los programas que finalizan le indican al Kernel un código de status, el cual puede indicar que todo se ejecuto correctamente, o que hubo un error. Si el código de status es diferente a 0, indica que hubo un error, una advertencia o un estado diferente a 0. El programador determina lo que cada numero significa. <En la siguiente sección vamos a ver cada parte del código.
Estructura de una programa assembly Al principio del programa, las líneas que empiezan con numeral # son comentarios. Estos comentarios no son traducidos por el ensamblador. Son usados por el programador para "hablar" con cualquier otra persona, o el mismo, que lea el código. El habito de escribir buenos comentarios en él código va a ayudar a entender tanto el como funciona como el por que existe el programa. El libro recomienda que siempre incluyas los siguientes comentarios: • El propósito del código • Una visión general del proceso • Cualquier cosa rara que haga tu programa y por que la hace. Después de los comentarios, esta la línea .section .data Cualquier cosa que empiece con un punto, no es traducido directamente en lenguaje maquina, sino que es una instrucción para el ensamblador. Existen las llamadas directivas de ensamblador o pseudo-operaciones por que las ejecuta el ensamblador, pero no son ejecutadas por la computadora. El comando .section separa el código en secciones. Este comando empieza la sección de datos, donde se lista cualquier almacenamiento de memoria que se vaya a usar. Después de esta sección, aparece otra sección: .section .text que empieza la sección de texto. La sección de texto de un programa es donde están las instrucciones del programa.
La siguiente instrucción es .globl _start Esta instrucción le indica al ensamblador que es importante recordad _start. _start es un símbolo, lo que significa que va a ser reemplazado durante el ensamblado o enlace. Generalmente son usados para marcar ubicaciones en el programa o datos, para que los puedas referenciar por este nombre y no por su numero de ubicación o dirección de memoria. .globl significa que el ensamblador no descartara este símbolo después de ensamblar, por que el enlazador lo va a necesitar. _start es un símbolo que siempre necesita marcarse con .globl por que indica el inicio del programa. Si no se marca esta ubicación de esta forma, la computadora cuando cargue el programa no entenderá por donde debe empezar a ejecutar el programa. La siguiente línea _start: define el valor de la etiqueta _start. Una etiqueta es un símbolo seguido por dos puntos. Las etiquetas definen el valor del símbolo. Cuando el programa es ensamblado, se le asigna a cada dato e instrucción una dirección. Las etiquetas le indican al ensamblador que el valor del símbolo esté donde vaya a estar la siguiente instrucción, ósea que la etiqueta almacené la dirección de la próxima instrucción a la misma etiqueta. Por eso, si la dirección de memoria de un dato o instrucción cambia, no se tiene que reescribir la dirección, sino que el símbolo la va a obtener automáticamente.
Ahora, las instrucciones de computadora. La primera instrucción es movl $1, %eax Cuando el programa la ejecuta, se transfiere un numero 1 al registro %eax. En lenguaje ensamblador, muchas instrucciones tienen operadores. movl tiene dos, la fuente u origen y el destino. En este caso la fuente u origen es el numero 1 y el destino es el registro %eax. Los operandos pueden ser un numero, dirección de memoria o registros. <Vea el Apéndice B para mas información sobre las instrucciones. En la mayoría de las instrucciones que tienen dos operandos, el primero es la fuente u origen y el segundo el destino. En este caso, el operando fuente u origen no es modificado. Otras instrucciones, como addl, subl y imull/ suma, resta y multiplicación tampoco lo modifican, solo hacen el calculo y lo guardan en el operando destino. Otros, son mas complicados. Como dice el libro, idivl división entera larga necesita que el dividiendo se ubique en %eax, mientras que %edx tiene que estar en cero por que va a ser el lugar donde se almacene el resto de la división, pero el divisor puede ser cualquier registro o posición de memoria. En los procesadores x86 hay varios registros de uso general: • %eax • %ebx • %ecx • %edx • %edi • %esi Pero también hay varios registros de uso especial, incluyendo: • %ebp: Puntero base • %esp: Puntero a pila • %eip: Puntero de instrucción • %eflags: Registros de flags Algunos de estos registros solo se pueden acceder desde instrucciones especiales, como %eip. Otros se pueden acceder como si fueran registros de propósito general, pero todos tienen un uso especial, o son mas rápidos si se usan de una forma especifica.
Entonces, la instrucción movl mueve el numero 1 dentro del registro %eax. El símbolo de billete o monetario $ delante del uno indica que se va a usar el modo inmediato de direccionamiento. Sin este símbolo, haría un direccionamiento directo, cargando cualquier numero que este en la dirección uno. La razón de haber movido un numero 1 dentro de %eax es porque se esta preparando para llamar al Kernel de Linux. El numero 1 es el numero de llamada de salida del sistema. Las llamadas al sistema se va discutir mas adelante, pero son solicitudes para obtener la ayuda del sistema operativo. Por lo general el programa no puede hacer todo, tareas como manejar archivos, comunicarse con otros programas o finalizar la ejecución son manejados por el sistema operativo a través de llamadas al sistema. Cuando vas a hacer una llamada al sistema, el numero de la llamada debe ser cargador en %eax. Según la llamada al sistema, otros registros pueden necesitar tener valores almacenados. Aunque, las llamadas al sistema no son el uso principal ni el único uso de los registros. <Para una lista completa de las llamadas al sistema y su numero, ver Apéndice C Usualmente, el sistema operativo necesita mas información. Por ejemplo, cuando se va a salir de un programa, necesita saber el código de estado, el cual debe ser cargado en %ebx y este es el valor que se devuelve cuando se ejecuta echo $?. Esta información extra que necesita el sistema operativo son parámetros. Entonces, para cargar el registro %ebx con un cero, se usa la siguiente instrucción: movl $0, %ebx Pero, no solo es necesario haber cargado el registro con este numero para finalizar el programa, esto no hace nada ya que los registros se suelen usar para varias cosas aparte de llamadas al sistema, como sumas o comparaciones.
La siguiente instrucción es la que hace la magia: int $0x80 El int es por interrupt interrupción. El numero 0x80 es el numero de interrupción a usar. Una interrupción interrumpe el flujo normal del programa, transfiere el control del programa a Linux para que puede hacer la llamada al sistema. Una vez que la interrupción termina, como programador no te importa como funciona sino que funciona, Linux devuelve el control al programa Revisión rápida de las llamadas al sistema: Para recapitular, se accede a las características del Sistema Operativo a través de llamadas al sistema. Éstas se invocan configurando los registros de una manera especial y emitiendo la instrucción int $0x80. Linux sabe a qué llamada del sistema queremos acceder por lo que almacenamos en el registro %eax. Cada llamada al sistema tiene otros requerimientos en cuanto a lo que necesita ser almacenado en los otros registros. El número de llamada al sistema 1 es la llamada al sistema de salida (exit), la cual requiere que el código de estado sea colocado en %ebx. Ahora, el libro recomienda hacer pequeñas modificaciones al programa, como cambiar el numero cargado en %ebx. También da una pequeña recomendación, "No te preocupes, la peor cosa que podría pasar es que el programa no se ensamble o enlace, o que se congele la pantalla. Es todo parte del aprendizaje.".
Anon sigue aprendiendo assembly? section .data text db 'Hello, world!', 10 section .text global _start _start: mov rax, 1 mov rdi, 1 mov rsi, text mov rdx, 14 syscall mov rax, 60 mov rdi, 0 syscall
>>804 Perdón negro, estuve ocupado. Esta noche seguiré con el hilo.
Planeando el programa En el siguiente programa vamos a tratar de encontrar el máximo de una lista de números. Las computadoras necesitan instrucciones muy detalladas, por lo que habrá que planear en detalle lo que hará el programa, lo que incluye: • ¿Dónde se va a almacenar la lista de números? • ¿Qué proceso va a necesitar para encontrar el número máximo? • ¿Cuánto espacio necesitamos para almacenar las instrucciones del proceso? • ¿Todo el almacenamiento va a caber en los registros, o se necesita usar memoria? Puedes pensar que algo tan simple como encontrar el numero máximo de una lista no puede tomar tanta planeación. Puedes decirle a las personas que encuentren el numero máximo, lo harán sin problema. De todas formas, nuestras mentes están hechas para hacer varias tareas complejas automáticamente. En cambio las computadoras necesitan ser instruidas a través del proceso. También podemos guardar cualquier numero de cosas en nuestra mente sin mucho problema, incluso lo hacemos sin darnos cuenta. Por ejemplo, si escaneas una lista de números buscando el máximo, lo mas probable es que mantengas en mente dos cosas, el numero mas grande que encontraste hasta el momento, y la ubicación de este numero en la lista. Mientras tu mente hace esto automáticamente, con las computadoras tienes que ser explicito en especificar el almacenamiento para guardar el máximo numero y su ubicación. Puedes tener otros problemas, por ejemplo, cuando parar. Cuando lees un pedazo de papel, puedes detenerte cuando te quedas sin números. Pero la computadora solo contiene números, por lo que no tiene idea de cuando llego a lo ultimo de la lista.
En las computadoras, tienes que pensar todos lo que la computadora debe hacer. Supongamos que el nombre de la dirección donde la lista de numeros empieza es data_items. También, el ultimo numero de la lista va a ser cero, por lo que ya se sabe donde debe parar el programa. También es necesario un valor para fijar la posición actual en la lista, otro para el valor que se va a examinar y un ultimo valor para guardar el mayor numero: • %edi guarda la posición actual en la lista • %ebx guarda el máximo numero • %eax guarda el numero que se esta examinando El programa empieza por examinar el primer numero de la lista, y como acaba de empezar, ese numero va a ser el mayor numero, por lo que se tiene los siguientes pasos: 1. Verificar si el numero actual (%eax) es cero. 2. Si lo es, el programa termina. 3. Incrementa la posición actual en uno (%edi). 4. Carga el siguiente numero en la lista en el registro de valor actual (%eax). 5. Compara el numero actual (%eax) con el mayor numero (%ebx). 6. Si el numero actual es mayor, se reemplaza el numero guardad en el registro de mayor numero por el numero actual. (%eax) --->>> (%ebx) 7. Repetir.
Dependiendo si la condición del 'si' 'if' es correcta, el programa tomara un camino u otro. Por ejmplo, si en el primer paso el numero actual es igual a cero entonces se ejecuta el paso dos el programa termina, sino salta al paso tres. Esos 'si' 'if' en los lenguajes de programación son instrucciones de control de flujo, por que le dice a la computadora, según una condición, que instrucción seguir. Esto se logra gracias a dos instrucciones diferentes, el salto condicional y el salto incondicional. El salto condicional cambia el rumbo en base al resultado de una comparación o calculo previo. En cambio, el salto incondicional salta directamente a un camino diferente. Puede parecer que es inútil, pero es necesario en muchos casos. Por ejemplo, es útil cuando se debe volver al rumbo principal del programa. Otro uso del control de flujo es la implementación de bucles. Un bucle es una parte de un programa que se repite, puede implementarse con saltos incondicionales al comienzo del bucle en el final de este. También es importante establecer un salto condicional para salir de este bucle, o sino se convierte en un bucle infinito. En la siguiente sección se va a implementar el programa como fue planeado. Parece complicado, y en cierto punto lo es, pero cuando comienzas a programar es difícil convertir los pensamientos normales en un proceso que la computadora pueda entender.
>>804 Anon, ¿Puedes explicarme ese programa? ¿Cuál ensamblador usas?
>>811 Es del segundo vid de esta lista https://www.youtube.com/playlist?list=PLXNR8A6QBzobli2v8QoXkJVpQWNx_fH-1 Para ser honesto solo entiendo que es un hola mundo en x_64 y poco más. Desde ese día no he avanzado porque primero quiero aprender c y cosas de estructura de datos y algoritmos, es decir programación como dios manda. De todas formas mi objetivo es aprender asm y entender el funcionamiento de la máquina. Also libros que he encontrado de asm x_64 sobre gnu/linux >Ed Jorgensen - x86-64 Assembly Language Programming with Ubuntu (2020) >Ray Seyfarth - Introduction to 64 Bit Intel Assembly Language Programming for Linux (2011) >Jeff Duntemann - x64 Assembly Language Step-by-Step: Programming with Linux (2023)
>>812 Estaba pensando, como mencione antes hacer un hilo sobre el libro "Introducción a los algoritmos", tercera edición de Cormen, Leiserson, Rivest y Stein. Podría crearlo y con suerte aumente el trafico en >>>/hispatec/. También es buena idea, como menciono un negro, tener un hilo sobre programación diaria.
>>815 ¿Y si abre un general de programación para que sirva de centro de operaciones desde donde gestionar hilos relacionados? Por cierto, de ese libro hay una cuarta edición en libgen.
Encontrando el numero máximo Escribe el siguiente programa como maximum.s: #PURPOSE: Este programa encuentra el maximo numero de # un conjunto de numeros. # #VARIABLES: Los registros tiene el siguiente uso: # # %edi - Guarda el indice del numero que se esta comparando actualmente # %ebx - El mayor numero encontrado # %eax - El numero actual que se esta comparando # # Se usan las siguientes ubicaciones de memoria: # # data_items - Contiene la lista. Se usa un 0 # para senialar el final de la lista # .section .data data_items: #Estos son los números a analizar .long 3,67,34,222,45,75,54,34,44,33,22,11,66,0 .section .text .globl _start _start: movl $0, %edi # Mueve un cero al registro indice movl data_items(,%edi,4), %eax # Carga el primer byte de datos movl %eax, %ebx # como este es el primer numero, %eax es # el mas grande start_loop: # empieza un bucle cmpl $0, %eax # verifica si llego al final je loop_exit incl %edi # incrementa en indice en uno movl data_items(,%edi,4), %eax # carga el elemento en esa posicion en %eax cmpl %ebx, %eax # compara los valores jle start_loop # salta al principio del bucle si el numero # no es mayor movl %eax, %ebx # Carga el nuevo numero como el mayor jmp start_loop # Salta al principio del bucle loop_exit:
[Expand Post] # %ebx es el codigo de estado para la llamada al sistema de salida # y ya tiene el maximo numero movl $1, %eax #1 es la llamada al sistema de salida int $0x80 Entonces, ensambla y enlaza con estos comandos: as maximum.s -o maximum.o ld maximum.o -o maximum Ahora ejecuta el programa y verifica la salida. ./maximum echo $?
Este programa va a devolver el numero 222. En la sección de datos esta la etiqueta data_items que referencia a la ubicación que le sigue, ósea, la sección de memoria que guarda la lista a analizar. Después hay una instrucción que empieza con .long. Esta instrucción sirve para que assembly reserve memoria para la lista de números que le sigue. Algo muy importante y a tener en cuenta es que data_items referencia a la ubicación del primer elemento o numero de la lista. Hay muchas mas instrucciones del tipo .long Voy a buscar mas información sobre estas directivas, y si encuentro lo posteo.: .byte Reserva memoria para un byte, soporta números entre 0 a 255. .int Reserva dos ubicaciones por número, por lo que soporta números entre 0 a 65535. .long Reserva cuatro ubicaciones de memoria por numero. Son del mismo tamaño que los registros, por lo que pueden soportar números entre 0 y 4294967295 .ascii Sirve para guardar en memoria caracteres o cadenas de caracteres, como dice el libro, si tienes la directiva .ascii "Hello World!\0", entonces reservara 12 ubicaciones. \0 es un carácter de escape que indica el final de la cadena. El autor hace una mención de los métodos para que el bucle termine. Tanto haber hard-codeado el tamaño de la lista, como crear un símbolo que marque el final de la lista son posibles soluciones. Pero a lo que quiere resaltar es que la computadora no sabe, solo lo que el programador dice. data_items no tiene la declaración o modificador .globl por que es una ubicación que solo se usa dentro de ese programa, no en otros archivos. Al contrario que con el símbolo _start, por que en este caso es necesario para que Linux conozca por donde debe empezar la ejecución del programa. Por lo que no es un error agregar la declaración .globl a _start, solo es innecesario por que no se esta usando esa etiqueta en otros archivos.
Una variable es una ubicación de memoria usada para un propósito especifico, con un nombre dado por el programador. Por ejemplo, el programa de encontrar el mayor numero tiene tres variables, de la cuál una es usada para almacenar el mayor numero encontrado. Hay casos en que las variables, por ser mas que los registros, deben guardarse en memoria y, al momento de usarse, después cargarse en los registros. Esto es algo que se va a ver mas adelante. Los índices, en este caso de la lista de números, son la posición en la que se encuentra cada numero. El primer elemento siempre tiene índice cero y a medida que se avanza por la lista, el índice incrementa en uno. Por eso mismo, al usar el registro %edi como índice, se lo carga con el valor 0. La siguiente instrucción es importante: movl data_items(,%edi,4), %eax Para entender esta instrucción hay que tener en cuenta que: • data_items es la ubicación en memoria del comienzo de la lista de los números No es la lista, solo el primer elemento • Cada elemento tiene, para su almacenamiento, 4 ubicaciones de memoria, por que se declaro que los elementos de la lista son de tipo .long%edi esta almacenando 0 en este punto del programa. Básicamente, lo que esta instrucción dice es "comienza en el principio de data_items, y toma el primero elemento, y recuerda que cada elemento tiene 4 ubicaciones de memoria para su almacenamiento. Entonces, guarda ese elemento de la lista en %eax". La forma general es la siguiente: movl BEGINNINGADDRESS(,%INDEXREGISTER,WORDSIZE) Como %eax esta almacenando en este momento al primer numero, es el mayor numero que encontró el programa, por lo que se lo copia en %ebx
Acá empieza lo interesante, ahora estamos dentro de un bucle. Un bucle es un segmento del programa que se va a ejecutar mas de una vez. Se marco el comienzo del bucle con el símbolo start_loop. Un bucle se usa siempre que se vaya a repetir un fragmento del código, se sepa o no la cantidad de veces que se vaya a repetir. Cuando el código alcanza el final del bucle, pero la condición para salir de este no es verdadera, se vuelve al principio del bucle. Ósea, se vuelve al símbolo start_loop. Después del comienzo del bucle esta el siguiente fragmento de código. cmpl $0, %eax je loop_exit La instrucción cmpl compara dos valores, en este caso el numero cero con el numero almacenado en %eax. Esta comparación afecta a un registro que no se había mencionado en el programa, el registro %eflags, también conocido como registro de estatus, tiene mucho usos, pero el que vamos a ver ahora es donde se almacena el resultado de la comparación en este registro. La siguiente línea es un control de código, que dice que el programa debe saltear al símbolo loop_exit si los valores antes comparados son iguales. Eso es lo que la e de la instrucción je significa. Hay muchas variantes de la instrucción jump: je:Salta si los valores comparados son iguales jg:Salta si el segundo valor es mas mayor que el primero jge:Salta si el segundo valor es mayor o igual al primero jl:Salta si el segundo valor es menor que el primero jle:Salta si el segundo valor es menor o igual al primero jmp:Salta no importa que, por lo que no necesita estar precedido por una comparación La lista completa esta documentado en el Apéndice B. En el caso del programa, salta al símbolo loop_exit si el valor que almacena %eax es igual a cero.
Siguiendo con el programa, si el ultimo numero cargado no es cero, entonces se sigue con estas instrucciones: incl %edi movl data_items(,%edi,4), %eax En este caso, la instrucción incl se encarga de incrementar en uno el valor de %edi, entonces copia el elemento de la lista que este en la posición indicada por %edi en el registro %eax, ahora este registro tiene el siguiente valor a ser testeado. cmpl %ebx, %eax jle start_loop Aca compara el valor almacenado en el registro %eax con el mayor numero encontrado, almacenado en %ebx. Si el mayor numero encontrado es mayor o igual al numero actual, salta al principio del bucle. Si, por el contrario, el mayor numero es menor al numero actual, entonces se debe guardar en %ebx y saltar al principio del bucle. movl %eax, %ebx jmp start_loop Entonces, el bucle se ejecuta hasta que alcanza un valor cero, en ese momento salta al símbolo loop_exit. En esta parte del programa, se llama al kernel de Linux para terminar la ejecución del programa. Como se menciono antes, para salir del programa se carga un 1 en el registro %eax y se especifica el estatus de salida en el registro %ebx. Como se quiere retornar el mayor numero de la lista, no se toca ese registro: movl $1, %eax int 0x80
instrucciones movl origen, destino: Copia un valor desde origen a destino. cmpl operando1, operando2: Compara los dos operandos y actualiza el registro eflags. incl operando: Incrementa a operando en uno. addl origen, destino: Suma el valor del operando fuente al operando de destino y almacena el resultado en el operando de destino. subl origen, destino: resta el valor del operando fuente al operando de destino y almacena el resultado en el operando de destino. imull origen, destino: hace una multiplicación con signo y guarda el resultado en destino. divl divisor: hace una división sin signo, divide el contenido de la doble palabra contenida en %edx:%eax por el registro o ubicación de memoria especificado. El registro %eax guarda el cociente resultante y el registro %edx el resto resultante. Si el cociente es demasiado grande para %eax, se activa una interrupción de tipo 0. idivl divisor: trabaja igual que divl, pero hace una división con signo. int numero: causa una interrupción según numero. Registros Uso general %eax %ebx %ecx %edx %edi %esi Uso especial %eflags: Registro de estatus Reserva de memoria .byte: Reserva memoria para un byte, soporta números entre 0 a 255. .int: Reserva dos ubicaciones por número, por lo que soporta números entre 0 a 65535. .long: Reserva cuatro ubicaciones de memoria por numero. Son del mismo tamaño que los registros, por lo que pueden soportar números entre 0 y 4294967295 .ascii: Sirve para guardar en memoria caracteres o cadenas de caracteres, como dice el libro, si tienes la directiva .ascii "Hello World!\0", entonces reservara 12 ubicaciones. \0 es un carácter de escape que indica el final de la cadena. Saltos Saltos condicionales je:Salta si los valores comparados son iguales jg:Salta si el segundo valor es mas mayor que el primero jge:Salta si el segundo valor es mayor o igual al primero jl:Salta si el segundo valor es menor que el primero jle:Salta si el segundo valor es menor o igual al primero Saltos incondicionales jmp:Salta no importa que, por lo que no necesita estar precedido por una comparación
Modos de direccionamiento En la sección Métodos de acceso a datos en el capitulo se mostro diferentes tipos de métodos de direccionamiento disponibles para usar en ensamblador. En esta sección se va a mostrar como estos modos son representados en las instrucciones de ensamblador. La forma general de referencias a direcciones de memoria es esta: ADDRESS_OR_OFFSET(%BASE_OR_OFFSET,%INDEX,MULTIPLIER) Todos esos campos son opcionales, para calcular la dirección simplemente se calcula de es esta forma: FINAL ADDRESS = ADDRESS_OR_OFFSET + %BASE_OR_OFFSET + MULTIPLIER * %INDEX ADDRESS_OR_OFFSET y MULTIPLIER deben ser constantes, mientras que los otros dos deben ser registros. si alguna de esas partes se excluye, simplemente se reemplaza con un cero en la ecuación. Todos los métodos de direccionamientos mostrados en el capitulo 2, excepto el modo inmediato, puede ser representado de esta forma.
Modo de direccionamiento directo En este caso, solo se usa la parte ADDRESS_OR_OFFSET. Por ejemplo: movl ADDRESS, %eax Esto carga %eax con el valor que esta en la posición ADDRESS. Modo de direccionamiento indexado En este, solo se usa las partes ADDRESS_OR_OFFSET e %INDEX. Se puede usar cualquier registro de propósito general como el registro index. También se puede agregar un multiplicador constante de 1, 2 o 4 al registro índice, para hacer mas fácil el indexado de bytes, bytes dobles y palabras. Por ejemplo, se tiene una cadena de bytes como string_start y se quiere acceder a la tercer (un índice de 2 ya que empezamos a contar el índice en cero), y %ecx contiene el valor 2, si se quiere cagar este valor en %eax se podría hacer de esta forma: movl string_start(,%ecx,1), %eax Método indirecto El metodo indirecto carga un valor de la dirección indicada por un registro. Por ejemplo, si %eax contiene una dirección, se podría mover el valor de esa dirección a %ebx haciendo lo siguiente: movl (%eax), %ebx
Método de direccionamiento con puntero base Este direccionamiento es similar al direccionamiento indirecto, excepto que agrega un valor constante a la dirección en el registro. Por ejemplo, si se tiene un registro donde el valor de la edad ocupa 4 bytes, y se tiene la dirección del registro en %eax, se puede carga el valor de edad en %ebx usando la siguiente instrucción: movl 4(%eax), %ebx Método inmediato El método inmediato es muy simple, no sigue la forma general. Este método es usado para cargar valores directos en los registros en la memoria. Por ejemplo, si se quiere cara el numero 12 en %eax, solo hay que hacer lo siguiente: movl $12, %eax Para indicar que se va a usar el método inmediato se usa el símbolo de dinero en frente del numero. Si no se usara, se toma como el modo de direccionamiento directo. Método de direccionamiento de registro Este modo solo mueve datos dentro o fuera de los registros, este método se uso en todos los anteriores ejemplos para la otra parte del operando.
Estos métodos de direccionamiento son muy importantes, por que cualquier acceso a memoria va a utilizar uno de estos. Todos estos métodos pueden ser usados como operando de destino u origen, a excepción del método inmediato, este método solo puede ser usado como operando de destino. Existen diferentes instrucciones para los diferentes tamaños de valores que se quiera mover. Por ejemplo movl se usa para mover una palabra, en cambio movb se usa para mover un byte. Encontré estas posibles variantes del operando mov: movb: Indica un byte (8 bits). movw: Indica una palabra (16 bits). movl: Indica un valor de doble palabra (32 bits). movq: Indica un valor de cuatro palabras (64 bits) También se puede dividir a los registros. Por ejemplo el registro %eax almacena una palabra, pero si solo se necesita dos bytes se puede usar %ax, justamente la e en %eax significa extended. Si se quiere usar un solo byte se puede usar %al o %ah. En estos casos la l simboliza low y la h simboliza high. Tanto high como low hacen referencia en la significancia del bit. Cabe resaltar que si se tenía un numero almacenado en %eax y se cambia el valor de %al, se va a corromper el valor que esta almacenado en %eax.
Con esto ya se pueden hacer programas interesantes, como el ordenamiento por burbuja. https://es.wikipedia.org/wiki/Ordenamiento_de_burbuja
>>844 Me equivoque, todavía no se puede programar un ordenamiento de burbuja con lo que se explico. Sigamos con el libro.


Forms
Eliminar
Informar
Respuesta rápida