Основной задачей было уменьшение размера программы, т.к. использовали микроконтроллер с небольшим объемом памяти, а функциональность изделия должна быть большой. По этому появилась идея использовать анализатор кода, поиск в интернете ничего не дал, по этому пришлось делать самостоятельно.
Решил поделиться идеями, так как думаю что может кто-то напишет более приличную программу для анализа программы на ассемблере 8051.
В этой статье опишу основные этапы получившегося анализатора. Часть этих этапов можно использовать для анализа программ написанных на других языках.
Этап 1. Сначала необходимо преобразовать исходный текст программы к максимально простому виду. С кодом программы из которого удалили все лишнее удобнее работать.
Пример:
Function_ADD:
mov A,Peremenay1
add A,#Constanta1
mov Peremenay2,A
приводим к виду
code_0001: mov A,020h
add A,#030h
mov 021h,A
Для этого решил сделать дизасемблирование из HEX-файла. Так это показалось гораздо удобнее, чем обрабатывать исходник, к тому же у скомпилированного файла уже код распределен по памяти, что пригодиться в дальнейшим.
Этап 2. Создал таблицу, в которую занес строки исходной программы, тип команды, тип операндов, адрес в памяти и т.д.
Пример:
Номер строки|Адрес перехода(если есть) | Название операции |Операнд 1| Операнд 2| Адрес перехода| Тип операнда 1 | Тип операнда 2|
1|code_0001|mov|A|020h| |ACC|DIR|
2| |add|A|030h| |ACC|CONST|
3| |mov|021h|A| |DIR|ACC|
Чем больше заполненных столбцов с различными возможными вариантами, тем будет проще делать анализ.
Этап 3. Переходим непосредственно к анализу. Пока сделал анализ двух соседних команд, алгоритм такого анализа довольно простой, по определенным правилам проверяем команды.
Пример:
1. Замена команд вызова процедур
LCALL code_0001
RET
можно заменить командой
LJMP code_0001
это даст экономию 1-го байта.
2. Замена команд присвоения
MOV A,#CONSTANTA
MOV @R0,A
можно заменить командой
MOV @R0, #CONSTANTA
или
MOV A,@R0
MOV RAM,A
можно заменить командой
MOV RAM,@R0
3. Замена команд перемещения
JNZ label1
SJMP label2
label1:
.................
label2:
можно заменить командой
JZ label2
label1:
..........
label2:
И таких вариантов достаточно много, когда пишется программа она не всегда пишется с учетом экономии места, а так чтобы было в дальнейшем понятно.
Этап 4. Посчитать сколько обращений по каждому адресу перехода, задача найти единичные обращения. Также смотрим, какая команда стоит перед адресом перехода в программе. На основе этих данный добавляем в таблицу дополнительный данные касательно переходов.
Пример:
MOV A,030h
MOV 020h,A
ret
code_0001: MOV A,020h
Значит возможно перенести часть программы начиная с адреса перехода "code_0001" ближе к команде обращения к этому адресу или вообще убрать команду перехода, а за место нее перенести часть программы.
Также есть нулевые обращения, это когда к определенному адресу можно попасть только через команду перехода, а ее нет в программе, например как в примере, перед командой с адресом "code_0001" стоит команда «RET».
Этап 5. Посчитать сколько раз используется каждая ячейка памяти данных (ОЗУ), и как используется (исходные данные для команды или изменение данных в ячейки).
Например может оказаться, что ячейку памяти только изменяют, при этом ее нигде не используют как исходные данные. Можно избавиться от команд которые изменяют такую ячейку.
Этап 6. Компиляция исходного кода. После компиляции возможно проверить количество байт между командой перехода и адресом перехода, например для того чтобы сместить часть кода поближе к команде перехода, заменив ее с LJMP на SJMP.
При использование такого анализа получил результат, что 8-ми килобайтную программу удалось уменьшить на 300 байт.
Дальнейшие планы, сделать анализ кода на основе его функционирования, т.е. что делает определенная часть программы и как ее можно заменить.