Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Танненбаум Е. Архітектура компютера [pdf]

.pdf
Скачиваний:
103
Добавлен:
02.05.2014
Размер:
5.59 Mб
Скачать

Поток управления

413

шое допустимое число, срабатывает ловушка. Это значит, что поток управления переходит в какую-то фиксированную ячейку памяти, а не продолжается последовательно дальше. В этой фиксированной ячейке находится команда перехода к специальной процедуре (обработчику системных прерываний), которая выполняет какое-либо определенное действие, например печатает сообщение об ошибке. Если результат операции находится в пределах допустимого, ловушка не задействуется.

Важно то, что этот вид прерывания вызывается каким-то исключительным условием, вызванным самой программой и обнаруженным аппаратным обеспечением или микропрограммой. Есть и другой способ определения переполнения. Нужно иметь 1-битный регистр, который принимает значение всякий раз, когда происходит переполнение. Программист, который хочет проверить результат на переполнение, должен включить в программу явную команду «переход в случае установки бита переполнения» после каждой арифметической команды. Но это очень неудобно. А ловушки экономят время и память по сравнению с открытой проверкой под контролем программиста.

Ловушку можно реализовать путем открытой проверки, выполняемой микропрограммой (или аппаратным обеспечением). Если обнаружено переполнение, адрес ловушки загружается в счетчик команд. То, что является ловушкой на одном уровне, может находиться под контролем программы на более низком уровне. Проверка на уровне микропрограммы требует меньше времени, чем проверка под контролем программиста, поскольку она может выполняться одновременно с ка- ким-либодругимдействием. Крометого, такая проверкаэкономитпамять, поскольку она должна присутствовать только в одном месте, например в основном цикле микропрограммы, независимо от того, сколько арифметических команд встречается в основной программе.

Наиболее распространенные условия, которые могут вызывать ловушки, — это переполнение и исчезновение значащих разрядов при операциях с плавающей точкой, переполнение при операциях с целыми числами, нарушения защиты, неопределяемый код операции, переполнение стека, попытка запустить несуществующее устройство ввода-вывода, попытка вызвать слово из ячейки с нечетным адресом и деление на 0.

Прерывания

Прерывания — это изменения в потоке управления, вызванные не самой программой, а чем-либо другим и обычно связанные с процессом ввода-вывода. Например, программа может приказать диску начать передачу информации и заставить диск произвести прерывание, как только передача данных завершится. Как и ловушка, прерывание останавливает работу программы и передает управление программе обработки прерываний, которая выполняет какое-то определенное действие. После завершения этого действия программа обработки прерываний передает управление прерванной программе. Она должна заново начать прерванный процесс в том же самом состоянии, в котором она находилась, когда произошло прерывание. Это значит, что прежнее состояние всех внутренних регистров (то есть состояние, которое было до прерывания) должно быть восстановлено.

4 1 4 Глава 5. Уровень архитектуры команд

Различие между ловушками и прерываниями в следующем: ловушки синхронны с программой, а прерывания асинхронны. Если программа перезапускается много раз с одним и тем же материалом на входе, ловушки каждый раз будут происходить в одном и том же месте, а прерывания могут меняться в зависимости от того, в какой момент человек нажимает возврат каретки. Причина воспроизводимости ловушек и невоспроизводимости прерываний состоит в том, что первые вызываются непосредственно самой программой, а прерывания вызываются программой косвенно.

Чтобы понять, как происходят прерывания, рассмотрим обычный пример: компьютеру нужно вывести на терминал строку символов. Программное обеспечение сначала собирает в буфер все символы, которые нужно вывести на экран, инициализирует глобальную переменную ptr, которая должна указывать на начало буфера, и устанавливает вторую глобальную переменную count, которая равна числу символов, выводимых на экран. Затем программное обеспечение проверяет, готов ли терминал, и если готов, то выводит на экран первый символ (например, используя регистры, которые показаны на рис. 5.30). Начав процесс ввода-вывода, центральный процессор освобождается и может запустить другую программу или сделать что-либо еще.

