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

3222

.pdf
Скачиваний:
5
Добавлен:
15.11.2022
Размер:
3.54 Mб
Скачать

англ. branch if equal) и ветвление при неравенстве (bne, от англ. branch if not equal).

Инструкция beq осуществляет переход, когда содержимое двух регистров равно, а bne осуществляет переход, если оно не равно. Пример кода ниже демонстрирует использование инструкции beq.

Условный переход с использованием beq

addi $s0, $0, 4

# $s0 = 0 + 4 = 4

addi $s1, $0, 1

# $s1 = 0 + 1 = 1

sll $s1, $s1, 2

# $s1 = 1 << 2 = 4

beq

$s0, $s1, target # $s0 = = $s1, so branch is taken

addi

$s1, $s1, 1

# not executed

sub

$s1, $s1, $s0

# not executed

target:

 

 

add

$s1, $s1, $s0

# $s1 = 4 + 4 = 8

Когда программа в примере кода выше достигает инструкции ветвления при равенстве (beq), значение в регистре $s0 равно значению в $s1, поэтому осуществляется переход. Таким образом, cледующей выполненной инструкцией будет инструкция add, располагающаяся сразу за меткой с именем target. Две инструкции, расположенные между инструкцией ветвления и меткой, не выполняются.

Метки в ассемблерном коде являются ссылками на инструкции программы. Когда ассемблерный код транслируется в машинный, метки заменяются соответствующими адресами инструкций. Определяя новую метку непосредственно перед инструкцией, на которую она будет ссылаться, мы ставим двоеточие после имени метки. Имена меток не могут совпадать с зарезервированными словами, в частности, с именами инструкций. Большинство программистов делают отступы из пробелов или символов табуляции перед инструкциями, но не делают их перед метками, что позволяет визуально выделить метки среди остального кода.

61

Следующий пример кода демонстрирует использование инструкции ветвления при неравенстве (bne). В этом случае ветвление не осуществляется потому, что $s0 равен $s1, и процессор продолжает выполнять код, расположенный сразу после инструкции bne. В этом фрагменте кода выполняются все инструкции.

Условный переход с использованием bne

addi $s0, $0, 4

# $s0 = 0 + 4 = 4

addi $s1, $0, 1

# $s1 = 0 + 1 = 1

s11

$s1, $s1, 2

# $s1 = 1 << 2 = 4

bne

$s0, $s1, target

# $s0 = = $s1, so branch is not taken

addi $s1, $s1, 1

# $s1 = 4 + 1 = 5

sub

$s1, $s1, $s0

# $s1 = 5 − 4 = 1

target:

 

 

add

$s1, $s1, $s0

# $s1 = 1 + 4 = 5

Безусловные переходы

Для безусловных переходов программа может использовать инструкции трёх типов: обычный безусловный переход (j, от англ. jump), безусловный переход с возвратом (jal, от англ. jump and link) и безусловный переход по регистру (jr, от англ. jump register).

Безусловный переход (j) осуществляет переход к инструкции по указанной метке. Безусловный переход с возвратом (jal) похож на j, но дополнительно сохраняет адрес возврата и используется при вызове функций.

Безусловный переход по регистру (jr) осуществляет переход к инструкции, адрес которой хранится в одном из регистров процессора.

По окончании выполнения инструкции j target в примере следующего кода программа продолжит исполнение с инструкции add, расположенной непосредственно за меткой target. Ни одна инструкция между j и меткой не будет выполнена.

62

Безусловный переход с использованием j

addi

$s0, $0, 4

# $s0 = 4

addi

$s1, $0, 1

# $s1 = 1

j target

# jump to target

addi

$s1, $s1, 1 # not executed

sub

$s1, $s1, $s0

# not executed

target:

 

 

 

add

$s1, $s1, $s0 # $s1 = 1 + 4 = 5

Следующий пример демонстрирует использование инструкции безусловного перехода по регистру (jr). В этом примере адреса инструкций указаны слева от каждой из них. Инструкция jr $s0 осуществляет переход по адресу 0x00002010, взятому из регистра $s0.

Безусловный переход с использованием jr

0x00002000 addi $s0,$0, 0x2010 # $s0 = 0x2010

0x00002004 jr

$s0

# jump to 0x00002010

0x00002008 addi $s1,$0, 1

# not executed

0x0000200c sra

$s1,$s1, 2

# not executed

0x00002010 lw

$s3,44($s1)

# executed after jr instruc-

tion

2.4. Условные операторы и циклы

Операторы if, if/else и switch/case являются условными операторами, которые часто используются в языках высокого уровня.

