Break STM8 stack
In the process of writing the STM8uLoader bootloader for STM8 microcontrollers, it became necessary to measure the stack depth.
Let us ask questions:
The amount of RAM memory and the depth of the stack for different models STM8 may vary.
Model STM8S103F3 was chosen for the study.
The documentation on STM8S103F3 gives the following data:
- stack depth is 513 bytes;
- when reset, the SP pointer is initialized to the value 0x03FF (RAM END);
- the stack grows towards decreasing addresses.
The calculation shows that the lower limit of the stack is:
To break this boundary, you need to put a few more on the stack than 513 bytes.
The contents of the stack itself do not interest us. It is enough to know the contents of the SP stack pointer which should contain the address of the next RAM cell not occupied by the stack.
We will sequentially place bytes with any “push” command (for example, “push A”) and before each step send the contents of the higher SPH and low SPL byte of the SP pointer to the UART.
Algorithm of the procedure:
1 Initialize the stack pointer with the value 0x03FF and configure the UART;
2 We are waiting for any byte from the terminal program;
3 Bytes received;
4 Send the contents of the SP pointer to the UART;
5 We push the contents of the battery with the “push A” command;
6 If the send cycles are less than 64, go to step 4;
7 If the send cycle is 64, go to step 2.
Observe how the terminal program consistently receives the contents of the SP pointer, starting at 0x03FF:
After the value reached 0x01FF (the previously calculated stack boundary),
the SP pointer again took the value 0x03FF (the stack closed in a ring)
and began to overwrite the oldest data.
Now consider how the contents of the SP pointer will behave if you try to unload the contents from the stack indefinitely.
Algorithm of the procedure:
1 Initialize the stack pointer with the value 0x03FF and configure the UART;
2 We are waiting for any byte from the terminal program;
3 Bytes received;
4 Extract the contents of the stack with the command "pop A" in the battery;
5 Sends the contents of the SP pointer to the UART;
6 If the send cycles are less than 64, go to step 3;
7 If the send cycles are 64, go to step 2.
Paragraphs 4 and 5 of the algorithm and the “push A” command are reversed to the “pop A” command.
Despite the fact that we initialized the SP pointer with the value 0x03FF already after the first “pop A” command, the pointer took the value 0x01FF and continued to increase towards 0x03FF.
Reaching the value of 0x03FF. after the next “pop A” command, the pointer again took the value 0x01FF and continued to increase towards 0x03FF.
In the opposite direction, with an excessive number of pop (w) commands, the stack is also closed into a ring with a length of 513 bytes.
The stack in STM8S103F3 is linear, until you break one of its bounds 0x01FF or 0x03FF.
As soon as you break one of the boundaries, the stack becomes a ring with a length of 513 bytes.
No matter where in the ring (in the addresses 0x01FF ... 0x03FF) there will be the top / bottom of the stack, we can put an unlimited number of bytes on the stack, but we can retrieve no more than 513 not damaged bytes (the most "fresh").
Now that the stack is localized in the addresses 0x01FF ... 0x03FF, it is time to break this range when initializing the SP pointer.
In paragraph 1 of the first procedure, we replace the value 0x03FF of the initialization of the SP pointer by the value 0x01FE.
We observe how the stack from the address 0x01FE went in the direction of decreasing addresses.
Reaching the address 0x0000, the stack went out of the RAM memory and penetrated the FLASH “memory” cells inaccessible for the STM8S103F3.
No subroutine and interrupt calls are possible after the pointer leaves the RAM memory. True, somewhere in the depths of the stack, the most “ancient” data still remained, which were fortunate enough to remain in RAM memory.
Now we will try to extract data from the stack with “forbidden” (outside the range 0x01FF ... 0x03FF) initialization of the SP pointer.
Let's start with addresses outside of RAM. In paragraph 1 of the second procedure, we replace the value 0x03FF of the initialization of the SP pointer by the value 0xFFF8.
Observe how the stack went into RAM memory.
Crossing the lower border 0x01FF, the stack entered its territory.
Reaching the address 0x03FF, the stack is closed in a ring.
Conclusions:
The STM8S103F3 stack is capable of performing its duties only within the range of 0x01FF ... 0x03FF.
To get the greatest linear depth of the SP stack pointer in the STM8S103F3, you need to initialize with the value 0x03FF.
The stack in STM8S103F3 is linear until you break the lower bound 0x01FF.
As soon as you break the lower bound, the stack becomes a ring length of 513 bytes.
Let us ask questions:
- What happens if you try to push more information onto the stack than its depth?
- What happens if you try to extract more information from the stack than you put it?
- What will happen if I initialize the SP pointer of the stack address beyond the stack boundaries?
The amount of RAM memory and the depth of the stack for different models STM8 may vary.
Model STM8S103F3 was chosen for the study.
The documentation on STM8S103F3 gives the following data:
- stack depth is 513 bytes;
- when reset, the SP pointer is initialized to the value 0x03FF (RAM END);
- the stack grows towards decreasing addresses.
The calculation shows that the lower limit of the stack is:
0x03FF - 513 = 0x01FF
To break this boundary, you need to put a few more on the stack than 513 bytes.
The contents of the stack itself do not interest us. It is enough to know the contents of the SP stack pointer which should contain the address of the next RAM cell not occupied by the stack.
We will sequentially place bytes with any “push” command (for example, “push A”) and before each step send the contents of the higher SPH and low SPL byte of the SP pointer to the UART.
Algorithm of the procedure:
1 Initialize the stack pointer with the value 0x03FF and configure the UART;
2 We are waiting for any byte from the terminal program;
3 Bytes received;
4 Send the contents of the SP pointer to the UART;
5 We push the contents of the battery with the “push A” command;
6 If the send cycles are less than 64, go to step 4;
7 If the send cycle is 64, go to step 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--) <= A
decw Y
jrne stack_cycle
; выключаем светодиод
bres PB_DDR,#5
bres PB_CR1,#5
jra wait_rx_byte
Observe how the terminal program consistently receives the contents of the SP pointer, starting at 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
After the value reached 0x01FF (the previously calculated stack boundary),
the SP pointer again took the value 0x03FF (the stack closed in a ring)
and began to overwrite the oldest data.
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
Now consider how the contents of the SP pointer will behave if you try to unload the contents from the stack indefinitely.
Algorithm of the procedure:
1 Initialize the stack pointer with the value 0x03FF and configure the UART;
2 We are waiting for any byte from the terminal program;
3 Bytes received;
4 Extract the contents of the stack with the command "pop A" in the battery;
5 Sends the contents of the SP pointer to the UART;
6 If the send cycles are less than 64, go to step 3;
7 If the send cycles are 64, go to step 2.
Paragraphs 4 and 5 of the algorithm and the “push A” command are reversed to the “pop A” command.
Despite the fact that we initialized the SP pointer with the value 0x03FF already after the first “pop A” command, the pointer took the value 0x01FF and continued to increase towards 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
Reaching the value of 0x03FF. after the next “pop A” command, the pointer again took the value 0x01FF and continued to increase towards 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
In the opposite direction, with an excessive number of pop (w) commands, the stack is also closed into a ring with a length of 513 bytes.
The stack in STM8S103F3 is linear, until you break one of its bounds 0x01FF or 0x03FF.
As soon as you break one of the boundaries, the stack becomes a ring with a length of 513 bytes.
No matter where in the ring (in the addresses 0x01FF ... 0x03FF) there will be the top / bottom of the stack, we can put an unlimited number of bytes on the stack, but we can retrieve no more than 513 not damaged bytes (the most "fresh").
Now that the stack is localized in the addresses 0x01FF ... 0x03FF, it is time to break this range when initializing the SP pointer.
In paragraph 1 of the first procedure, we replace the value 0x03FF of the initialization of the SP pointer by the value 0x01FE.
We observe how the stack from the address 0x01FE went in the direction of decreasing addresses.
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
Reaching the address 0x0000, the stack went out of the RAM memory and penetrated the FLASH “memory” cells inaccessible for the 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 subroutine and interrupt calls are possible after the pointer leaves the RAM memory. True, somewhere in the depths of the stack, the most “ancient” data still remained, which were fortunate enough to remain in RAM memory.
Now we will try to extract data from the stack with “forbidden” (outside the range 0x01FF ... 0x03FF) initialization of the SP pointer.
Let's start with addresses outside of RAM. In paragraph 1 of the second procedure, we replace the value 0x03FF of the initialization of the SP pointer by the value 0xFFF8.
Observe how the stack went into RAM memory.
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 000000010002000300040005000600070008000900 0A 00 0B 00 0C 00 0D 00 0E 00 0F 0010
Crossing the lower border 0x01FF, the stack entered its territory.
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
Reaching the address 0x03FF, the stack is closed in a ring.
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
Conclusions:
The STM8S103F3 stack is capable of performing its duties only within the range of 0x01FF ... 0x03FF.
To get the greatest linear depth of the SP stack pointer in the STM8S103F3, you need to initialize with the value 0x03FF.
The stack in STM8S103F3 is linear until you break the lower bound 0x01FF.
As soon as you break the lower bound, the stack becomes a ring length of 513 bytes.