Через некоторое время символ отображается на экране. Теперь может начаться прерывание. Ниже перечислены основные шаги (в упрощенной форме).

Действия аппаратного обеспечения:

1. Контроллер устройства устанавливает линию прерывания на системной шине.

2.Когда центральный процессор готов к обработке прерывания, он устанавливает символ подтверждения прерывания на шине.

3.Когда контроллер устройства узнает, что сигнал прерывания был подтвержден, он помещает небольшое целое число на информационные линии, чтобы «представиться» (то есть показать, что это за устройство). Это число называется вектором прерываний1.

4.Центральный процессор удаляет вектор прерывания с шины и временно его сохраняет.

5.Центральный процессор помещает в стек счетчик команд и слово состояния программы.

6.Затем центральный процессор определяет местонахождение нового счетчика команд, используя вектор прерывания в качестве индексав таблице в нижней части памяти. Если, например, размер счетчика команд составляет 4 байта, тогда вектор прерываний п соответствует адресу 4п. Новый счетчик команд указывает на начало программы обслуживания прерываний для устройства, вызвавшего прерывание. Часто помимо этого загружается или изменяется слово состояния программы (например, чтобы блокировать дальнейшие прерывания).

1Автор не совсем прав: здесь речь должна идти о номере прерывания. Каждому типу прерывания соответствует свой номер. Термин «вектор прерываний» используется в случае, когда по номеру прерывания находится адрес программы обработки прерывания и этот адрес представляется не одним значением, а несколькими, то есть необходимо проинициализировать более одного регистра. Другими словами, адрес представляется не скалярной величиной, а многомерной, векторной. — Примеч. научн. ред.

Поток управления

415

Действия программного обеспечения:

1.Первое, что делает программа обработки прерываний, — сохраняет все нужные ей регистры таким образом, чтобы их можно было восстановить позднее. Их можно сохранить в стеке или в системной таблице.

2.Каждый вектор прерывания разделяется всеми устройствами данного типа, поэтому в данный момент еще не известно, какой терминал вызвал прерывание. Номер терминала можно узнать, считав значение какого-нибудь регистра.

3.Теперь можно считывать любую другую информацию о прерывании, например коды состояния.

4.Если происходит ошибка ввода-вывода, ее нужно обработать здесь.

5.Глобальные переменные ptr и count обновляются. Первая увеличивается на 1, чтобы показывать на следующий байт, а вторая уменьшается на 1, чтобы указать, что осталось вывести на 1 байт меньше. Если count все еще больше О, значит, еще не все символы выведены наэкран. Тот символ, на который в данный момент указывает ptr, копируется в выходной буферный регистр.

6.В случае необходимости выдается специальный код, который сообщает устройству или контроллеру прерывания, что прерывание обработано.

7.Восстанавливаются все сохраненные регистры.

8.Выполнение команды RETURN FROM INTERRUPT(выход из прерывания): возвращение центрального процессора в то состояние, в котором он находился до прерывания. После этого компьютер продолжает работу с того места, в котором ее приостановил.

С прерываниями связано важное понятие прозрачности. Когда происходит прерывание, производятся какие-либо действия и запускаются какие-либо программы, но когда все закончено, компьютер должен вернуться точно в то же состояние, в котором он находился до прерывания. Программа обработки прерываний, обладающая таким свойством, называется прозрачной.

Если компьютер имеет только одно устройство ввода-вывода, тогда прерывания работают точно так, как мы только что описали. Однако большой компьютер может содержать много устройств ввода-вывода, причем несколько устройств могут работать одновременно, возможно, у разных пользователей. Существует некоторая вероятность, что во время работы программы обработки прерывания другое устройство ввода-вывода тоже захочет произвести свое прерывание.

