Косвенно-регистровый режим со смещением Адрес операнда образуется путем сложения
регистра и адресного поля команды. Этот режим наиболее богат возможностями и,
в зависимости от стиля использования, имеет много других названий, например базовая
адресация или индексная адресация. Адресное
поле необязательно содержит полноценный адрес и может быть укороченным. Команды
id/st процессора SPARC, используемые в примере 2.2, реализуют косвенно-регистровую
адресацию с 13-разрядным смещением. Возможные варианты использования этого
режима адресации многочисленны. Например, если смещение представляет собой абсолютный
адрес начала массива, а в регистре хранится индекс, этот режим может использоваться
для индексации массива. В этом случае смещение должно представлять собой полноценный
адрес. 3 другом случае, в регистре может храниться указатель на структуру
Данных, а смещение может означать смещение конкретного поля относительно начала
структуры. Еще один вариант -- регистр хранит указатель на стековый кадр или блок
параметров процедуры, а смещение -адрес локальной переменной в этом кадре или
определенного параметра. В этих случаях можно использовать (и обычно используется)
укороченное смещение. Стековый кадр
Стековый кадр является стандартным способом выделения памяти под локальные переменные
в алголоподобных процедурных языках (С, C++, Pascal) и других языках, допускающих
рекурсивные вызовы. Семантика рекурсивного вызова в алголоподобных языках
требует, чтобы каждая из рекурсивно вызванных процедур имела собственную копию
локальных переменных. В SPARC это достигается сдвигом регистрового окна по регистровому
файлу (рис. 2.9), но большинство других процессоров такой роскоши лишены и вынуждены
выделять память под локальные переменные в стеке, размещаемом в ОЗУ. 
Рис. 2.9. Регистровый стек процессора SPARC Для этого вызванная
процедура уменьшает (если стек растет вниз) указатель стека на количество байтов,
достаточное, чтобы разместить переменные. Адресация этих переменных у некоторых
процессоров (например, у PDP-11) происходит относительно указателя стека, а у
большинства — например, у МС680хО и VAX, с большим количеством регистров или у
х86, указатель стека которого нельзя использовать для адресации со смещением —
для этой цели выделяется отдельный регистр (рис. 2.10, пример 2.4).
Пример 2.4. Формирование, использование и уничтожение стекового
кадра. Код на языке С и результат его обработки GNU С 2.7.2.1 (комментарии автора)
#include <stdio.h> # include <strings.h>
/* Фрагмент примитивной реализации сервера SMTP (RFC822) */ int parse_line(FILE
* socket) { /* Согласно RFC822, команда имеет длину не более 4 байт,а
вся строка — не более 255 байт V char cmd[5], args [255]; fscanf (socket, "%s
%s\n", cmd, args); if (stricmpfcmd, "HELO")==0) { fprintf
(socket, "200 Hello, %s, glad to meet you\n", args); return 200;
) /* etc */ fprintf (socket, "500 Unknown command %s\n", cmd);
return 500; .file "sample" gcc2_compiled. : _ gnu_compiled_c : .text
LCO: .ascii "%s %s\12\0" LCI: .ascii "HELCAO" LC2:
.ascii "200 Hello, %s, glad to meet you\12\0" LC3: .ascii "500
Unknown command %s\12\0" .align 2, 0x90 .globl _parse_line _parse_line:
; x86 имеет для этой цели специальную команду enter, но она может ; формировать
кадры размером не более 255 байт. В данном случае кадр ; имеет больший размер,
и его необходимо формировать вручную. pushl %ebp ; Сохраняем указатель кадра
; вызвавшей нас подпрограммы. movl %esp, %ebp ; Формируем указатель нашего
кадра subl $264,%esp ; И сам кадр. ; Конец пролога функции leal -264 (%ebp)
, %еах ; Помещаем в стек указатель на args • pushl %eax leal -8 (%ebp) , %еах
; ... на cmd pushl %eax pushl $LCO ; на строковую константу ; Наши
собственные параметры тоже адресуются относительно кадра. movl 8(%ebp),%eax ;
Параметр socket мы тоже проталкиваем pushl %eax ; в стек call fscanf ; Вызов
(параметры в стеке) addl $16,%esp ; очищаем стек ; в языке С переменное количество
параметров, поэтому вычищать их из ; стека должна вызывающая процедура. Вызываемая
просто не знает, ; СКОЛЬКО ИХ бЫЛО. pushl $LC1 leal -8(%ebp),%eax
pushl %eax call _stricmp addl $8,%esp movl %eax,%eax ; выключенная
оптимизация в действии :) ; А ведь недалеки времена, когда компиляторы только
такое и умели ; генерировать. testl %eax,%еах jne L14 leal -264(%ebp),%еах
pushl %eax pushl $LC2 movl 8(%ebp),%eax pushl %eax call _fprintf
addl $12,%esp ; Обратите внимание, что компилятор не стал генерировать второй
эпилог ; функции на втором операторе return. movl $200,%eax jmp L13
» Выравнивание потенциальных точек перехода на границу слова полезно: » процессор
не будет тратить дополнительный цикл шины на чтение » невыровненной команды. Для
выравнивания используется команда NOP ; (код операции 0x90). align 2,0x90
L14: leal -8(%ebp),%еах Pushl %eax Pushl $LC3 movl 8(%ebp),%eax
pushl %eax call _fprintf addl ?12,%esp movl $500,%eax jmp L13
.align 2,0x90 L13: ; Команда leave совершает действия, обратные прологу функции:
; Она эквивалентна командам: move %ebp, %esp; pop %ebp. ; Размер кадра явным образом
не указывается, поэтому ограничений ; на этот размер в данном случае нет.
leave ret 
Рис. 2.10. Стековый кадр Примечание
Обратите внимание, что программа из примера 2.4 содержит серьезнейшую ошибку.
В комментариях сказано, что команда обязана иметь длину не более 4 байт, а вся
строка вместе с аргументами не более 255. Если программа-клиент на другом конце
сокета (сетевого соединения) соответствует RFC822 [RFC822], так оно и будет. Но
если программа требованиям этого документа не соответствует, нас ждет беда: нам
могут предложить более длинную команду и/или более длинную строку. Последствия,
к которым это может привести, будут более подробно разбираться в главе
12. Но вернемся к стековым кадрам.
Стековые кадры в системе команд SPARC Микропроцессоры
SPARC также не могут обойтись без стекового кадра. Во-первых, не всегда локальные
переменные процедуры помещаются в восьми 32-разрядных локальных регистрах. Именно
такая процедура приведена в примере 2.4. Во-вторых, нередки ситуации, когда в
качестве параметров надо передать по значению структуры, для которых 6 регистров-параметров
тоже не хватит. В-третьих, глубина регистрового файла ограничена и при работе
рекурсивных или просто глубоко вложенных процедур может исчерпаться. В-четвертых,
в многозадачной системе регистровый файл может одновременно использоваться несколькими
задачами. Все эти проблемы решаются при помощи создания стекового кадра [www.sparc.com
v9]. Для этой цели используются регистры Isp (о6) и %fp (i6). Команда save
%sp, -96 %sp делает следующее: она складывает первые два операнда, сдвигает стековый
кадр и помещает результат сложения в третий операнд. Благодаря такому порядку
исполнения отдельных операций, старый %sp становится %fp, а результат сложения
помещается уже в новый %sp. Самую важную роль стековые кадры играют при обработке
переполнений регистрового файла. Регистровый файл SPARC представляет собой кольцевой
буфер, доступность отдельных участков которого описывается привилегированными
регистрами CANSAVE и CANRESTORE. Окна, находящиеся между значениями этих двух
регистров, доступны текущей программе (рис. 2.11). На рисунке показано состояние
регистрового файла, в котором текущий процесс может восстановить один стековый
кадр (CANRESTORE=1) и сохранить три (CANSAVE=3). Регистр OTHERWIN указывает количество
регистровых окон, занятых другим процессом. Регистровое окно w4 на рисунке (обозначенное
как перекрытие) занято лишь частично. Текущее окно, частично занятое окно и участки
регистрового файла, описанные перечисленными регистрами, в сумме должны составлять
весь регистровый файл, так чтобы соблюдалось отношение CANSAVE + CANRESTORE +
OTHERWIN = NWINDOWS - 2, Где NWINDOWS- количество окон (на рисунке регистровый
файл имеет 8 окон, т. е. 128 регистров). 
Рис.
2.11. Регистровый файл SPARC в виде кольцевого буфера. Регистры CANSAVE и CANRESTORE
(цит. по [www.sparc.com v9]) Когда же программа пытается сдвинуть
свое окно за описанные границы (в ситуации, изображенной на рис 2.11 это может
произойти после вызовов четырех вложенных процедур или после возврата из двух
процедур — текущей и соответствующей окну w7), генерируются исключительные состояния
заполнения окна (window fill) и сброса окна (window spill). При этом вызывается
системная процедура, которая освобождает окна из интервала OTHERWIN, сбрасывая
их содержимое в стековые кадры соответствующих процедур и при заполнении восстанавливает
содержимое принадлежащего нам окна из соответствующего кадра. В многозадачной
системе заполнение и сброс окна может произойти в любой момент, поэтому пользовательская
программа всегда должна иметь по стековому кадру на каждое из используемых ею
регистровых окон, а указатель на этот кадр должен всегда лежать в %sp соответствующего
окна. При этом очень важно, чтобы создание стекового кадра и сдвиг регистрового
окна производились одной командой. |