Итак, давайте потихоньку двигаться дальше. Предлагаю внести в пример из предыдущего сообщения небольшие изменения. А именно, после строки с директивой org добавить ещё три строки:
То есть по итогу должно получиться вот так:Код:blx start thumb start:
Так как мы что-то добавили в программу, то исполняемый код должен увеличиться на несколько байт. Давайте скомпилируем и проверим. И что мы видим? Исполняемый код стал объёмом 46 байт, то есть ещё короче чем был! А был, напомню, 54 байта.
Что же произошло? Давайте попробуем разобраться и дизассемблировать код обеих вариантов с помощью популярного дизассемблера IDA Pro.
Вот так выглядит код первоначального варианта (54 байта):
Всё совпадает с ассемблерным текстом! (На команду MOV R0 не обращайте внимания. По умолчанию IDA для простоты восприятия заменяет некоторые последовательности команд в метакоманды. Вот и здесь она заменила две команды загрузки регистра R0 по половинкам (MOV и MOVT) в одну длинную загрузку, которая на самом деле отсутствует в наборе команд процессоров ARM. Где-то в настройках IDA Pro это можно отключить и тогда всё будет отображаться, как есть.)
А вот так выглядит дизассемблер модифицированного варианта (46 байт):
И тоже всё совпадает, и даже дополнительная команда BLX появилась. Но при этом код стал короче!
А произошло следующее: директивой thumb мы дали команду ассемблеру все дальнейшие команды вместо стандартного 32-х битного режима ассемблировать в 16-битном режиме thumb. А, собственно, командой BLX мы дали понять процессору, что код, на который будет осуществлён дальнейший переход, будет исполняться в режиме thumb.
В режиме thumb все команды, в отличие от 32-х битного режима, кодируются только двумя байтами. В результате исполняемый код такой программы становится более компактным. И всё было бы отлично, если бы не кое-какие ограничения. И прежде всего это ограниченный набор команд - их всего лишь 36. Есть и другие ограничения, но сейчас не будем на них останавливаться.
Что касается меня, то режим thumb привлёк моё внимание именно ограниченным набором команд. Дело в том, что за долгую историю архитектуры ARM было выпущено несколько поколений процессоров. Для них было придумано большое количество команд, у каждой из которых есть свои особенности. Разобраться и изучить их все довольно проблематично. А здесь же мы имеем только 36 команд, с помощью которых можно запрограммировать практически любое действие. При этом, если вам когда-нибудь вдруг станет тесно в рамках thumb, вы всегда сможете перейти на 32-х битную систему команд без необходимости что-то менять в своём коде. Наверное, здесь можно привести аналогию с процессорами i8080 (КР580ВМ80А) и Z80. Если писать код для процессора i8080 (аналогия с thumb), то код будет работать и на i8080, и на Z80. Но если вам становится тесно, то вы можете начать использовать дополнительные регистры и команды процессора Z80 (аналогия с 32-х битным режимом ARM), но тогда уже потеряете совместимость с i8080.
В конце хочу привести полный список из 36 команд процессоров ARM в режиме thumb:
№ Мнемоника Команда Пример Эквивалент кода ARM 1 ADC Сложение с учетом переноса ADC Rd, Rs ADCS Rd, Rd, Rs 2 ADD Сложение ADD Rd, Rs, Rn ADD Rd, Rs, Rn 3 AND Логическое "И" AND Rd, Rs ANDS Rd, Rd, Rs 4 ASR Арифметический сдвиг вправо ASR Rd, Rs MOVS Rd, Rd, ASR Rs 5 B Безусловный переход B label B label 6 BCC Переход по условию CC BCC label BCC label 7 BIC Сброс битов (маскирование) BIC Rd, Rs BICS Rd, Rd, Rs 8 BL Переход со ссылкой BL label BL label 9 BX Переход и смена режима ядра BX Hs BX Hs 10 CMN Сравнение с отрицанием CMN Rd, Rs CMN Rd, Rs 11 CMP Сравнение CMP Rd, #Offset8 CMP Rd, #Offset8 12 EOR Исключающее "ИЛИ" EOR Rd, Rs EORS Rd, Rd, Rs 13 LDMIA Групповая загрузка регистров LDMIA Rb!, {Rlist} LDMIA Rb!, {Rlist} 14 LDR Загрузка целого слова LDR Rd, [PC, #lmm] LDR Rd, [PC, #lmm] 15 LDRB Загрузка байта LDRB Rd, [Rb, Ro] LDRB Rd, [Rb, Ro] 16 LDRH Загрузка полуслова LDRH Rd, [Rb, #lmm] LDRH Rd, [Rb, #lmm] 17 LSL Логический сдвиг влево LSL Rd, Rs, #Offset5 MOVS Rd, Rs, LSL #Offset5 18 LDRSB Загрузка байта со знаком LDRSB Rd, [Rb, Ro] LDRSB Rd, [Rb, Ro] 19 LDRSH Загрузка полуслова со знаком LDRSH Rd, [Rb, Ro] LDRSH Rd, [Rb, Ro] 20 LSR Логический сдвиг вправо LSR Rd, Rs MOVS Rd, Rd, LSR Rs 21 MOV Пересылка MOV Rd, #Offset8 MOVS Rd, #Offset8 22 MUL Умножение MUL Rd, Rs MULS Rd, Rs, Rd 23 MVN Пересылка с инверсией MVN Rd, Rs MVNS Rd, Rs 24 NEG Инверсия (побитовое "НЕ") NEG Rd, Rs RSBS Rd, Rs, #0 25 ORR Логическое "ИЛИ" ORR Rd, Rs ORRS Rd, Rd, Rs 26 POP Извлечение регистров из вершины стека POP {Rlist} LDMIA R13!, {Rlist} 27 PUSH Размещение регистров в вершине стека PUSH {Rlist} STMDB R13!, {Rlist} 28 ROR Циклический сдвиг вправо ROR Rd, Rs MOVS Rd, Rd, ROR Rs 29 SBC Вычитание с переносом SBC Rd, Rs SBCS Rd, Rd, Rs 30 STMIA Групповое сохранение регистров STMIA Rb!, {Rlist} STMIA Rb!, {Rlist} 31 STR Сохранение целого слова STR Rd, [Rb, Ro] STR Rd, [Rb, Ro] 32 STRB Сохранение байта STRB Rd, [Rb, Ro] STRB Rd, [Rb, Ro] 33 STRH Сохранение полуслова STRH Rd, [Rb, Ro] STRH Rd, [Rb, Ro] 34 SWI Программное прерывание SWI Value8 SWI Value8 35 SUB Вычитание SUB Rd, Rs, Rn SUBS Rd, Rs, Rn 36 TST Побитовая проверка TST Rd, Rs TST Rd, Rs
Rs - Регистр-источник (Register source)
Rd - Регистр-приёмник (Register destination)
Ro - Отступ (Register offset)
Rn - Второй аргумент в арифметических операциях
Rlist - Список регистров