Здесь существует два подхода. Первый подход — для всех программ обработки прерываний в первую очередь (даже до сохранения регистров) предотвратить последующие прерывания. При этом прерывания будут совершаться в строгой последовательности, но это может привести к проблемам с устройствами, которые не могут долго простаивать. Например, на коммуникационной линии со скоростью передачи 9600 битов в секунду символы поступают каждые 1042 микросекунды. Если первый символ еще не обработан, когда поступает второй, то данные могут потеряться.

Если компьютер имеет подобные устройства ввода-вывода, то лучше всего приписать каждому устройству определенный приоритет, высокий для более критич-

416 Глава 5. Уровень архитектуры команд

ных и низкий для менее критичных устройств. Центральный процессор тоже должен иметь приоритеты, которые определяются по одному из полей слова состояния программы. Если устройство с приоритетом п вызывает прерывание, программа обработки прерывания тоже должна работать с приоритетом п.

Если работает программа обработки прерываний с приоритетом п, любая попытка другого устройства с более низким приоритетом будет игнорироваться, пока программа обработки прерываний не завершится и пока центральный процессор не возвратится к выполнению программы более низкого приоритета. С другой стороны, прерывания, поступающие от устройств с более высоким приоритетом, должны происходить без задержек.

Поскольку сами программы обработки прерываний подвержены прерыванию, лучший способ строгого управления — сделать так, чтобы все прерывания были прозрачными. Рассмотрим простой пример с несколькими прерываниями. Компьютер имеет три устройства ввода-вывода: принтер, диск и линию RS232 с приоритетами 2,4 и 5 соответственно. Изначально (t=0; t — время) работает пользовательская программа. Вдруг при t= 10 принтер совершает прерывание. Запускается программа обработки прерывания принтера, как показано на рис. 5.30.

Прерывание диска, приоритет2

Окончание работы RS232, прерываниедискапринято

 

Прерывание RS232,

 

Окончание обработки

 

 

 

прерывания диска

 

 

приоритет5

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Окончаниеобработки

 

Прерывание принтера,

 

 

 

прерывания принтера

 

 

 

 

 

 

приоритет2

 

 

 

 

 

 

О

1 0

15

20

25

35

40

 

I

I

 

 

 

 

 

 

 

 

 

 

 

 

 

Программа

 

Работа

 

Обработка

Программа Время-

пользователя:

 

RS232

 

прерывания

пользователя

 

 

 

 

 

 

диска

 

 

 

