在STM8中破坏堆栈

在为STM8微控制器编写STM8uLoader引导加载程序的过程中,有必要测量堆栈的深度。

让我们问问题:

  • 如果您尝试在堆栈中推送比其深度更多的信息,会发生什么?
  • 如果您尝试从堆栈中提取比放置更多的信息会发生什么?
  • 如果使用超出堆栈边界的地址初始化堆栈指针SP,会发生什么情况?

对于不同的STM8型号,RAM存储器和堆栈深度可能会有所不同。
为了进行研究,选择了STM8S103F3型。

STM8S103F3的文档提供以下数据:
-堆栈深度513字节;
-复位时,SP指针初始化为0x03FF(RAM END);
-堆栈朝地址减少的方向增长。

计算表明,堆栈的下限为:

0x03FF - 513 = 0x01FF 

要打破此界限,您需要在堆栈中压入多于513个字节。

堆栈中的内容对我们不感兴趣。 知道指向SP堆栈的指针的内容就足够了,其中应该包含SP堆栈未占用的下一个RAM单元的地址。
我们将使用任何推入命令(例如,推入A)按顺序放置字节,并在每一步骤之前将SP堆栈指针的高SPH和低SPL字节的内容发送到UART。

程序算法:

1用值0x03FF初始化堆栈指针并配置UART;
2我们正在等待终端程序中的任何字节;
接受3个字节;
4将SP指针的内容发送到UART;
5我们使用push A命令将电池的内容推入堆栈中;
6如果发送周期小于64,请转到步骤4;否则,请转至步骤4。
7如果发送周期为64,请转到步骤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 

我们观察终端程序如何从0x03FF开始依次接受SP指针的内容:

  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 

值达到0x01FF之后(先前计算的堆栈边界)
SP指针再次取值为0x03FF(堆栈关闭成环形)
并开始覆盖最早的数据

  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 

现在让我们看看如果尝试从堆栈中无限制地检索内容,SP指针的内容将如何运行。

程序算法:

1用值0x03FF初始化堆栈指针并配置UART;
2我们正在等待终端程序中的任何字节;
接受3个字节;
4我们使用“ pop A”命令从堆栈中提取内容到电池;
5将SP指针的内容发送到UART;
6如果发送周期小于64,请转到步骤3;否则,请转至步骤3。
7如果发送周期为64,请转到步骤2。

算法的第4项和第5项以及“ push A”命令与“ pop A”命令互换。
尽管我们在执行第一个pop A命令后已经使用值0x03FF初始化了SP指针,但该指针的取值为0x01FF并继续增加到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 

达到值0x03FF。 在下一个弹出A命令之后,指针再次取值为0x01FF,并继续向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 

相反,由于弹出(w)指令过多,堆栈也以513字节的环闭合。

STM8S103F3中的堆栈是线性的,直到打破其边界0x01FF或0x03FF之一为止。

一旦打破其中一个界限,堆栈便成为一个513字节的环。
堆栈顶部/底部位于环中的哪个位置(地址为0x01FF ... 0x03FF)都没有关系,我们可以在堆栈中放置无限个字节,但是我们最多可以提取513个未损坏的字节(最近的字节)。

现在,堆栈已定位在0x01FF ... 0x03FF,是时候在初始化SP指针时打破该范围了。

在第一个过程的步骤1中,我们使用值0x01FE替换了用于初始化SP指针的值0x03FF。

我们观察到地址0x01FE的堆栈如何朝地址减少的方向发展。

  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 

到达地址0x0000后,堆栈退出RAM存储器并进入STM8S103F3无法访问的FLASH存储器单元。

  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 

指针退出RAM存储器后,再也不能谈论子程序调用或中断。 没错,在堆栈最深处仍然保留了最“古老”的数据,这些数据很幸运地保存在RAM内存中。

现在,让我们尝试使用SP指针的“禁止”(超出0x01FF ... 0x03FF范围)初始化从堆栈中检索数据。

让我们从RAM外部的地址开始。 在第二个过程的步骤1中,我们将指针SP初始化的值0x03FF替换为值0xFFF8。

我们观察堆栈如何进入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 

越过下边界0x01FF,堆栈进入其区域。

  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 

到达地址0x03FF后,堆栈呈环形关闭。

  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 

结论:

STM8S103F3中的堆栈只能在0x01FF ... 0x03FF范围内执行其职责。

为了获得最大的线性深度,必须将STM8S103F3中的SP堆栈指针初始化为0x03FF。

STM8S103F3中的堆栈是线性的,直到您打破0x01FF的下限。
一旦打破下限,堆栈就会变成一个513字节的环。

Source: https://habr.com/ru/post/zh-CN417203/


All Articles