Скриптовый отладчик в KEIL. Ассемблер А51, задание тестовых значений переменных при отладке програмы.

- прописные и строчные символы не различаются;
- существуют ограничения по работе с указателями и адресной арифметикой;
- не может использоваться для вызова процедур и функций отлаживаемой программы;
- не поддерживает определение структур;
Хотя данная статья написана применительно к ассемблеру А51 архитектуры MCS-51, данные приемы универсальны, и могут быть применены и к языку С, и к архитектуре ARM с минимальными переделками или без них.
Доступ к отладочным окнам появляется при переходе в режим отладки и может быть осуществлен или через кнопки отладочного ToolBar (выделены кнопки, о которых пойдет речь в статье),

или через появившиеся подменю в меню View:

Обычно задание тестовых значений переменных при отладке я производил с использованием директив условной компиляции, примерно так:
$SET (Debug)
; -------------------------------------------------------------------------
_VARS SEGMENT DATA
RSEG _VARS
AV_OVF_F: DS 1 ; Старший байт счетчика частоты
AV_Value: DS 4 ; буфер входных значений
AV_Value3 DATA AV_Value+3 ; for debug
AV_Value2 DATA AV_Value+2 ; for debug
AV_Value1 DATA AV_Value+1 ; for debug
AV_Value0 DATA AV_Value+0 ; for debug
; -------------------------------------------------------------------------
$IF (Debug) ; Отладочные значения регистров
MOV AV_Value3, #0x03 ; 4 байт
MOV AV_Value2, #0xD0 ; 3 байт
MOV AV_Value1, #0x80 ; 2 байт
MOV AV_Value0, #0x00 ; 1 байт
$ELSE
;-- Перенос данных измерений из входных регистров в рабочие ------------------;
MOV R0, #AV_Value ; Начальный адрес
MOV @R0, SBUF ; 1 байт
INC R0 ;
MOV @R0, TL0 ; 2 байт
INC R0 ;
MOV @R0, TH0 ; 3 байт
INC R0 ;
MOV @R0, AV_OVF_F ; 4 байт
$ENDIF
При частом изменении отладочных значений такое задание тестового значения переменных начинает напрягать. Ведь для того чтобы изменить его при таком способе отладки необходимо:
- Выйти из режима отладки;
- Вернуться к месту присвоения и изменить тестовое значение;
- Перекомпилировать программу с новыми значениями;
- Войти в режим отладки;
- Вернуться к нужному месту программы;
Для облегчения присвоения тестового значения можно использовать Configuration Wizard. Это псевдокод написанный в комментариях и оформленный спецтегами. При наличии такого кода в проекте появляется дополнительная вкладка «Configuration Wizard».
Для данной задачи его код будет выглядеть примерно так:
;- Мастер подстановки входных настроечных значений ---------------------------;
;- включен при DG_EnInVar EQU 1, используется только при отладке -------------;
; *** <<< Use Configuration Wizard in Context Menu >>> ***
;- Header --------------------------------------------------------------------;
; <h> Setting debugging of input variables
; <i> Setting debugging of input variables online
;- Debug input variables -----------------------------------------------------;
; <e> Enable debug input variables
; <i> Enable setting of input variables for debugging
DG_EnInVar EQU 1
; <o> Value+3: fourth byte <0x00-0xFF>
; <i> Fourth byte input register.
DG_Value_3 EQU 0x42 ;
; <o> Value+2: the third byte <0x00-0xFF>
; <i> The third byte input register.
DG_Value_2 EQU 0xEA ;
; <o> Value+1: second byte <0x00-0xFF>
; <i> Second byte input register.
DG_Value_1 EQU 0x58 ;
; <o> Value+0: first byte <0x00-0xFF>
; <i> First byte input register.
DG_Value_0 EQU 0x2F ;
; </e>
; </h>
; *** <<< end of configuration section >>> ***
С таким способом подстановки тестовых значений работать более удобно.

