Стек (stack кипа) - область старших адресов оперативной памяти RAM, которая дополнительно к обычной адресации с помощью специальных команд имеет доступ по принципу стопки бумаг - последним положил первым забрал.
Стек используется для хранения адресов возврата из подпрограмм и из программ обработки прерываний, для сохранения содержимого регистов ядра STM8 на время обработки прерывани с восстановлением содержимого при выходе из программы обработки прерывания. Может использоваться для передачи переменных подпрограмме, для организации буфера данных и пр.
Стек имеет ограниченную глубину (напр. STM8S103F3 имеет глубину 513 байт если указатель стека SP инициализировать величиной $03FF) и заполняется от старших адресов к младшим. Переполнение стека никак не контролирутся микроконтроллером и может привести к потере данных если этих данных будет напихано в стек более чем его глубина.
Со стеком тесно связан регист указателя стека SP (Stack Pounter). Регистр всегда хранит адрес первой не занятой стопкой ячейки для бумаги (так называемой вершины/дна стека).
Далее все будет описано применительно к модели STM8S103F3, другие модели могут отличаться объемом памяти RAM и глубиной стека.
При сбросе/включении питания микроконтроллера STM8S103F3 указатель стека SP аппаратно инициализируется величиной $03FF (RAM END). Но инициализации стека не происходит если программно выполнить переход на вектор сброса $8000.
Память RAM у STM8S103F3 располагается в адресах $0000...$03FF. Стек может располагаться в адресах $01FF...$03FF. Чем ближе к границе памяти RAM расположить начальную вершину стека тем больше будет его глубина. Программная инициализация вершины стека:
ldw X, #$03FF ; X <= RAM END ldw SP, X ; SP <= X
При этом стек пустой. Разберемся как он заполняется командами "push A", "push CC", "push #byte", "push lmem", "pushw X", "pushw Y" и как информация извлекается из стека командами "pop A", "pop CC", "pop lmem", "popw X", "popw Y" (команд "pop" на одну меньше, чем команд "push".
--------- $03FE | 0 | --------- $03FF | 0 | --------- SP = $03FF
В указателе стека содержится адрес $03FF (RAM END) самой старшей ячейки оперативной памяти RAM.
push A ; M(SP--) <= A --------- $03FE | 0 | --------- $03FF | A | M($03FF) <= A , SP <= $03FF - 1 = $03FE --------- SP = $03FE
По команде "push A" содержимое регистра A помещается в ячейку памяти RAM адрес которой находится в указателе стека SP (на данном этапе это $03FF) далее содержимое указателя стека SP уменьшается на единицу и указывает на не занятую стеком ячейку памяти RAM с адресом $03FE.
push CC ; M(SP--) <= CC --------- $03FD | 0 | --------- $03FE | CC | M($03FE) <= CC, SP <= $03FE - 1 = $03FD --------- $03FF | A | --------- SP = $03FD
По команде "push CC" содержимое регистра состояния CC помещается в ячейку памяти RAM адрес которой находится в указателе стека SP (на данном этапе это $03FE) далее содержимое указателя стека SP уменьшается на единицу и указывает на не занятую стеком ячейку памяти RAM с адресом $03FD.
push #byte ; M(SP--) <= #byte --------- $03FC | 0 | --------- $03FD | #byte | M($03FE) <= #byte, SP <= $03FD - 1 = $03FC --------- $03FE | CC | --------- $03FF | A | --------- SP = $03FC
По команде "push #byte" 8-разрядная константа(число) #byte помещается в ячейку памяти RAM адрес которой находится в указателе стека SP (на данном этапе это $03FD) далее содержимое указателя стека SP уменьшается на единицу и указывает на не занятую стеком ячейку памяти RAM с адресом $03FC.
push lmem ; M(SP--) <= lmem --------- $03FB | 0 | --------- $03FC | lmem | M($03FE) <= lmem, SP <= $03FC - 1 = $03FB --------- $03FD | #byte | --------- $03FE | CC | --------- $03FF | A | --------- SP = $03FB
По команде "push lmem" содержимое ячейки памяти с адресом lmem помещается в ячейку памяти RAM адрес которой находится в указателе стека SP (на данном этапе это $03FC) далее содержимое указателя стека SP уменьшается на единицу и указывает на не занятую стеком ячейку памяти RAM с адресом $03FB.
pushw X ; M(SP--) <= XL M(SP--) <= XH --------- $03F9 | 0 | --------- $03FA | XH | M($03FE) <= XH, SP <= $03FA - 1 = $03F9 --------- $03FB | XL | M($03FE) <= XL, SP <= $03FB - 1 = $03FA --------- $03FC | lmem | --------- $03FD | #byte | --------- $03FE | CC | --------- $03FF | A | --------- SP = $03F9
По команде "pushw X" содержимое младшего байта индесного регистра X помещается в ячейку памяти RAM адрес которой находится в указателе стека SP (на данном этапе это $03FB) далее содержимое указателя стека SP уменьшается на единицу и указывает на не занятую стеком ячейку памяти RAM с адресом $03FA. Содержимое старшего байта индесного регистра X помещается в ячейку памяти RAM адрес которой находится в указателе стека SP (на данном этапе это $03FA) далее содержимое указателя стека SP уменьшается на единицу и указывает на не занятую стеком ячейку памяти RAM с адресом $03F9.
pushw Y ; M(SP--) <= YL M(SP--) <= YH --------- $03F7 | 0 | --------- $03F8 | YH | M($03F8) <= YH, SP <= $03F8 - 1 = $03F7 --------- $03F9 | YL | M($03F9) <= YL, SP <= $03F9 - 1 = $03F8 --------- $03FA | XH | --------- $03FB | XL | --------- $03FC | lmem | --------- $03FD | #byte | --------- $03FE | CC | --------- $03FF | A | --------- SP = $03F7
По команде "pushw Y" содержимое младшего байта индесного регистра Y помещается в ячейку памяти RAM адрес которой находится в указателе стека SP (на данном этапе это $03F9) далее содержимое указателя стека SP уменьшается на единицу и указывает на не занятую стеком ячейку памяти RAM с адресом $03F8. Содержимое старшего байта индесного регистра X помещается в ячейку памяти RAM адрес которой находится в указателе стека SP (на данном этапе это $03F8) далее содержимое указателя стека SP уменьшается на единицу и указывает на не занятую стеком ячейку памяти RAM с адресом $03F7.
Последовательность команд "push" "pushw" сохранения в стек может быть произвольной. Важно соблюдать обратную последовательность комманд по извлечению информации из стека. Но тут есть ньюансы. Если сохранять в стек аккумулятор или регистр состояния, то скорее всего будет необходимость извлекать из стека также в аккумулятор и регистр состояния. Если сохранять в стек содержимое индексных регистров X или Y, то в зависимости от необходимости, можно восстановить из стека соответствующее содержимое в регистов X и Y или наоборот Y и X. Если сохранить в стек однократно или последовательно некоторое количество раз содержимое регистра UART1_DR, то необходимо столько же выполнить извлечение из стека но уже не в регистр UART1_DR, а напр. аккумулятор. Главное соблюдать количество команд "push" "pushw" после сохранения конкрентоного содержимого в стеке и количество команд "pop" "popw" до извлечения этого же содержимого из стека.
popw Y ; YH <= M(++SP) YL <= M(++SP) --------- $03F7 | 0 | --------- $03F8 | YH | SP <= $03F7 + 1 = $03F8, YH <= M($03F8) --------- $03F9 | YL | SP <= $03F8 + 1 = $03F9, YL <= M($03F9) --------- $03FA | XH | --------- $03FB | XL | --------- $03FC | lmem | --------- $03FD | #byte | --------- $03FE | CC | --------- $03FF | A | --------- SP = $03F9
По команде "popw Y" сначала содержимое указателя SP увеличивается на единицу далее содержимое ячейки памяти RAM адрес которой находится в указателе SP (на данном этапе $03F8) копируется в старший байт индексного регистра Y. Потом содержимое указателя SP увеличивается на единицу и следом содержимое ячейки памяти RAM адрес которой находится в указателе SP (на данном этапе $03F9) копируется в младший байт индексного регистра Y. С этого момента ячейки с адресами $03F8 и $03F9 в памяти RAM считаются свободными (не занятыми).
popw X ; XH <= M(++SP) XL <= M(++SP) --------- $03F7 | 0 | --------- $03F8 | YH | --------- $03F9 | YL | --------- $03FA | XH | SP <= $03F9 + 1 = $03FA, YH <= M($03FA) --------- $03FB | XL | SP <= $03FA + 1 = $03FB, YL <= M($03FB) --------- $03FC | lmem | --------- $03FD | #byte | --------- $03FE | CC | --------- $03FF | A | --------- SP = $03FB
По команде "popw Y" сначала содержимое указателя SP увеличивается на единицу далее содержимое ячейки памяти RAM адрес которой находится в указателе SP (на данном этапе $03FA) копируется в старший байт индексного регистра X. Потом содержимое указателя SP увеличивается на единицу и следом содержимое ячейки памяти RAM адрес которой находится в указателе SP (на данном этапе $03FB) копируется в младший байт индексного регистра X. С этого момента ячейки с адресами $03FA и $03FB в памяти RAM считаются свободными (не занятыми).
pop lmem ; lmem <= M(++SP) --------- $03F7 | 0 | --------- $03F8 | YH | --------- $03F9 | YL | --------- $03FA | XH | --------- $03FB | XL | --------- $03FC | lmem | SP <= $03FB + 1 = $03FC, lmem <= M($03FC) --------- $03FD | #byte | --------- $03FE | CC | --------- $03FF | A | --------- SP = $03FC
По команде "pop lmem" сначала содержимое указателя SP увеличивается на единицу далее содержимое ячейки памяти RAM адрес которой находится в указателе SP (на данном этапе $03FC) копируется в ячейку памяти с адресом lmem. С этого момента ячейка с адресом $03FC в памяти RAM считается свободной (не занятой).
pop lmem ; lmem <= M(++SP) --------- $03F7 | 0 | --------- $03F8 | YH | --------- $03F9 | YL | --------- $03FA | XH | --------- $03FB | XL | --------- $03FC | lmem | --------- $03FD | #byte | SP <= $03FC + 1 = $03FD, lmem <= M($03FD) --------- $03FE | CC | --------- $03FF | A | --------- SP = $03FD
Не существует команды которая восстанавливает из стека обратно в константу(число). Но если число было сохранено в стек его надо извлечь. Выбор команды извлечения "pop A", "pop lmem" (маловероятно но не исключено "pop СС") зависит от назначения числа. По команде "pop lmem" сначала содержимое указателя SP увеличивается на единицу далее содержимое ячейки памяти RAM адрес которой находится в указателе SP (на данном этапе $03FD) копируется в ячейку памяти с адресом lmem. С этого момента ячейка с адресом $03FD в памяти RAM считается свободной (не занятой).
pop CC ; CC <= M(++SP) --------- $03F7 | 0 | --------- $03F8 | YH | --------- $03F9 | YL | --------- $03FA | XH | --------- $03FB | XL | --------- $03FC | lmem | --------- $03FD | #byte | --------- $03FE | CC | SP <= $03FD + 1 = $03FE, CC <= M($03FE) --------- $03FF | A | --------- SP = $03FE
По команде "pop CC" сначала содержимое указателя SP увеличивается на единицу далее содержимое ячейки памяти RAM адрес которой находится в указателе SP (на данном этапе $03FE) копируется в регистр состояния CC. С этого момента ячейка с адресом $03FE в памяти RAM считается свободной (не занятой).
pop A ; A <= M(++SP) --------- $03F7 | 0 | --------- $03F8 | YH | --------- $03F9 | YL | --------- $03FA | XH | --------- $03FB | XL | --------- $03FC | lmem | --------- $03FD | #byte | --------- $03FE | CC | --------- $03FF | A | SP <= $03FE + 1 = $03FF, A <= M($03FF) --------- SP = $03FF
По команде "pop A" сначала содержимое указателя SP увеличивается на единицу далее содержимое ячейки памяти RAM адрес которой находится в указателе SP (на данном этапе $03FE) копируется в аккумулятор A. С этого момента ячейка с адресом $03FF в памяти RAM считается свободной (не занятой). Теперь указатель SP опять содержит адрес самой старшей ячейки памяти RAM.
Стек используется для хранения адресов возврата из подпрограмм. Рассмотрим что происходит при вызове подпрограммы и возврате из подпрограммы
call lmem ; M(SP--)<=PCL M(SP--)<-PCH PC<=lmem ; next comand lmem: ; some code ret ; PCH <= M(++SP) PCL <= M(++SP)
Командой "call lmem" в стек сохраняется адрес следующей команды, следом в счетчик команд копируется адрес вызова подпрограммы lmem. Командой "ret" из стека в счетчик команд восстанавливается адрес следующей за "call lmem" команды. Байт PCE счетчика команд PC не сохраняется в стек командой "call lmem" и не восстанавливается из стека командой "ret" поэтому команды "call lmem" и "ret" действуют только в пределах своей секции памяти $xx0000...$xxFFFFF.
callr ssof ; M(SP--)<=PCL M(SP--)<-PCH PC<=PC+ssof ; next comand lmem: ; some code ret ; PCH <= M(++SP) PCL <= M(++SP)
Командой "callr ssof" в стек сохраняется адрес следующей команды, следом в счетчик команд копируется адрес вызова подпрограммы PC+ssof. Командой "ret" из стека в счетчик команд восстанавливается адрес следующей за "callr ssof" команды. Команда "callr ssof" действует в пределах страницы памяти (-128...+127).
callf emem ; M(SP--)<=PCL M(SP--)<-PCH M(SP--)<-PCE PC<=emem ; next comand lmem: ; some code retf ; PCE <= M(++SP) PCH <= M(++SP) PCL <= M(++SP)
Командой "callf emem" в стек сохраняется адрес следующей команды, следом в счетчик команд копируется адрес вызова подпрограммы emem. Командой "retf" из стека в счетчик команд восстанавливается адрес следующей за "callf emem" команды. Команда "callf emem" и "retf" действуют в пределах всего адресного пространства STM8.
Стек также используется для хранения адресов возврата из программ обработки прерываний. Рассмотрим что происходит со стеком при вызове прерывания и возврате из прерывания. Условимся, что в указателе SP находится адрес $03FF RAM END. При срабатывании какого-либа прерывания заканчивается выполнение текущей команды, в стек сохраняется содержимое регистров PC(адрес следующей команды), Y, X, A, CC в следующем порядке:
pop A ; A <= M(++SP) --------- $03F6 | 0 | --------- $03F7 | CC | M($03F7) <= CC, SP <= $03F7 - 1 = $03F6 --------- $03F8 | A | M($03F8) <= A, SP <= $03F8 - 1 = $03F7 --------- $03F9 | YH | M($03F9) <= XH, SP <= $03F9 - 1 = $03F8 --------- $03FA | XL | M($03FA) <= XL, SP <= $03FA - 1 = $03F9 --------- $03FB | YH | M($03FB) <= YH, SP <= $03FB - 1 = $03FA --------- $03FC | YL | M($03FC) <= YL, SP <= $03FC - 1 = $03FB --------- $03FD | PCE | M($03FD) <= PCE, SP <= $03FD - 1 = $03FC --------- $03FE | PCH | M($03FE) <= PCH, SP <= $03FE - 1 = $03FD --------- $03FF | PCL | M($03FF) <= PCL, SP <= $03FF - 1 = $03FE --------- SP = $03F6
В разряды I0 и I1 регистра статуса CC копируется информация о приоритете прерывания из соответствующего регистра ITC_SPR ($7F70...$7F7F). В счетчик программ PC загружается вектор сработавшего прерывания. После чего командой "INT dst" происходит переход к программе обработки прерывания.
По завершению программы обработки прерывания командой "iret" из стека восстанавливается содержимое регистров CC, A, X, Y, PC в следующем порядке:
iret ; Interrupt Return ; CC<=M(++SP) A<=M(++SP) XH<=M(++SP) ; XL<=M(++SP) YH<=M(++SP) YL<=M(++SP) ; PCE<=M(++SP) PCH<=M(++SP) PCL<=M(++SP) --------- $03F6 | 0 | --------- $03F7 | CC | SP <= $03F6 + 1 = $03F7, CC <= M($03F7) --------- $03F8 | A | SP <= $03F7 + 1 = $03F8, A <= M($03F8) --------- $03F9 | YH | SP <= $03F8 + 1 = $03F9, YH <= M($03F9) --------- $03FA | YL | SP <= $03F9 + 1 = $03FA, YL <= M($03FA) --------- $03FB | YH | SP <= $03FA + 1 = $03FB, YH <= M($03FB) --------- $03FC | YL | SP <= $03FB + 1 = $03FC, YL <= M($03FC) --------- $03FD | PCE | SP <= $03FC + 1 = $03FD, PCE <= M($03FD) --------- $03FE | PCH | SP <= $03FD + 1 = $03FE, PCH <= M($03FE) --------- $03FF | PCL | SP <= $03FE + 1 = $03FF, PCL <= M($03FF) --------- SP = $03FF
Поcле восстановления младшего байта счетчика команд PC продолжится выполнение основной программы.
Команды в которых участвует указатель стека SP:
00 XX NEG (soff,SP) ; Negate (Logical 2's complement) M(soff+SP) <= -M(soff+SP) 03 XX CPL (soff,SP) ; Logical 1's complement M(soff+SP) <= /M(soff+SP) 04 XX SRL (soff,SP) ; Shift Right Logical M(soff+SP).{0->#7->#6->#5->#4->#3->#2->#1->#0->CC.C} 06 XX RRC (soff,SP) ; Rotate Right Logical through Carry M(soff+SP).{CC.C->#7->#6->#5->#4->#3->#2->#1->#0->CC.C} 07 XX SRA (soff,SP) ; Shift Right Arithmetic M(soff+SP).{#7->#6->#5->#4->#3->#2->#1->#0->CC.C} 08 XX SLA (soff,SP) ; Shift Left Arithmetic M(soff+SP).{CC.C<-#7<-#6<-#5<-#4<-#3<-#2<-#1<-#0<-0} 08 XX SLL (soff,SP) ; Shift Left Logical M(soff+SP).{CC.C<-#7<-#6<-#5<-#4<-#3<-#2<-#1<-#0<-0} 09 XX RLC (soff,SP) ; Rotate Left Logical through Carry M(soff+SP).{CC.C<-#7<-#6<-#5<-#4<-#3<-#2<-#1<-#0<-CC.C} 0A XX DEC (soff,SP) ; Decrement M(soff+SP) <= M(soff+SP) - 1 0C XX INC (soff,SP) ; Increment M(soff+SP) <= M(soff+SP) + 1 0D XX TNZ (soff,SP) ; Test for Negative or Zero {CC.N, CC.Z} <= Test{ M(soff+SP } 0E XX SWAP (soff,SP) ; Swap Nibbles M(soff+SP).low <-> M(soff+SP).high 0F XX CLR (soff,SP) ; Clear M(soff+SP) <= 0 10 XX SUB A, (soff,SP) ; Subtraction A <= A - M(soff+SP) 11 XX CP A, (soff,SP) ; Compare {CC.N, CC.Z, CC.C} <= Test{ A - M(soff+SP) } 12 XX SBC A, (soff,SP) ; Subtraction A <= A - M(soff+SP) 13 XX CPW X, (soff,SP) ; Compare Word {CC.N, CC.Z, CC.C} <= Test{ X - M(soff+SP).w } 14 XX AND A, (soff,SP) ; Logical AND A <= A AND M(soff+SP) 15 XX BCP A, (soff,SP) ; Logical Bit Compare {CC.N, CC.Z} <= Test{ A AND M(soff,SP) } 17 XX LDW (soff,SP), Y ; Load Word M(soff+SP) <= Y 18 XX XOR A, (soff,SP) ; Logical Exclusive OR A <= A XOR M(soff+SP) 19 XX ADC A, (soff,SP) ; Addition with Carry A <= A + M(soff+SP) + CC.C 1A XX OR A, (soff,SP) ; Logical OR A <= A OR M(soff+SP) 1B XX ADD A, (soff,SP) ; Addition A <= A + M(soff+SP) 1E XX LDW X, (soff,SP) ; Load Word X <= M(soff+SP) 32 MS LS POP lmem ; Pop from stack SP++ M(lmem) <= M(SP) 4B XX PUSH #byte ; Push onto the Stack M(SP--) <= #byte 52 XX SUB SP, #byte ; Subtraction SP <= SP - #byte 5B XX ADDW SP, #byte ; Addition SP <= SP + #byte 6B XX LD (soff,SP), A ; Load A <= M(soff+SP) 7B XX LD A, (soff,SP) ; Load A <= M(soff+SP) 80 IRET ; Interrupt Return CC<=M(++SP) A<=M(++SP) XH<=M(++SP) ; XL<=M(++SP) YH<=M(++SP) YL<=M(++SP) ; PCE<=M(++SP) PCH<=M(++SP) PCL<=M(++SP) 84 POP A ; Pop from stack A <= M(++SP) preincremrnt 85 POPW X ; Pop Word from stack XH <= M(++SP) XL <= M(++SP) 90 85 POPW Y ; Pop Word from stack YH <= M(++SP) YL <= M(++SP) 86 POP CC ; Pop from stack CC <= M(++SP) preincremrnt 87 RETF ; Far Return from subroutine PCE<=M(++SP) PCH<=M(++SP) PCL<=M(++SP) 88 PUSH A ; Push into the Stack M(SP--) <= A postdecremrnt 89 PUSHW X ; Push Word onto the Stack M(SP--) <= XL M(SP--) <= XH 90 89 PUSHW Y ; Push Word onto the Stack M(SP--) <= YL M(SP--) <= YH 8A PUSH CC ; Push into the Stack M(SP--) <= CC postdecremrnt 8D ExtB MS LS CALLF emem ; Call Far Subroutine M(SP--)<=PCL M(SP--)<-PCH M(SP--)<-PCE PC<=emem 92 8D MS LS CALLF [lptr.e] ; Call Far Subroutine M(SP--)<=PCL M(SP--)<-PCH M(SP--)<-PCE PC<=M(lptr).e 94 LDW SP, X ; Load Word SP <= X 90 94 LDW SP, Y ; Load Word SP <= Y 96 LDW X, SP ; Load Word X <= SP 90 96 LDW Y, SP ; Load Word Y <= SP AD XX CALLR ssoff ; CALL Subroutine Relative M(SP--)<=PCL M(SP--)<-PCH PC<=PC+ssoff CD MS LS CALL lmem ; Call Subroutine (Absolute) M(SP--)<=PCL M(SP--)<-PCH PC<=lmem 72 CD MS LS CALL [lptr.w] ; CALL Subroutine (Absolute) M(SP--)<=PCL M(SP--)<-PCH PC<=M(lptr).w 92 CD XX CALL [sptr.w] ; CALL Subroutine (Absolute) M(SP--)<=PCL M(SP--)<-PCH PC<=M(sptr).w DD MS LS CALL (loff,X) ; CALL Subroutine (Absolute) M(SP--)<=PCL M(SP--)<-PCH PC<=M(loff+X).w 72 DD MS LS CALL ([lptr.w],X) ; CALL Subroutine (Absolute) M(SP--)<=PCL M(SP--)<-PCH PC<=M(M(lptr).w+X).w 90 DD MS LS CALL (loff,Y) ; CALL Subroutine (Absolute) M(SP--)<=PCL M(SP--)<-PCH PC<=M(loff+Y).w 91 DD XX CALL ([sptr.w],Y) ; CALL Subroutine (Absolute) M(SP--)<=PCL M(SP--)<-PCH PC<=M(M(sptr).w+Y).w 92 DD XX CALL ([sptr.w],X) ; CALL Subroutine (Absolute) M(SP--)<=PCL M(SP--)<-PCH PC<=M(M(sptr).w+X).w 72 F2 XX SUBW Y, (soff.w,SP).w ; Subtraction Y <= Y - M(soff.w+SP).w ED XX CALL (soff,X) ; CALL Subroutine (Absolute) M(SP--)<=PCL M(SP--)<-PCH PC<=M(soff+X).w 90 ED XX CALL (soff,Y) ; CALL Subroutine (Absolute) M(SP--)<=PCL M(SP--)<-PCH PC<=M(soff+Y).w 72 F0 XX SUBW X, (soff,SP) ; Subtraction Word X <= X - M(soff+SP).w 72 F9 XX ADDW Y, (soff,SP) ; Addition Word Y <= Y + M(soff+SP) 72 FB XX ADDW X, (soff,SP) ; Addition Word X <= X + M(soff+SP) FD CALL (X) ; CALL Subroutine (Absolute) M(SP--)<=PCL M(SP--)<-PCH PC<=M(X).w 90 FD CALL (Y) ; CALL Subroutine (Absolute) M(SP--)<=PCL M(SP--)<-PCH PC<=M(Y).w
Теперь рассмотрим где может располагаться стек.
--------- $03FF | | RAM END верхняя граница стека --------- $03F7 | | --------- ... | | --------- $0200 | | --------- $01FF | | нижняя граница стека --------- $01FE | | --------- ... | | --------- $0001 | | --------- $0000 | | RAM START ---------
Аппартно у стека существуют две границы. Верхняя граница совмещена со старшей ячейкой памяти RAM. Нижняя граница расположено примерно посередине области RAM.
Стек может располагаться по обо стороны от нижней границы, но логика его работы будет несколько отличаться.
Рассмотрим расположение стека в области $01FF...#$03FF.
При включении указатель вершины стека SP инициализируется величиной $03FF. При наполнении стека содержимое указателя приблизается к величине $01FF, по достижению которой при следующей команде "push" указатель принимает первоначальное значение $03FF. С этого момента стек начинает повторять свою траекторию, затирая ранее сохраненную информацию. Благодаря тому, что стек замкнулся в кольцо, гарантируется целостность последних 513 байт помещенных в стек вне зависимости от текущего положения вершины стека. При извлечении информации из стека содержимое указателя повторяет траекторию в обратном направлении. После достижения верхней границы $03FF указатель SP принимает содержимое $01FF и так по кольцу.
Несколько иначе стек ведет себя при расположении под нижней границей $01FF.
При наполнении стека содержимое указателя SP также уменьшается, но по достижении адреса $0000 стек проваливается в несуществующую область памяти и перестает сохранять последние помещенные данные. При неконтролируемом извлечении информации содержимое указателя SP увеличивается, проходит нижнюю границу и начинает подчиняться опять "кольцевому режиму".
Машинные коды STM8 и команды ассемблера здесь. Адресное пространство STM8S103F3 тут.