Решения и циклы

Решения и циклы

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

Нет if или for. Вы строите их из меток (именованных позиций) и ветвлений (переход к позиции, когда условие выполняется). Метка — это имя, за которым следует :.

Чтобы понять, как выполняется ветвление, нужно знать о Счётчике Команд (PC). PC — это специальный регистр, скрытый от вашего кода, который хранит адрес памяти выполняемой инструкции. Инструкция ветвления записывает новое значение в PC, поэтому процессор извлекает следующую инструкцию из вашей целевой метки, а не из следующей строки кода.

Семейство ветвлений

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

Шесть реальных ветвей
beq a, b, label # переход, если a == b
bne a, b, label # переход, если a != b
blt a, b, label # переход, если a < b (со знаком)
bge a, b, label # переход, если a >= b (со знаком)
bltu a, b, label # переход, если a < b (без знака)
bgeu a, b, label # переход, если a >= b (без знака)

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

Что означают знаковый и беззнаковый? Регистр хранит 32 бита, и одни и те же биты можно прочитать двумя способами. Прочитанные как беззнаковые, они образуют обычное целое число от 0 примерно до 4 миллиардов. Прочитанные как знаковые, самый старший бит считается знаком минус, поэтому половина битовых шаблонов представляет отрицательные числа. Сами биты не меняются. Меняется только то, как вы их читаете. Вот почему существует два набора сравнений: используйте знаковые (, ) для обычных чисел, которые могут быть отрицательными, и беззнаковые (, ) для значений, которые никогда не бывают отрицательными, таких как адреса памяти.

Нет прямого "ветвления, если больше" или "ветвления, если меньше-или-равно". Вы получаете их, меняя местами два регистра, и ассемблер предоставляет и как псевдоветвления, которые выполняют обмен за вас, так что становится . Ещё два сокращения: и проверяют один регистр относительно нуля, а выполняет переход вообще без условия.

Сумма 1..5 с циклом
li t0, 0 # сумма = 0
li t1, 1 # i = 1
li t2, 6 # предел = 6 (цикл пока i < 6)
loop:
bge t1, t2, done # выходит из цикла, когда i >= 6
add t0, t0, t1 # сумма += i
addi t1, t1, 1 # i++
j loop # снова проверить
done:
mv a0, t0 # печатает сумму (должно быть 15)
li a7, 1
ecall
li a7, 10
ecall

Проследите это. t0 хранит текущую сумму (начинается с 0), t1 хранит счётчик i (начинается с 1), а t2 хранит предел 6. При каждом проходе через loop: покидает цикл, как только i достигает 6. В противном случае добавляет i к сумме, увеличивает i на единицу, а переходит обратно для повторной проверки. Цикл складывает 1, 2, 3, 4 и 5, а done выводит итог, 15.

Цикл с ветвлением
loop:
bge t1, t2, done
add t0, t0, t1
addi t1, t1, 1
j loop
i (t1)1
sum (t0)0
даnot1 < 6?sum += t1t1 += 1готово

bge сравнивает t1 с t2, чтобы решить, продолжается ли цикл.

На рисунке ниже отмечены инструкция, обновляющая счётчик, и та, что обновляет текущую сумму, выделяя их среди остального цикла. увеличивает счётчик, а следующий за ним добавляет новое значение счётчика к сумме. Последний кадр показывает проверку, которая завершает цикл. Когда t1 становится 6, передаёт управление в done, вместо того чтобы позволить телу выполниться снова. Эта проверка — то, что отделяет проход, который повторяется, от прохода, который останавливается.
💡
Продвигайтесь по программе с помощью Step, а не Run. По мере продвижения t1 увеличивается, а счётчик команд (pc) переходит обратно к loop. Каждый раз, когда pc возвращается к loop, условие ветвления было ложным, поэтому цикл выполняется снова.
⚠
Если условие выхода неверно, цикл никогда не закончится. Симулятор останавливает вышедший из-под контроля цикл, но логика ветвления — ваша, и её нужно сделать правильно.