Отладочный код почти не изменился:
IF (DG_EnInVar = 1) ; Отладочные значения регистров
MOV AV_Value3, #DG_Value_3 ; 4 байт
MOV AV_Value2, #DG_Value_2 ; 3 байт
MOV AV_Value1, #DG_Value_1 ; 2 байт
MOV AV_Value0, #DG_Value_0 ; 1 байт
ELSE
;-- Перенос данных измерений из входных регистров в рабочие ------------------;
MOV R0, #AV_Value ; Начальный адрес
MOV @R0, SBUF ; 1 байт
INC R0 ;
MOV @R0, TL0 ; 2 байт
INC R0 ;
MOV @R0, TH0 ; 3 байт
INC R0 ;
MOV @R0, AV_OVF_F ; 4 байт
ENDIF
Однако главной задачи — значительно ускорить отладку, он не решает. Все равно требуется перекомпиляция проекта, а хотелось бы от нее избавиться. Вот здесь и может помочь скриптовый отладчик.
Он изменяет значения переменных внутри самой программы, поэтому перекомпиляции при изменении тестовых значений не требуется. Изменив тестовые значения через скриптовый отладчик, можно тут же продолжать работать далее. Отладка значительно облегчается и убыстряется.
Сначала уберем из программы старый отладочный код с условной компиляцией и создадим для отладчика собственную функцию, которая и будет перезаписывать тестовые значения.
Находясь в режиме отладки из меню Debug -> Function Editor (Open Ini File)... вызываем редактор функций. Он сразу откроет диалог открытия INI файла. Т.к. мы его только создаем, закроем диалог, нажмем кнопку New и Save as, сохраним под нужным именем (например Input.ini) в каталоге программы, и затем заново откроем его в редакторе функций. Теперь можно ввести свою функцию. Применительно к моему случаю у меня получилось так:
FUNC void Transfer (void) {
unsigned long int val;
val = getlong("Value, dec or hex(0x12345678)");
if (val == 0)
_break_ = 1;
else {
AV_Value3 = val>>24;
AV_Value2 = val>>16&0xff;
AV_Value1 = val>>8&0xff;
AV_Value0 = val&0xff;
printf("Value (dec) = %lu.\n", val);
}
}
Краткий комментарий:
- определим временную переменную типа DWord;
- с помощью встроенной функции getlong присвоим ей значение (введенное вручную число). Функция getlong вызывает стандартный InputBox в который нужно ввести значение в десятичном (по умолчанию) или шестнадцатиричном виде. Это значение (или 0, если число невалидно) и возвращается в качестве результата getlong (присваивается временной переменной);
- предусмотрим закрытие модального окна ввода с остановом программы при нулевом вводе;
- если ввод правильный далее DWord разделяется побайтно и присваивается входному буферу. Все символьные выражения программы, доступные отладчику можно увидеть набрав DIR в командной строке отладчика. В более развернутом виде их также можно посмотреть в окне View -> Symbols Window;
- последней для контроля введенного значения вызывается стандартная функция printf, она выведет в окно отладчика введенное число в десятичном виде;
Наша функция для отладчика готова, теперь осталось ее подключить. Сделать это можно двумя способами;
- В командной строке отладчика ввести INCLUDE Input.ini
- Ввести название файла в строке Initialization File на странице Debug настроек проекта.
Введем в командной строке отладчика DEFINE BUTTON «Input», «Transfer()». Этим мы создаем на ToolBox кнопку Input,

при нажатии она вызовет функцию Transfer().

На ToolBox всегда присутствует на одну кнопку больше, чем мы создали. Самая первая кнопка Update Windows создается автоматически и присутствует всегда. Нажатие на нее приводит к обновлению значений во всех окнах просмотра.
ToolBox имеет диалоговое окно которое располагается поверх всех других окон и иногда это мешает. Его можно закрыть, тогда доступ к кнопкам можно получить из меню, нажав стрелку вниз рядом с кнопкой ToolBox.

Начинаем пошаговую отладку с выявлением багов.
…
Ну вот, код программы мы почистили, и от багов как нам кажется избавились. Как теперь перейти от пошаговой отладки к более производительной проверке? Можно воспользоваться возможностью привязать срабатывание скриптов-функций к точкам останова.
На самом деле точка останова не обязательно останавливает программу. Это делают только стандартные точки, устанавливаемые мышкой. Они достигнув точки останова, вызывают стандартный обработчик, который и останавливает выполнение программы. Мы же можем определить свой обработчик, и он отработав требуемые действия, продолжит выполнение программы. Остановить программу можно и в этом случае, надо внутри функции-обработчика присвоить единицу внутренней переменной _break_.
Мне показалось удобным вывести полученный результат BCD преобразования в отладочную консоль. В основном коде выходной буфер BCD преобразования описан так:
AV_BCDBuffer: DS 10 ; Буфер неупакованных BCD (разрядов)
AV_BCDBuffer9 DATA AV_BCDBuffer+9 ; for debug
AV_BCDBuffer8 DATA AV_BCDBuffer+8 ; for debug
AV_BCDBuffer7 DATA AV_BCDBuffer+7 ; for debug
AV_BCDBuffer6 DATA AV_BCDBuffer+6 ; for debug
AV_BCDBuffer5 DATA AV_BCDBuffer+5 ; for debug
AV_BCDBuffer4 DATA AV_BCDBuffer+4 ; for debug
AV_BCDBuffer3 DATA AV_BCDBuffer+3 ; for debug
AV_BCDBuffer2 DATA AV_BCDBuffer+2 ; for debug
AV_BCDBuffer1 DATA AV_BCDBuffer+1 ; for debug
AV_BCDBuffer0 DATA AV_BCDBuffer+0 ; for debug
Функция вывода простая, выводит содержимое BCD буфера в виде 10-разрядного десятичного числа:
FUNC void BCD (void) {
printf("BCD = %d%d%d%d%d%d%d%d%d%d\n", AV_BCDBuffer9, AV_BCDBuffer8,
AV_BCDBuffer7, AV_BCDBuffer6, AV_BCDBuffer5, AV_BCDBuffer4,
AV_BCDBuffer3, AV_BCDBuffer2, AV_BCDBuffer1, AV_BCDBuffer0);
}
Т.к. введенное число приводится к десятичному виду и результат BCD преобразования — десятичное число, ввод и результат должны совпасть.
Контрольные функции готовы, осталось эти функции назначить точкам останова. Выбираем строку в программе где ввод уже должен быть произведен и ставим мышкой точку останова. Затем вызываем окно свойств (Debug -> Breakpoints...) и редактируем, присвоив свою функцию. Здесь в качестве выражения я указал адрес в коде из старой точки останова.

