En el proceso de escribir el gestor de arranque STM8uLoader para microcontroladores STM8, se hizo necesario medir la profundidad de la pila.
Hagamos preguntas:
- ¿Qué sucede si intentas insertar más información en la pila que su profundidad?
- ¿Qué sucede si intenta extraer más información de la pila de la que la colocó?
- ¿Qué sucede si inicializa el puntero SP de la pila con una dirección que va más allá de los límites de la pila?
La memoria RAM y la profundidad de la pila pueden variar para diferentes modelos STM8.
Para el estudio, se eligió el modelo STM8S103F3.
La documentación para el STM8S103F3 proporciona los siguientes datos:
- profundidad de pila 513 bytes;
- cuando se reinicia, el puntero SP se inicializa a 0x03FF (RAM END);
- la pila crece en la dirección de direcciones decrecientes.
El cálculo muestra que el límite inferior de la pila es:
0x03FF - 513 = 0x01FF
Para romper este límite, debe empujar unos pocos más de 513 bytes en la pila.
El contenido de la pila no nos interesa. Es suficiente conocer el contenido del puntero a la pila SP, que debe contener la dirección de la siguiente celda RAM no ocupada por la pila.
Colocaremos secuencialmente los bytes con cualquier comando push (por ejemplo, push A) y antes de cada paso enviaremos el contenido del SPH más alto y el byte SPL más bajo del puntero de pila SP al UART.
Algoritmo de procedimiento:
1 Inicialice el puntero de la pila con el valor 0x03FF y configure el UART;
2 Estamos a la espera de cualquier byte del programa del terminal;
3 bytes aceptados;
4 Envíe el contenido del puntero SP al UART;
5 Empujamos el contenido de la batería a la pila con el comando push A;
6 Si los ciclos de envío son inferiores a 64, vaya al paso 4;
7 Si los ciclos de envío 64, vaya al paso 2.
; UART 9600/8N1 mov UART1_BRR2, #$00 ; mov UART1_BRR1, #$0D ; / mov UART1_CR2, #%00001100 ; SP $03FF ldw X, #$03FF ; X <= RAM END ldw SP, X ; SP <= X ; wait_rx_byte: btjf UART1_SR, #5, wait_rx_byte ; ld A, UART1_DR ; bset PB_DDR,#5 bset PB_CR1,#5 ldw Y, #64 ; Y <= 64 stack_cycle: ldw X, SP ; X <= SP ; SPH UART ; rlwa X ; A <- XH <- XL <- A ld A, XH ; A <- XH ld UART1_DR, A ; UART1_DR <= A wait_tx_byte_XH: btjf UART1_SR, #7, wait_tx_byte_XH ; SPL UART ; rlwa X ; A <- XH <- XL <- A ld A, XL ; A <- XL ld UART1_DR, A ; UART1_DR <= A wait_tx_byte_XL: btjf UART1_SR, #7, wait_tx_byte_XL ; A push A ; M(SP
Observamos cómo el programa de terminal acepta secuencialmente el contenido del puntero SP a partir de 0x03FF:
03 FF 03 FE 03 FD 03 FC 03 FB 03 FA 03 F9 03 F8 03 F7 03 F6 03 F5 03 F4 03 F3 03 F2 03 F1 03 F0 03 EF 03 EE 03 ED 03 EC 03 EB 03 EA 03 E9 03 E8 03 E7 03 E6 03 E5 03 E4 03 E3 03 E2 03 E1 03 E0 03 DF 03 DE 03 DD 03 DC 03 DB 03 DA 03 D9 03 D8
Después de que el valor alcance 0x01FF (borde de pila previamente calculado)
el puntero SP nuevamente tomó el valor 0x03FF (la pila se cerró en un anillo)
y comencé a sobrescribir los datos más antiguos
02 0F 02 0E 02 0D 02 0C 02 0B 02 0A 02 09 02 08 02 07 02 06 02 05 02 04 02 03 02 02 02 01 02 00 01 FF 03 FF 03 FE 03 FD 03 FC 03 FB 03 FA 03 F9 03 F8 03 F7 03 F6 03 F5 03 F4 03 F3 03 F2 03 F1 03 F0 03 EF 03 EE 03 ED 03 EC 03 EB 03 EA 03 E9
Ahora veamos cómo se comportan los contenidos del puntero SP si intentamos recuperar ilimitadamente los contenidos de la pila.
Algoritmo de procedimiento:
1 Inicialice el puntero de la pila con el valor 0x03FF y configure el UART;
2 Estamos a la espera de cualquier byte del programa del terminal;
3 bytes aceptados;
4 Extraemos el contenido de la pila con el comando "pop A" a la batería;
5 Envía el contenido del puntero SP al UART;
6 Si los ciclos de envío son inferiores a 64, vaya al paso 3;
7 Si los ciclos de envío 64, vaya al paso 2.
Los elementos 4 y 5 del algoritmo y el comando "empujar A" se intercambian con el comando "pop A".
A pesar del hecho de que inicializamos el puntero SP con el valor 0x03FF ya después del primer comando emergente A, el puntero tomó el valor 0x01FF y continuó aumentando hacia 0x03FF.
01 FF 02 00 02 01 02 02 02 03 02 04 02 05 02 06 02 07 02 08 02 09 02 0A 02 0B 02 0C 02 0D 02 0E 02 0F 02 10 02 11 02 12 02 13 02 14 02 15 02 16 02 17 02 18 02 19 02 1A 02 1B 02 1C 02 1D 02 1E 02 1F 02 20 02 21 02 22 02 23 02 24 02 25 02 26
Alcanzando el valor 0x03FF. después del siguiente comando emergente A, el puntero nuevamente asumió el valor 0x01FF y continuó aumentando hacia 0x03FF.
03 EF 03 F0 03 F1 03 F2 03 F3 03 F4 03 F5 03 F6 03 F7 03 F8 03 F9 03 FA 03 FB 03 FC 03 FD 03 FE 03 FF 01 FF 02 00 02 01 02 02 02 03 02 04 02 05 02 06 02 07 02 08 02 09 02 0A 02 0B 02 0C 02 0D 02 0E 02 0F 02 10 02 11 02 12 02 13 02 14 02 15
En la dirección opuesta, con un número excesivo de instrucciones pop (w), la pila también se cierra en un anillo de 513 bytes.
La pila en STM8S103F3 es lineal hasta que rompa uno de sus bordes 0x01FF o 0x03FF.
Tan pronto como rompas uno de los límites, la pila se convierte en un anillo de 513 bytes.
No importa en qué parte del anillo (en las direcciones 0x01FF ... 0x03FF) habrá una parte superior / inferior de la pila, podemos poner un número ilimitado de bytes en la pila, pero no podemos extraer más de 513 bytes no dañados (el más reciente).
Ahora que la pila está localizada en 0x01FF ... 0x03FF, es hora de romper ese rango al inicializar el puntero SP.
En el paso 1 del primer procedimiento, reemplazamos el valor 0x03FF para inicializar el puntero SP con el valor 0x01FE.
Observamos cómo la pila de la dirección 0x01FE fue en la dirección de direcciones decrecientes.
01 FE 01 FD 01 FC 01 FB 01 FA 01 F9 01 F8 01 F7 01 F6 01 F5 01 F4 01 F3 01 F2 01 F1 01 F0 01 EF 01 EE 01 ED 01 EC 01 EB 01 EA 01 E9 01 E8 01 E7 01 E6 01 E5 01 E4 01 E3 01 E2 01 E1 01 E0 01 DF 01 DE 01 DD 01 DC 01 DB 01 DA 01 D9 01 D8 01 D7
Habiendo alcanzado la dirección 0x0000, la pila salió de la memoria RAM y entró en las celdas de memoria FLASH inaccesibles para el STM8S103F3.
00 16 00 15 00 14 00 13 00 12 00 11 00 10 00 0F 00 0E 00 0D 00 0C 00 0B 00 0A 00 09 00 08 00 07 00 06 00 05 00 04 00 03 00 02 00 01 00 00 FF FF FF FE FF FD FF FC FF FB FF FA FF F9 FF F8 FF F7 FF F6 FF F5 FF F4 FF F3 FF F2 FF F1 FF F0 FF EF
No se puede hablar de llamadas de subrutina o interrupciones después de que el puntero sale de la memoria RAM. Es cierto que en algún lugar de las profundidades de la pila los datos más "antiguos" aún permanecían, y tuvimos la suerte de guardarlos en la memoria RAM.
Ahora intentemos recuperar datos de la pila con la inicialización "prohibida" (fuera del rango 0x01FF ... 0x03FF) del puntero SP.
Comencemos con direcciones fuera de RAM. En el paso 1 del segundo procedimiento, reemplazamos el valor 0x03FF de inicialización del puntero SP por el valor 0xFFF8.
Observamos cómo la pila entró en la memoria RAM.
FF E9 FF EA FF EB FF EC FF ED FF EE FF EF FF F0 FF F1 FF F2 FF F3 FF F4 FF F5 FF F6 FF F7 FF F8 FF F9 FF FA FF FB FF FC FF FD FF FE FF FF 00 00 00 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08 00 09 00 0A 00 0B 00 0C 00 0D 00 0E 00 0F 00 10
Cruzando el borde inferior 0x01FF, la pila entró en su territorio.
01 E9 01 EA 01 EB 01 EC 01 ED 01 EE 01 EF 01 F0 01 F1 01 F2 01 F3 01 F4 01 F5 01 F6 01 F7 01 F8 01 F9 01 FA 01 FB 01 FC 01 FD 01 FE 01 FF 02 00 02 01 02 02 02 03 02 04 02 05 02 06 02 07 02 08 02 09 02 0A 02 0B 02 0C 02 0D 02 0E 02 0F 02 10
Habiendo alcanzado la dirección 0x03FF, la pila se cerró en un anillo.
03 E9 03 EA 03 EB 03 EC 03 ED 03 EE 03 EF 03 F0 03 F1 03 F2 03 F3 03 F4 03 F5 03 F6 03 F7 03 F8 03 F9 03 FA 03 FB 03 FC 03 FD 03 FE 03 FF 01 FF 02 00 02 01 02 02 02 03 02 04 02 05 02 06 02 07 02 08 02 09 02 0A 02 0B 02 0C 02 0D 02 0E 02 0F
Conclusiones:
La pila en STM8S103F3 solo puede realizar sus tareas dentro del rango 0x01FF ... 0x03FF.
Para obtener la mayor profundidad lineal, el puntero de la pila SP en STM8S103F3 debe inicializarse a 0x03FF.
La pila en STM8S103F3 es lineal hasta que rompa el límite inferior 0x01FF.
Tan pronto como rompas el límite inferior, la pila se convierte en un anillo de 513 bytes.