Каждый из этих операторов при выполнении определённого условия исполняет участок кода, состоящий, в свою очередь, из одного или нескольких операторов. В этом разделе показано, как перевести эти высокоуровневые конструкции на язык ассемблера MIPS.

63

Оператор if

Оператор if выполняет участок кода, называемый блоком «если» (англ.: if block), только если выполнено заданное условие.

Пример демонстрирует, как перевести выражение с оператором if на язык ассемблера MIPS.

Оператор if

Код на языке высокого уровня if (i == j)

f = g + h; f = f – i;

Код на языке ассемблера MIPS

# $s0 = f, $s1 = g, $s2 = h, $s3 = i, $s4 = j bne $s3, $s4, L1 # if i != j, skip if block add $s0, $s1, $s2 # if block: f = g + h

L1:

sub $s0, $s0, $s3 # f = f − i

Код на языке ассемблера для оператора if проверяет условие, противоположное условию, заданному на языке высокого уровня.

В выше изложенным примере код на языке высокого уровня проверяет условие i == j, а ассемблерный код проверяет условие i!= j. Инструкция bne осуществляет ветвление, пропуская блок если, когда i!= j. Если i == j, то ветвление не осуществляется и блок «если», как и ожидалось, выполняется.

Операторы if/else

Операторы if/else выполняют один из двух участков кода в зависимости от условия. Когда выполнено условие выражения if, выполняется блок «если». В противном случае выполняется блок «иначе» (англ.: else block). Пример кода демонстрирует пример оператора if/else.

Код на языке высокого уровня if (i == j)

64

f = g + h; else

f = f − i;

Код на языке ассемблера MIPS

# $s0 = f, $s1 = g, $s2 = h, $s3 = i, $s4 = j

bne $s3, $s4, else

# if i != j, branch to else

add $s0, $s1, $s2

# if block: f = g + h

j L2

# skip past the else block

else:

sub $s0, $s0, $s3 # else block: f = f − i

L2:

Как и в случае оператора if, ассемблерный код для оператора if/else проверяет условие, противоположное условию, заданному в коде на языке высокого уровня. Так, в примере приведённом выше код высокого уровня проверяет условие i==j. Ассемблерный код проверяет противоположное условие (i!=j). Если противоположное условие истинно, то инструкция bne пропускает блок если и выполняет блок иначе. В противном случае, блок «если» выполняется и завершается инструкцией перехода (j) для перехода на участок после блока «если» (прим. переводчика: компиляторы языков высокого уровня могут менять местами блоки «если» и блоки «иначе», а значит и генерировать машинный код с проверкой оригинального, а не противоположного условия).

Операторы switch/case

Операторы switch/case выполняют один из нескольких участков кода в зависимости от того, какое из данных условий удовлетворяется. Если ни одно из условий не удовлетворяется, то выполняется блок default.

Оператор case аналогичен последовательности вложенных операторов if/else. Пример кода ниже демонстрирует два фрагмента на языке высокого уровня с одной и той же функциональностью: они вычисляют размер комиссии при выдаче денег из банкомата для сумм 20, 50 и 100 долларов.

65

Сумма задаётся в переменной amount (прим. переводчика: следует отметить, что разные высокоуровневые конструкции могут быть реализованы одинаково на ассемблере MIPS, и наоборот, одна и та же высокоуровневая конструкция может быть реализована на ассемблере по-разному.

Например, оператор switch можно реализовать на языке ассемблера, создав массив с адресами переходов, который индексируется аргументом switch).

Операторы switch/case

Код на языке высокого уровня switch (amount) {

case 20: fee = 2; break; case 50: fee = 3; break; case 100: fee = 5; break; default: fee = 0;

}

 

 

// equivalent function using if/else statements

if

(amount = = 20)

fee = 2;

else if (amount = = 50)

fee = 3;

else if (amount = = 100) fee = 5;

else

fee = 0;

Код на языке ассемблера MIPS

# $s0 = amount, $s1 = fee

case20:

 

addi $t0, $0, 20

# $t0 = 20

bne $s0, $t0, case50 # amount = = 20? if not,

 

# skip to case50

addi $s1, $0, 2

# if so, fee = 2

j done

# and break out of case

case50:

 

addi $t0, $0, 50

# $t0 = 50

bne $s0, $t0, case100 # amount = = 50? if not,

# skip to case100

66

 

addi $s1, $0, 3

# if so, fee = 3

j

done

# and break out of case

case100:

 

 

 

addi $t0, $0, 100

# $t0 = 100

 

