Обычно первое, что вы делаете в функции, это:
push r4
чтобы сохранить текущий указатель кадра в стеке, чтобы вы могли установить новый указатель кадра для функции, которую вы собираетесь вызвать, а затем восстановить старый. push
автоматически уменьшит указатель стека на 2, поэтому, когда вы выполните:
mov r1, r4
адрес, помещенный в r4
, будет выше значения, которое вы только что поместили в стек ("выше" здесь в том смысле, что стек растет вниз - на самом деле он ниже с точки зрения числовых адресов памяти). Вы хотите, чтобы указатель фрейма на самом деле указывал ниже значения, которое вы только что поместили в стек, поэтому вы увеличиваете его на два, чтобы добиться этого с помощью:
add #2, r4
Поскольку main()
— это первая выполняемая функция, у вас нет существующего указателя кадра для сохранения, поэтому вы видите здесь mov
и add
при отсутствии push
.
Это будет иметь больше смысла, когда вы сделаете реальный вызов функции, и вы увидите все это:
push r4
mov r1, r4
add #2, r4
После того, как вы это сделаете, -2(r4)
будет ссылаться на предыдущее значение указателя фрейма, которое вы только что поместили в стек, и, поскольку вы не добавили два к значению указателя стека, -2(r4)
тоже будет равно этому.
Теперь, когда вы выделяете 16 бит для вашей локальной переменной i
, вам придется вычесть 2
из указателя стека, чтобы освободить место для него, и поэтому адрес i
будет -4(r4)
.
Пример
В качестве примера предположим, что указатель стека содержит 0x200
, а указатель фрейма содержит 0x202
, и затем вы хотите вызвать функцию. Вы начинаете с таким стеком:
r4 --> 0x202 ---------------------
<empty>
r1 --> 0x200 ---------------------
После того, как вы вернетесь из своей функции, вы захотите восстановить текущее значение указателя кадра, поэтому первое, что вы делаете, это помещаете его в стек, чтобы сохранить. Таким образом, после push r4
значение 0x202
помещается в ячейку памяти 0x200
(т. е. на вершину стека, на которую указывает r1
), а указатель стека уменьшается на 2
, чтобы освободить для него место, поэтому вы получаете:
r4 --> 0x202 ---------------------
<empty>
0x200 ---------------------
0x202
r1 --> 0x1FE ---------------------
Поместив предыдущее значение указателя фрейма в стек, вы теперь хотите установить текущее значение указателя фрейма на базу вашего текущего фрейма стека, поэтому вы начинаете это с перемещения нового указателя стека в r4
, и вы получить:
0x202 ---------------------
<empty>
0x200 ---------------------
0x202
r1 == r4 --> 0x1FE ---------------------
Старое значение указателя кадра — это первое значение в текущем кадре стека, поэтому вы хотите, чтобы r4
указывало перед ним, а не после него. Для этого вы добавляете 2
к r4
, и вы получаете:
0x202 ---------------------
<empty>
r4 --> 0x200 ---------------------
0x202
r1 --> 0x1FE ---------------------
Позиция, в которой вы сейчас находитесь, такова, что r4
указывает на нижнюю часть вашего стекового фрейма, а r1
указывает на его верхнюю часть, где вы и хотите быть. Единственное, что на самом деле находится в вашем текущем кадре стека на данный момент, — это предыдущее значение указателя кадра, которое вы вставили в него в начале функции.
Затем вы уменьшаете указатель стека на 2
, чтобы освободить место для вашей новой локальной переменной, и в итоге вы получаете:
0x202 ---------------------
<empty>
r4 --> 0x200 ---------------------
0x202
0x1FE ---------------------
<uninitialized i>
r1 --> 0x1FC ---------------------
и вы можете видеть, что i
хранится в 0x1FC
, то есть -4(r4)
. Вы снова находитесь в позиции, где r4
указывает на нижнюю часть фрейма стека, а r1
указывает на его вершину, но теперь у вас есть два 16-битных значения в текущем фрейме стека, поэтому два указателя находятся на расстоянии 4 байта друг от друга. .
Когда вы будете готовы вернуться в конце своей функции, вы должны add #2, r1
"освободить" память для вашей локальной переменной i
. Это даст вам:
0x202 ---------------------
<empty>
r4 --> 0x200 ---------------------
0x202
r1 --> 0x1FE ---------------------
Затем вы pop r4
вытолкнете последнее значение из стека (теперь это 0x202
, исходное значение указателя кадра), поместите его в r4
и увеличите указатель стека на 2
, чтобы отразить, что это значение было удалено. из стека, который оставит вас в:
r4 --> 0x202 ---------------------
<empty>
r1 --> 0x200 ---------------------
это именно то, с чего вы начали, и вы идеально очистили стек после вызова функции.
Обратите внимание, что это немного упрощено, поскольку, когда вы выполняете вызов функции, счетчик программ также будет помещен в стек, а затем снова извлечен и восстановлен, когда вы вернетесь, и приведенный выше пример не показывает это, но именно там происходит то же самое.
person
Crowman
schedule
15.10.2014