•[Пользователь!

Пользователь

Пользователь

[Пользователь!"

Стек

 

 

 

Принтер

 

Принтер

 

 

 

 

 

 

 

Рис. 5.30. Примерс несколькими прерываниями. Последовательностьдействий

При t=15 линия RS232 порождает сигнал прерывания. Так как линия RS232 имеет более высокий приоритет (5), чем принтер (2), прерывание происходит. Состояние машины, при котором работает программа обработки прерывания принтера, сохраняется в стеке, и начинается выполнение программы обработки прерывания RS232.

Немного позже, при t=20, диск завершает свою работу. Однако его приоритет (4) ниже, чем приоритет работающей в данный момент программы обработки прерыва-

Ханойская башня

417

ний (5), поэтому центральный процессор не подтверждает прием сигнала прерывания, идиск вынужден простаивать. При t=25 заканчивается программа RS232, и машина возвращается в то состояние, в котором она находилась до прерывания RS232, то есть в то состояние, когда работала программа обработки прерывания принтера с приоритетом 2. Как только центральный процессор переключается на приоритет 2, еще до того как будет выполнена первая команда, диск с приоритетом 4 совершает прерывание и запускается программа обработки прерываний диска. После ее завершения снова продолжается программа обработки прерываний принтера. Наконец, при t=40 все программы обработки прерываний завершаются и выполнение пользовательской программы начинается с того места, на котором она прервалась.

Со времен процессора 8088 все процессоры Intel имеют два уровня (приоритета) прерываний: маскируемые и немаскируемые прерывания. Немаскируемые прерывания обычно используются только для сообщения об очень серьезных ситуациях, например об ошибках четности в памяти. Все устройства ввода-вывода используют одно маскируемое прерывание.

Когда устройство ввода-вывода вызывает прерывание, центральный процессор использует вектор прерывания при индексировании таблицы из 256 элементов, чтобы найти адрес программы обработки прерываний. Элементы таблицы представляют собой 8-байтные дескрипторы сегмента. Таблица может начинаться в любом месте памяти. Глобальный регистр указывает на ее начало.

При наличии только одного уровня прерываний центральный процессор не может сделать так, чтобы устройство с более высоким приоритетом прерывало работу программы обработки прерываний с более низким приоритетом и чтобы устройство с более низким приоритетом не смогло прерывать выполнение программы обработки прерываний с более высоким приоритетом. Для решения этой проблемы центральные процессоры Intel обычно используют внешний контроллер прерываний (например, 8259А). При первом прерывании (например, с приоритетом п) работа процессора приостанавливается. Если после этого происходит еще одно прерывание с более высоким приоритетом, контроллер прерывания вызывает прерывание во второй раз. Если второе прерывание обладает более низким приоритетом, оно не реализуется до окончания первого. Чтобы эта система работала, контроллер прерывания должен каким-либо образом узнавать о завершении текущей программы обработки прерываний. Поэтому когда текущее прерывание полностью обработано, центральный процессор должен посылать специальную команду контроллеру прерываний.

Ханойская башня

Теперь, когда мы уже изучили уровень команд трех машин, нам нужно все обобщить. Давайте подробно рассмотрим тот же пример программы («Ханойская башня») для всех трех машин. В листинге 5.6 приведена версия этой программы на языке Java. В следующих разделах для решения этой задачи мы предложим программы на ассемблере.

Однако вместо того, чтобы давать трансляцию версии на языке Java, для машин Pentium II и UltraSPARC II мы представим трансляцию версии на языке С,

4 1 8 Глава 5. Уровень архитектуры команд

чтобы избежать проблем с вводом-выводомJava. Единственное различие — это замена оператора Java printf на стандартный оператор языка С

printfC"Переместить диск с %6 на %d\r\", i,j)

Синтаксис строки printf не важен (строка печатается буквально за исключением *d — это означает, что следующее целое число будет дано в десятичной системе счисления). Здесь важно только то, что процедура вызывается с тремя параметрами: форматирующей строкой и двумя целыми числами.

Мы использовали язык С для Pentium II и UltraSPARC II, поскольку библиотека ввода-вывода Java не доступна для этих машин, а библиотека С доступна. ДляJVM мы будем использовать языкJava. Разница минимальна: всего один оператор вывода строки на экран.

Решение задачи «Ханойская башня» на ассемблере Pentium II

В листинге 5.7 приведен возможный вариант трансляции программы на языке С для компьютера Pentium П. Регистр ЕВР используется в качестве указателя фрейма. Первые два слова применяются для установления связи, поэтому первый параметр п (или N, поскольку регистр для макроассемблера не важен) находится в ячейке ЕВР+8, а за ним следуют параметры i иj в ячейках ЕВР+12 и ЕВР+16 соответственно. Локальная переменная к находится в ЕВР+20.

Листинг 5.7. Решение задачи «Ханойская башня» для машины Pentium II

 

.586

 

 

;компилируется для Pentium

 

.MODEL FLAT

 

 

 

 

 

 

 

PUBLIC _towers

 

 

;экспорт

'towers'

 

EXTERN _printf:NEAR

 

;импорт printf

 

.CODE

 

 

 

 

 

 

 

_towers:

PUSH EBP

 

сохраняет

ЕВР (указатель

фрейма)

 

MOV EBP. ESP

[устанавливает новый указатель фрейма над ESP

 

CMP[EBP+8].l

;if(n==l)

 

 

 

 

JNE LI

 

;переход,

если п?1

 

 

MOV EAX.

[EBP+16] ;printf("...". i. j);

 

 

 

PUSH

EAX

 

;сохранение параметров i. j и формата

 

MOV EAX. [ЕВР+12]

;строка помещается

в стек

 

 

PUSH

EAX

 

;в обратном порядке. Таково требование языка С

 

PUSH OFFSET FLAT:format : OFFSET FLAT - это адрес формата

 

CALL _printf

;вызов процедуры printf

 

 

ADD ESP.

12

:удаление параметров из стека

 

 

JMP

Done

 

;завершение

 

 

 

 

MOV

EAX.6

 

;начало вычисления k=6-i-j

 

 

SUB EAX. [EBP+12] :EAX=6-i

 

 

 

 

SUB EAX. [EBP+16] ;EAX-6-i-j

 

 

 

 

MOV [EBP+20], EAX

;k=EAX

 

 

 

 

PUSH

EAX

 

;начало процедуры towers(n-l,

п. к)

 

MOV EAX.

[EBP+12] ;EAX=i

 

 

 

 

PUSH

EAX

 

;помещает в

стек i

 

 

MOV EAX. [EBP+8]

;EAX=n

 

 

 

 

DEC

EAX;EAX=n-l

 

 

 

 

 

PUSH

EAX

 

;помещает в стек n-1

 

 

CALL _towers

;вызов процедуры towers(n-1. i,

6-i-j)

 

 

 

 

 

 

 

 

 

Ханойская башня

419

 

ADD ESP.

12

:удаление параметров

из стека

 

 

 

 

MOV EAX. [EBP+16] тачало процедуры towers (1,

i,

j)

 

 

PUSH EAX

 

:помещает в стек j

 

 

 

 

 

MOV

EAX,

[EBP+12]

:EAX=i

 

 

 

 

 

 

 

 

PUSH EAX

 

:помещает в стек i

 

 

 

 

 

PUSH 1

 

шомещает в стек 1

 

 

 

 

 

CALL

towers

;вызывает процедуру towersCl,

t,

j)

 

 

ADD ESP.

12

:удаляет

параметры из

стека

 

 

 

 

MOV

EAX. [EBP+12]

.•начало процедуры towers(n-l.

6-i-j. i)

 

 

PUSH EAX

 

.•помещает в стек i

 

 

 

 

 

MOV

EAX,

[EBP+20]

:EAX=k

 

 

 

 

 

 

 

 

PUSH EAX

 

:помещает в стек к

 

 

 

 

 

MOV EAX. [EBP+8]

:ЕАХ= п

 

 

 

 

 

 

 

 

DEC EAX:!ГАХ-n-l

 

 

 

 

 

 

 

 

 

PUSH EAX

 

;помещает в стек п-1

 

 

 

 

 

CALL

towers

:вызов процедуры towersСn-1,

6-i-j. i)

 

 

ADD ESP.

12

:корректировка

указателя стека

 

 

Done:

LEAVE

 

;подготовка

к выходу

 

 

 

 

.DATA

RET 0

 

.•возврат

к

вызывающей

программе

 

 

 

 

 

 

 

 

 

 

 

 

 

format

DB "Переместить диск

с %d на

£d\n"

[форматирующая

строка

 

END

 

 

 

 

 

 

 

 

 

 

 

Процедура начинается с создания нового фрейма в конце старого. Для этого значение регистра ESP копируется в указатель фрейма ЕВР. Затем п сравнивается с 1, и если п>1, то совершается переход к операторуel se. Тогда программа then помещает в стек три значения: адрес форматирующей строки, i иj, и вызывает саму себя.

Параметры помещаются в стек в обратном порядке, поскольку это требуется для программ на языке С. Необходимо поместить указатель на форматирующую строку в вершину стека. Процедура printf имеет переменное число параметров, и если параметры будут помещаться в стек в прямом порядке, то процедура не сможет узнать, в каком месте стека находится форматирующая строка.

После вызова процедуры к регистру ESP прибавляется 12, чтобы удалить параметры из стека. Насамомделе они неудаляются из памяти, но корректировка (изменение) регистра ESP делает их недоступными через обычные операции со стеком.

Выполнение части else начинается с L1. Здесь сначала вычисляется выражение 6-i-j, и полученное значение сохраняется в переменной к. Сохранение значения в переменной к избавляет от необходимости вычислять это во второй раз.

Затем процедура вызывает сама себя три раза, каждый раз с новыми параметрами. После каждого вызова стек освобождается.

Рекурсивные процедуры иногда приводят людей в замешательство. Но на самом деле они совсем несложные. Просто параметры помещаются в стек, и вызывается процедура.

Решение задачи «Ханойская башня» на ассемблере UltraSPARC II

А теперь рассмотрим то же самое для UltraSPARC П. Программа приведена в листинге 5.8. Поскольку программа для UltraSPARC II совершенно нечитаема даже после длительных тренировок, мы решили определить несколько символов, чтобы

4 2 0 Глава 5. Уровень архитектуры команд

прояснить дело. Чтобы такая программа работала, ее перед ассемблированием нужно пропустить через программу под названием срр (препроцессор С). Здесь мы используем строчные буквы, поскольку ассемблер Pentium II требует этого (это на тот случай, если читатели захотят напечатать и запустить эту программу).

Листинг 5.8. Решение задачи «Ханойская башня» для UltraSPARC II

#define N £iO /* N - это входной параметр 0 */ #define M i l /* I - это входной параметр 1 */ #define J £i2 /* J - это входной параметр 2 */

#define К «10

 

 

/* К - это локальная

переменная 0

*/

 

 

 

#define

ParamO

SoO

/*

ParamO -

это выходной параметр 0 */

 

 

 

#define

Paraml

Xol

/*

Paraml - это

выходной

параметр

1 */

 

 

 

Idefine

Param2 Яо2

/*

Param2 - это

выходной

параметр

2 */

 

 

 

#define Scratch XII /*примеч.: срр использует запись

комментариев как в

языке С*/

 

 

.proc

04

 

 

 

 

 

 

 

 

 

 

 

 

.global

towers

 

 

 

 

 

 

 

 

 

towers:

save *sp.-112. *sp

 

 

 

 

 

 

 

 

 

 

cmp N, 1

 

 

 

 

if(n=

1)

 

 

 

 

 

bne Else

 

 

 

 

if (n

!= 1) goto Else

 

 

 

sethi £hi(format). ParamO

 

printf("Переместить

диск с %d на %d\n". i,

j)

 

or ParamO. Xlo(format). ParamO

ParamO

= адрес форматирующей

строки

 

 

mov

I. Paraml

 

 

 

Paraml = i

 

 

 

 

 

call

printf

 

 

 

вызов printf ДО установки параметра 2 (j)

 

 

mov J. Param2

 

 

 

пустая операция для установки параметра 2

 

 

b Done

 

 

 

 

завершение

 

 

 

 

 

пор

 

 

 

 

 

 

вставляет

пустую операцию

 

 

Else:

mov 6. К

 

 

 

 

начало вычисления к = б -i-j

 

 

 

sub

K.J.K

 

 

 

k-6-j

 

 

 

 

 

 

sub

K.I,К

 

 

 

 

 

 

 

 

 

 

 

add N, -1. Scratch

начало процедуры towers(n-l. i. k)

 

 

 

mov Scratch, ParamO

Scratch = n-1

 

 

 

 

 

 

mov

I. Paraml

 

параметр 1 - i

 

 

 

 

 

 

call

towers

 

вызов процедуры towers ДО установки параметра 2 (k)

 

 

mov K,

Param2

 

пустая операция после вызова процедуры для установки

 

 

 

 

 

 

 

параметра2

 

 

 

 

 

 

 

mov 1,

ParamO

!

начало процедуры towersd. i. j)

 

 

 

mov

I. Paraml

!

параметр1=1

 

 

 

 

 

 

 

call

towers

!

вызов процедуры towers ДО установки параметра 2 (j)

 

 

mov J. Param2

!

параметр 2 = j

 

 

 

 

 

 

mov Scratch. ParamO !

начало процедуры towers(n-l. k. j)

 

 

 

mov K. Paraml

!

параметр 1 « к

 

 

 

 

 

 

call

towers

!

вызов процедуры towers ДО установки параметра 2 (j)

 

 

mov J. Param2

!

параметр 2 = j

 

 

 

 

 

Done:

ret

 

 

 

!

выход из

процедуры

 

 

 

 

 

restore

 

!

вставка

пустой команды после

ret для

восстановления

окон

format:

.asciz

"Переместить диск с %d на %й\п"

 

 

 

 

 

По алгоритму версия UltraSPARC идентична версии Pentium II. В обоих случаях сначала проверяется п, и если п>1, то совершается переход к el se. Основные

Ханойская башня

421

сложности версии UltraSPARC II связаны с некоторыми свойствами архитектурыкоманд.

Сначала UlraSPARC II должен передать адрес форматирующей строки в printf, но машина не может просто переместить адрес в регистр, который содержит выходящий параметр, поскольку нельзя поместить 32-битную константу в регистр за одну команду. Для этого требуется выполнить две команды: SETHI и OR.

После вызова не нужно делать подстройку стека, поскольку регистровое окно корректируется командой RESTORE в конце процедуры. Возможность помещать выходящие параметры в регистры и не обращаться к памяти дает огромный выигрыш в производительности.

А теперь рассмотрим команду NOP, которая следует за Done. Это пустая операция. Эта команда всегда будет выполняться, даже если она следует за командой условного перехода. Сложность состоит в том, что процессор UltraSPARC II сильно конвейеризирован, и к тому моменту, когда аппаратное обеспечение обнаруживает команду перехода, следующая команда уже практически закончена. Добро пожаловать в прекрасный мир программирования RISC!

Эта особенность распространяется и на вызовы процедур. Рассмотрим первый вызов процедуры towers в части else. Процедура помещает п-1 в %оО, a i — в %ol, но совершает вызов процедуры towers до того, как поместит последний параметр в нужное место. На компьютере Pentium II вы сначала передаете параметры, а затем вызываете процедуру. А здесь вы сначала передаете часть параметров, затем вызываете процедуру, и только после этого передаете последний параметр. К тому моменту, когда машина осознает, что она имеет дело с командой CALL, следующую команду все равно приходится выполнять (из-за конвейеризации системы). А почему бы в этом случае не использовать пустую операцию, чтобы передать последний параметр? Даже если самая первая команда вызванной процедуры использует этот параметр, он уже будет на своем месте.

Наконец, рассмотрим часть команды Done. Здесь после команды RET тоже вставляется пустая операция. Эта пустая операция используется для команды RESTORE, которая увеличивает на 1 значение CWP, чтобы вернуть регистровое окно в прежнее состояние.

Решение задачи «Ханойская башня» на ассемблере для JVM

Соответствующая программа дана в листинге 5.9. Решение довольно простое, за исключением процесса ввода-вывода. Эта программа была порождена компилятором Java, переделана в символический язык ассемблера и обработана определенным образом для удобочитаемости. Компилятор JVM хранит три параметра п, i и j в локальных переменных 0, 1 и 2 соответственно. Локальная переменная к хранится влокальной переменной 3. Ко всем четыремлокальным переменным можно обратиться с помощью 1-байтного кода операции, например ILOAD0. В результате двоичная версия этой программы в JVM получается очень короткой (всего 67 байтов).

4 2 2

Глава 5. Уровень архитектуры команд

 

Листинг 5.9. Решение задачи «Ханойская башня» для JVM

 

IL0ADJ)

 

