[imagen obtenida de:http://www.techfuels.com/attachments/ram/17492d1267249742-ram-computerram-main_full.jpg ]
¿Qué es la memoria principal?
La memoria principal o RAM (Random Access Memory) es una memoria en la que se puede acceder a cualquier parte de ella sin tener que acceder a partes precedentes de está. Está memoria es mas lentamente accesible que los registros pero mas rápida que el disco duro por eso es utilizada para guardar datos que se están utilizando en un determinado momento por un programa pero que no están siendo utilizados por operaciones comunes en los registros.
De forma lógica la memoria principal que normalmente es de entre uno a cuatro giga bytes es dividida en pequeñas celdas de 1 byte y cada celda se le da una dirección de memoria, después cuando un programa en ejecución necesita guardar datos se le asigna un rango de direcciones para trabajar.
[imagen obtenida de: http://www.jegsworks.com/lessons-sp/lesson4/memaddress.gif]
¿Que es la pila de memoria?
Una pila es una lista ordinal o estructura de datos en la que el modo de acceso a sus elementos es de tipo último en entrar, primero en salir que permite almacenar y recuperar datos.
En lenguaje ensamblador esta estructura de datos se utiliza para llevar control de los datos usados por diferentes bloques del programa y abstraer los bloques de ejecución en funciones y las variables(todo esto muy conocido en lenguajes de programación de capas superiores). Para esto retomaremos el tema de los registros, los registros utilizados por la pila de memoria son:
%ESP Stack Pointer
Este registro es un puntero(contiene la dirección de memoria de otra variable) hacia el ultimo elemento usado en la pila. Este se manipula de forma implícita por quien define el código.
%EBP Base Pointer
Este registro es un puntero que se utiliza para referenciar a los parámetros y variables locales de una función.
%EIP instruction pointer
Este guarda la dirección de la siguiente instrucción del CPU a ser ejecutada y se modifica directamente por las instrucciones "call"(mandar a llamar a una función) y "jmp"(saltar a otro bloque del código).
Ahora que sabemos para que sirve cada registro, necesitamos darnos una idea como se organiza la pila en ensamblador, cada que se entra a una función el puntero %EBP es el que marca donde empieza y termina, en cambio el puntero %ESP nos ayuda a saber donde terminan las variables locales de una función.
[imagen obtenida de: http://unixwiz.net/images/stackframe-cdecl.gif]
Es así que modificando el valor de estos registros punteros podemos manejar asignar fragmentos de memoria para diferentes fragmentos o funciones del programa por ejemplo para declarar una variable entera en Ansi C lo que se haría en assembly x86 seria:
pushl %ebp ;Recupera la base de la pila de la función superior movl %esp, %ebp ;Actualiza la el puntero %esp a la base de la pila anterior subl $4, %esp ;Despues le resta cuatro bytes a la dirección de memoria de %ebp movl $0, (%esp) ;Ahora asigna el valor 0 al espacio que reservamos
PUSH
Esta es una operación que decrementa una cantidad de posiciones el puntero base(%EBP) y en la dirección resultante guarda los valores recibidos como operando.
POP
Esta operación sirve para recuperar el ultimo valor empujado con la operación push, esto quiere decir que recupera el valor superior al puntero %EBP y luego incrementa una cantidad de posiciones el puntero.
Ejemplo de recursión
Como ejemplo para manejo de pila decidi hacer el mismo que para la publicación anterior solo que esta vez lo construí utilizando un algoritmo recursivo, para mostrar el movimiento de punteros de pila llamando funciones recursivamente.
.globl numeros
numeros:
pushq %rbp
movq %rsp, %rbp
jmp .LF ;Salta al ultimo bloque de la funcion
.L0:
movq 16(%rsp),%rax ;recupera el valor que se dio como parametro
subq $1, %rax ;y se le resta uno
pushq %rax ;Despues se empuja al puntero para mandarlo como parametro
call numeros ;y se manda a llamar a su misma funcion
popq %rax ;recupera el valor parametro es igual a lo que hice cuatro
; lineas anteriores pero ahora modificando el punteor base
pushq %rax ;ahora manda el valor a la funcion print que yo mismo defini
call print ;como en el ejemplo de la publicacion de registros
popq %rax
movq 16(%rsp),%rax
addq $1, %rax
movq %rax, 16(%rsp) ;suma uno al parametro recibido y termina la funcion
leave
ret
.LF:
cmpl $0, 16(%rsp) ;compara el ultimo valor mandado como parametro
jg .L0 ;si es mayor que cero pasa al bloque .L0
leave
ret
.globl main
main:
pushq %rbp ;Recupera la base de la función anterior
movq %rsp, %rbp
subq $8, %rsp
pushq $10 ;Mueve el valor 10 a la dirección del nuevo %RBP y disminuye el valor del puntero
call numeros ;Manda a llamar a la función numeros
leave
ret
Y esto fue el resultado que me imprimio en terminal:
Referencias:
http://www.masadelante.com/faqs/memoria-ram
http://www.jegsworks.com/lessons-sp/lesson4/lesson4-5.htm
http://es.wikipedia.org/wiki/Pila_%28inform%C3%A1tica%29
http://unixwiz.net/techtips/win32-callconv-asm.html
http://moisesrbb.tripod.com/unidad3.htm

No hay comentarios.:
Publicar un comentario