bne $s0, $t0, default # amount = = 100? if not,

 

 

 

# skip to default

addi $s1, $0, 5

 

# if so, fee = 5

j

done

 

# and break out of case

default:

 

 

add $s1, $0, $0

# fee = 0

done:

 

 

Циклы

Циклы многократно выполняют участок кода в зависимости от условия.

Циклы for и циклы while являются обычными конструкциями для организации циклов в языках высокого уровня. В этом разделе будет показано, как перевести их на язык ассемблера MIPS.

Циклы while

Циклы while многократно выполняют участок кода до тех пор, пока условие не станет ложным. В следующем примере кода цикл while ищет значение x такое, что 2x = 128. Цикл выполнится семь раз, прежде чем достигнет условия pow = 128.

Пример ЦИКЛ while

Код на языке высокого уровня int pow = 1;

int x = 0;

while (pow != 128)

{

pow = pow * 2; x = x + 1;

67

}

Код на языке ассемблера MIPS

# $s0 = pow, $s1 = x

addi $s0, $0, 1

# pow = 1

addi $s1, $0, 0

# x = 0

addi $t0, $0, 128 # t0 = 128 for comparison while:

beq $s0, $t0, done # if pow = = 128, exit while loop

sll $s0, $s0, 1

# pow = pow * 2

addi $s1, $s1, 1

# x = x + 1

j while

 

done:

 

Вассемблерном коде в цикле while проверяется условие, противоположное условию, использованному на языке высокого уровня, аналогично тому, как это делается для оператора if/else. Если это противоположное условие истинно, то цикл while завершается.

Впредыдущем примере оператор цикла while сравнивает значение переменной pow со значением 128 и завершает цикл, если они равны.

Впротивном случае происходит удвоение pow (используя сдвиг влево), увеличение x на 1 и переход обратно на начало цикла while.

Циклы for

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

for (инициализация; условие; операция цикла) оператор

Код инициализации выполняется до того, как цикл for начнется. Условие проверяется в начале каждой итерации. Если условие не выполнено, цикл завершается. Операция цикла выполняется в конце каждой итерации.

68

Пример ниже складывает целые числа от 0 до 9. Счетчик итераций цикла, в данном случае i, инициализируется нулем и увеличивается на единицу в конце каждой итерации. Условие i != 10 проверяется в начале каждой итерации. Итерация цикла for выполняется только тогда, когда условие истинно, т.е. когда значение i не равно 10, иначе цикл завершается. В нашем случае цикл for выполняется 10 раз. Циклы for могут быть реализованы и при помощи циклов while, но цикл for часто удобнее.

Пример Цикл for

Код на языке высокого уровня int sum = 0;

for (i = 0; i != 10; i = i + 1) { sum = sum + i ;

}

// equivalent to the following while loop int sum = 0;

int i = 0;

while (i != 10) { sum = sum + i; i = i + 1;

}

Код на языке ассемблера MIPS

# $s0 = i, $s1 = sum

add $s1, $0, $0

# sum = 0

addi $s0, $0, 0

# i = 0

addi $t0, $0, 10

# $t0 = 10

for:

beq $s0, $t0, done # if i = = 10, branch to done add $s1, $s1, $s0 # sum = sum + i

addi $s0, $s0, 1 # increment i j for

done:

69

Сравнение по величине

До сих пор в рассмотренных примерах мы использовали инструкции beq и bne для сравнений и ветвлений на основе равенства или неравенства значений. Архитектура MIPS содержит инструкцию установить, если меньше (slt, от англ. set less than) для сравнения значений по величине. Инструкция slt устанавливает регистр rd в 1, если значение регистра rs меньше значения регистра rt. Иначе, в регистр rd записывается 0.

Циклы с использованием инструкции slt

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

// high-level code int sum = 0;

for (i = 1; i < 101; i = i * 2) sum = sum + i;

Решение: Код на языке ассемблера использует инструкцию установить, если меньше (slt) для выполнения сравнения в цикле for.

#MIPS assembly code

#$s0 = i, $s1 = sum

addi $s1, $0, 0

# sum = 0

 

addi $s0, $0, 1

# i = 1

 

addi $t0, $0, 101

# $t0 = 101

 

loop:

 

 

slt

$t1, $s0, $t0

# if (i < 101)

$t1 = 1, else $t1 = 0

beq $t1, $0, done

# if $t1 == 0 (i >= 101), branch to done

add $s1, $s1, $s0

# sum = sum + i

sll

$s0, $s0, 1

# i = i * 2

 

j

loop

 

 

done:

 

 

 

 

 

70

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]