// лок. переменная 0 = п; помещает в стек п

 

ICONST_1

// помещает в стек 1

 

 

IFJCMPNE L I

//if(n!-l)gotoLl

 

 

 

GETSTATIC #13

// п

— 1:

эта

команда обрабатывает выражение pnntln

 

NEW #7

 

//

размещает

буфер для строки, которую нужно создать

 

DUP

 

// дублирует указатель на буфер

 

LDC #2

 

// помещает в стек указатель на цепочку "перенести диск с"

INVOKESPECIAL #10 // копирует эту цепочку в буфер

 

ILOAD_1

 

// помещает в стек i

 

 

INVOKEVIRTUAL #11 // превращает

i в цепочку и присоединяет к новому буферу

 

LDC #1

 

// помещает в стек указатель на цепочку "на"

 

INVOKEVIRTUAL #12 // присоединяет эту цепочку к буферу

 

ILOAD_2

 

// помещает в стек j

 

 

INVOKEVIRTUAL #11 // превращает j в цепочку и присоединяет ее к буферу

 

INVOKEVIRTUAL #15 // преобразование строки

 

 

INVOKEVIRTUAL #14 // вызов println

 

 

 

RETURN

 

// выход из процедуры towers

 

LI: BIPUSH6

// Часть Else:

вычисление k = 6-i-j

 

