arm_saltos
Diferencias
Muestra las diferencias entre dos versiones de la página.
Ambos lados, revisión anteriorRevisión previaPróxima revisión | Revisión previa | ||
arm_saltos [2020/05/27 03:00] – andrew | arm_saltos [2020/05/27 19:54] (actual) – [Subrutinas] andrew | ||
---|---|---|---|
Línea 5: | Línea 5: | ||
==== Subrutinas ==== | ==== Subrutinas ==== | ||
- | Un aspecto muy peculiar de la arquitectura ARM es que las llamadas a subrutinas se hacen mediante un sencillo añadido a la instrucción de salto. | + | Un aspecto muy peculiar de la arquitectura ARM es que las llamadas a subrutinas se hacen mediante un sencillo añadido a la instrucción de salto B (Branch). |
+ | |||
+ | Sintaxis: | ||
+ | B{L} {< | ||
+ | |||
+ | Donde: | ||
+ | |||
+ | L setea en 1 el bit 24 (el bit L) en la instrucción. Con esta opción, la instrucción almacena una dirección de retorno en el registo de link LR (R14). Si no se pone el sufijo L, la instrucción simplemente salta sin almacenar ninguna dirección. | ||
+ | |||
+ | < | ||
+ | |||
+ | < | ||
+ | - Extender a 30 bits el signo de los 24 bits en complemento A dos (signed_immed_24). | ||
+ | - Hacer un shift a izquierda de dos bits para formar un valor de 32 bits (SignExtend_30). | ||
+ | - El nuevo PC es: PC = PC + (SignExtend_30(signed_immed_24) << 2) | ||
+ | |||
+ | Si agregamos el sufijo L, la instrucción B es BL y se llama (Branch and Link), y se usa para llamar | ||
main : mov r1, #1 | main : mov r1, #1 | ||
Línea 15: | Línea 31: | ||
bx lr | bx lr | ||
| | ||
+ | | ||
+ | La instrucción BL se usa en combinación con la instruccion BX (Branch and Exchange). | ||
+ | |||
+ | Sintaxis: | ||
+ | | ||
+ | |||
+ | Donde: | ||
+ | | ||
+ | < | ||
+ | |||
+ | <Rm> Registro que contiene el valor de la dirección de destino del salto | ||
+ | |||
+ | En particular, en el retorno de una subrutina se usa Rm=LR. El retorno se logra copiando el registro LR (R14) al PC (Program Counter). | ||
+ | |||
+ | |||
+ | Continuemos analizando el ejemplo de subrutina anterior | ||
+ | |||
+ | main : mov r1, #1 | ||
+ | mov r2, #2 | ||
+ | bl subrut | ||
+ | mov r4, #4 /* Siguiente instrucción */ | ||
+ | ... | ||
+ | | ||
+ | bx lr | ||
+ | |||
+ | Si seguimos el flujo del programa primero cargamos r1 a 1, luego r2 a 2 y lo siguiente que hay es una llamada a la subrutina. En dicha llamada el procesador carga en **lr** la dirección de la siguiente instrucción “mov r4, #4” y salta a la etiqueta subrut. Se ejecuta el “mov r3, #3” de la subrutina y después **“bx lr”** que vendría a ser la instrucción de retorno. Es decir, salimos de la subrutina retomando el flujo del programa principal, ejecutando “mov r4, #4”. | ||
+ | |||
- | La convención AAPCS que nos servirá trabajar con las subrutinas | + | La convención AAPCS nos servirá |
==== Convención AAPCS ==== | ==== Convención AAPCS ==== | ||
Línea 23: | Línea 66: | ||
for the ARM Architecture). | for the ARM Architecture). | ||
- | - Podemos usar hasta cuatro registros (desde r0 hasta r3) para pasar parámetros y hasta dos (r0 y r1) para devolver el resultado. \\ | + | - **Parámetros input**: |
- | - No estamos obligados a usarlos todos, si por ejemplo la subrutina sólo usa dos parámetros de tipo int con r0 y r1 nos basta. Lo mismo pasa con el resultado, podemos no devolver nada (tipo void), devolver sólo r0 (tipo int ó un puntero a una estructura más compleja), o bien devolver r1:r0 cuando necesitemos enteros de 64 bits (tipo long long). | + | - **Parámetros output**: |
- | - Los valores están alineados a 32 bits (tamaño de un registro), salvo en el caso de que algún parámetro sea más grande, en cuyo caso alinearemos a 64 bits. | + | - **Alineación de la memoria**: |
- | - El resto de parámetros se pasan por pila. En la pila se aplican las mismas reglas de alineamiento que en los registros. La unidad mínima son 32 bits, por ejemplo si queremos pasar un char por valor, extendemos de byte a word rellenando con ceros los 3 bytes más significativos. Lo mismo ocurre con los enteros de 64 bits, pero en el momento en que haya un sólo parámetro de este tipo, todos los demás se alinean a 64 bits. | + | - **Preservar registros**: |
- | - Es muy importante preservar el resto de registros (de r4 en adelante incluyendo lr). La única excepción es el registro r12 que podemos cambiar a nuestro antojo. Normalmente se emplea la pila para almacenarlos al comienzo de la subrutina y restaurarlos a la salida de ésta. Podemos usar como registros temporales (no necesitan ser preservados) los registros desde r0 hasta r3 que no se hayan usado para pasar parámetros. | + | - **Alineación del stack**: |
- | - La pila debe estar alineada a 8 bytes, esto quiere decir que de usarla para preservar registros, debemos reservar un número par de ellos. Si sólo necesitamos preservar un número impar de ellos, añadimos un registro más a la lista dentro del push, aunque no necesite ser preservado. | + | |
- | - Aparte | + | |
- | Cuando programamos | + | Cuando programamos |
+ | |||
+ | Para poder reusar nuestras funciones en otros proyectos | ||
Lo mejor para entender estas reglas es con una serie de ejemplos: | Lo mejor para entender estas reglas es con una serie de ejemplos: | ||
Línea 71: | Línea 114: | ||
swi 0 // Salida al sistema operativo | swi 0 // Salida al sistema operativo | ||
- | Continuemos analizando el primer ejemplo sencillo de subrutina | ||
- | |||
- | main : mov r1, #1 | ||
- | mov r2, #2 | ||
- | bl subrut | ||
- | mov r4, #4 /* Siguiente instrucción */ | ||
- | ... | ||
- | | ||
- | bx lr | ||
- | |||
- | Si seguimos el flujo del programa primero cargamos r1 a 1, luego r2 a 2 y lo siguiente que hay es una llamada a subrutina. En dicha llamada el procesador carga en **lr** la dirección de la siguiente instrucción “mov r4, #4” y salta a la etiqueta subrut. Se ejecuta el “mov r3, #3” de la subrutina y después **“bx lr”** que vendría a ser la instrucción de retorno. Es decir, salimos de la subrutina retomando el flujo del programa principal, ejecutando “mov r4, #4”. | ||
==== Subrutinas anidadas ==== | ==== Subrutinas anidadas ==== | ||
Línea 107: | Línea 139: | ||
Vemos en el último nivel (nivel2) podemos ahorrarnos el tener que almacenar y recuperar lr en la pila. | Vemos en el último nivel (nivel2) podemos ahorrarnos el tener que almacenar y recuperar lr en la pila. | ||
- | Las instrucciones de salto en la arquitectura ARM abarcan una zona muy extensa, hasta 64 Mb (32 Mb hacia adelante y otros 32 Mb hacia atrás). | + | Las instrucciones de salto en la arquitectura ARM abarcan una zona muy extensa, hasta 64 Mb (32 Mb hacia adelante y otros 32 Mb hacia atrás). |
- | En caso de necesitar un salto mayor recurrimos a la misma solución de la carga de inmediatos del mov, solo que el registro a cargar es el pc. | + | |
- | ldr pc, = etiqueta | ||
[[oc_raspi0|Volver]] | [[oc_raspi0|Volver]] |
×
iphelper toolbox
you see this when javscript or css is not working correct
Untested
arm_saltos.1590548410.txt.gz · Última modificación: 2020/05/27 03:00 por andrew