Keil спросит надо ли заменить старую точку останова, подтверждаем.

В окне редактирования показано, что функция установлена.

Теперь находим строку после BCD преобразования и ставим мышкой вторую точку останова, снова редактируем. Здесь в качестве выражения я указал номер строки в программе, также из старой точки останова. Для ассемблера эти записи равноценны, можно использовать любую.

Опять подтверждаем.

В окне редактирования показано, что у нас установлено две точки останова с сопоставленными функциями.

Открываем окно командной строки отладчика, если в нем есть какие либо записи очищаем его (правая кнопка -> Clear Window). Все готово для контрольной отладки, запускаем программу на непрерывное выполнение кнопкой Run. Программа запросит число, введем его, программа сделает бесконечный цикл и снова запросит число, а мы в это время можем оценить правильность работы, сравнив числа в командном окне. Продолжать можно до тех пор пока не убедитесь в правильности работы, или не увидите ошибочный вывод.
Вот например я ввел максимальное 32-битное число 0xFFFFFFFF и получил такой вывод отладчика:

Числа не совпадают, надо продолжать искать ошибки.
Выйти из бесконечного цикла можно введя 0 или пустую строку. Если нашли ошибку, снова введите контрольное значение кнопкой ввода на ToolBox и продолжайте пошаговую отладку. На время пошаговой отладки точки останова можно временно отключить зайдя в их свойства и сняв галки с нужных (ненужных). Если вы убедились в правильной работе программы, можно выйти из отладки. При этом учтите, привязка функций к точкам останова сохраняется только пока вы в отладчике. Если выйти из отладчика и снова зайти, точки останова привязанные функции потеряют.
Есть способ привязать точки останова «навечно» при отладке. Во время замены старых точек в окне отладчика появляются соответствующие команды (BreakSet). Для данного случая это BS C:0x037B,1,«Transfer()» и BS \413,1,«BCD()». Эти команды можно перенести в INI файл. При входе в отладчик, он каждый раз заново перекомпилирует подключаемый файл, точки будут подключены автоматом. Я не рекомендую так делать…
Такое подключение возможно только если вы не редактируете код программы (с последующей перекомпиляцией).
- вы временно закомментировали строку кода в программе. Из нумерации строк кода она пропала и ваша первая точка (C:0x037B) может стоять уже не там, где вы ее поставили;
- вы добавили/удалили комментарий (пробел) между строками в программе, код не изменился, первая точка на месте, зато вторая может указывать уже не на ту строку, где ставилась;
- вы добавили в программу команду, в этом случае могут уехать обе точки;
Скриптовый отладчик может работать только со встроенным симулятором Keil. Связано это с тем, что он должен иметь полный доступ к внутренней области памяти отлаживаемой программы. При применении внешнего симулятора, например Proteus VSM Simulator, отладочные файлы грузятся в него и это условие не выполняется. Внешний JTAG отладчик выполняет отладку в среде Keil, и хотя и не использует внутренний симулятор, может теоретически работать со скриптовым отладчиком, нужно проверять по документации или экспериментально.
Толчком к освоению такого способа отладки послужила статья Особенности отладки программ для микроконтроллеров семейства 8051 в среде Keil uVision.
P.S. Если вывод в консоль слишком длинный, или его хочется просмотреть не спеша, позже, можно продублировать его в log файл. Перед началом вывода наберите в консоли (внесите в Ini файл) команду log > .\input.log. В каталоге отлаживаемой программы будет создан файл input.log, и весь отладочный вывод будет продублирован в него. Если файл с таким именем уже был, он перезапишется. Если хочется добавить вывод в конец существующего, надо не создать, а открыть файл командой log >> .\input.log. Если такого файла не было, он будет создан. Закрывается log файл командой log off.
- +5
- 10 февраля 2016, 19:15
- anakost
Мы для подобных целей (вернее для автоматизации тестирования) использовали скрипты + GDB. Из скриптов скармливали команды на вход GDB, анализировали вывод GDB. Пришлось немного повозится, но в результате получился отличный механизм для автоматизации тестов.
Да, я для стм32 через Ini-файл тоже кнопочки делал иногда или скрипт для подключения к рабочему устройству без перезагрузки и перепрошивки, почему-то кейл такое не позволяет без определённой команды в окне команд. Память надо было так дампить, чтоб можно было устройство записью этой памяти и регистров привести к нужному зависшему состоянию если что, переменные прописывать и всякое такое. Пригождается редко, но бывает полезным.
- teplofizik
- 11 февраля 2016, 01:18
- ↓
Комментарии (8)
RSS свернуть / развернуть