ILOAD_1

 

// лок. переменная 1 = i;

помещает в стек i

 

ISUB

 

// вершина стека = 6-i

 

 

IL0AD_2

 

// лок. переменная 2= j;

помещает в стек j

 

ISUB

 

// вершина стека = 6-i-j

 

ISTORE_3

 

// лок. перем. 3 - k = 6-i-j: стек сейчас пуст

 

IL0ADJ)

 

//

начало работы процедуры towers(n-l.i. k);помещает

в стек п

ICONST_1

 

// помещает в стек 1

 

 

ISUB

 

// вершина стека = п-1

 

 

ILOAD_1

 

// помещает в стек i

 

 

IL0AD_3

 

// помещает в стек к

 

 

INVOKESTATIC #16

// вызывает процедуру towers(п-1. i. к)

 

ICONST_1

 

//

начинается

работа процедуры towersQ,j) помещает в

стек 1

ILOAD_1

 

// помещает в стек i

 

 

IL0AD_2

 

// помещает в стек j

 

 

INVOKESTATIC #16 // вызов процедуры towersd. i. j)

 

ILOAD_0

"

// начало работы процедуры towers(n-kl.. j ) ; помещает в стек п

ICONSTJ.

 

// помещает в стек 1

 

 

ISUB

 

// вершина стека = п-1

 

 

IL0AD_3

 

// помещает в стек к

 

 

IL0AD_2

 

// помещает в стек j

 

 

INVOKESTATIC #16

// вызов процедуры towers(n-l, k. j)

 

RETURN

 

//

выход

из

процедуры towers

 

Сначала программа помещает в стек параметр п и константу 1, а затем сравнивает их с помощью команды IFICMPNE. Эта команда обратна команде IF_ICMPEQ, которая используется в машине IJVM. Она выталкивает из стека два операнда и совершает переход, если они различны.

Если они одинаковы, выполнение программы продолжается последовательно. Следующие 13 команд определяют буфер строки и строят в нем цепочку, которая затем передается в println для вывода на экран. После завершения печати совершается выход из процедуры.

Если говорить кратко, эти 13 команд размещают буфер строки в «кучу» и заполняют его. Команда GETSTATIC индексирует набор констант, чтобы получить слово 13, которое содержит указатель на дескриптор для буфера строки. Команда NEW использует этот дескриптор для размещения буфера строки в «куче». Следую-

Соседние файлы в предмете Аппаратное обеспечение ЭВМ, средств телекоммуникаций и сетей