Обычно вам не нужно беспокоиться об их вычислении, так как ваш ассемблер (или компоновщик) предпримет правильные вычисления.Допустим, у вас есть небольшая функция:
func:
slti $t0, $a0, 2
beq $t0, $zero, cont
ori $v0, $zero, 1
jr $ra
cont:
...
jal func
...
При преобразовании вышеуказанного кода в двоичный поток инструкций ассемблер (или компоновщик, если вы впервые собрали его в объектный файл) будет определять, где в памятифункция будет постоянно (давайте пока проигнорируем независимый от позиции код).Где он находится в памяти, обычно указывается в ABI или дается вам, если вы используете симулятор (например, SPIM , который загружает код в 0x400000
- обратите внимание на ссылкутакже содержит хорошее объяснение процесса).
Если мы говорим о случае SPIM, и наша функция находится в памяти первой, инструкция slti
будет находиться в 0x400000
, beq
в 0x400004
и так далее.Теперь мы почти у цели!Для инструкции beq
целевой адрес ветви является адресом cont
(0x400010
), если смотреть на ссылку на инструкцию MIPS , мы видим, что она закодирована как 16-битнаяподписывается непосредственно относительно следующей инструкции (делится на 4, так как все инструкции должны в любом случае находиться на 4-байтовом выровненном адресе).
То есть:
Current address of instruction + 4 = 0x400004 + 4 = 0x400008
Branch target = 0x400010
Difference = 0x400010 - 0x400008 = 0x8
To encode = Difference / 4 = 0x8 / 4 = 0x2 = 0b10
Кодировка beq $t0, $zero, cont
0001 00ss ssst tttt iiii iiii iiii iiii
---------------------------------------
0001 0001 0000 0000 0000 0000 0000 0010
Как вы можете видеть, вы можете переходить с точностью до -0x1fffc .. 0x20000
байтов.Если по какой-либо причине вам необходимо прыгнуть дальше, вы можете использовать батут (безусловный переход к реальной цели, помещенной в заданный предел).
Адреса цели перехода , в отличие от ветвицелевые адреса, закодированные с использованием абсолютного адреса (снова разделенного на 4).Так как кодирование команды использует 6 битов для кода операции, для адреса остается только 26 битов (фактически 28, учитывая, что 2 последних бита будут 0), поэтому при формировании адреса используются 4 старших бита старшего разряда регистра ПК (не имеет значения, если вы не собираетесь перепрыгивать границы 256 МБ).
Возвращаясь к приведенному выше примеру, кодировка jal func
:
Destination address = absolute address of func = 0x400000
Divided by 4 = 0x400000 / 4 = 0x100000
Lower 26 bits = 0x100000 & 0x03ffffff = 0x100000 = 0b100000000000000000000
0000 11ii iiii iiii iiii iiii iiii iiii
---------------------------------------
0000 1100 0001 0000 0000 0000 0000 0000
Вы можете быстро проверить это, ипоиграйтесь с разными инструкциями, используя этот онлайн-ассемблер MIPS , с которым я столкнулся (обратите внимание, что он не поддерживает все коды операций, например slti
, поэтому я просто изменил его на slt
здесь):
00400000: <func> ; <input:0> func:
00400000: 0000002a ; <input:1> slt $t0, $a0, 2
00400004: 11000002 ; <input:2> beq $t0, $zero, cont
00400008: 34020001 ; <input:3> ori $v0, $zero, 1
0040000c: 03e00008 ; <input:4> jr $ra
00400010: <cont> ; <input:5> cont:
00400010: 0c100000 ; <input:7> jal func