Как измерить время с момента старта устройства
В предыдущей статье мы рассказали о новом расширении системы команд «Эверест». Теперь поведаем о том, как мы реализовали это расширение на языке описания аппаратуры. Ниже показан результат работы шестого пункта меню — печать количества тактов с момента старта устройства:
Алгоритм работы довольно простой — при нажатии на клавишу «цифра шесть» на терминал выводится время, полученное преобразованием данных из счётчика тактов. Для решения этой задачи были использованы последние расширения системы команд.
Для удобства работы с кодом, с помощью имен описываем индексы локальных переменных. Для этого используется директива equ — объявление константы. Эти индексы будут использоваться при адресации локальных переменных:
$return_address equ 0 ; Позиция адреса возврата $seconds_count_lo equ 1 ; Количество секунд с момента старта устройства $seconds equ 2 ; Количество секунд $minutes equ 3 ; Количество минут $hours equ 4 ; Количество часов
Затем описывам функцию и её пролог. Для этого используем по соглашению регистр R14 как указатель на вершину стека. Два декремента в начале функции резервируют в стеке 6 машинных слов — одно под адрес возврата и пять для внутренних переменных. Не судите строго — такое формирование пролога функции это временное решение, которое при возникновении необходимости будет заменено чем либо более подходящим:
function show_uptime dec r14, 12 ; Освобождение места на стеке для локальных переменных dec r14, 12 ; Освобождение места на стеке для локальных переменных mov (r14), r15 ; Сохранение адрес возврата из функции
Затем используем счётик тактов, чтобы вывести его информацию о его состоянии в двух 32-х битных словах:
lea r1, $tick_str call _puts call _get_sysclock push r0 mov r0, r1 call _print_hex pop r0 call _print_hex
Чтобы не усложнять код, ещё раз читаем счётчик тактов. Сразу же переводим данные счётчика в секунды и сохраняем в локальной переменной seconds_count_lo.
call _get_sysclock ; ну и что mov r2, 100000000 call _div64 mov r14[$seconds_count_lo], r0
В этом месте можно расслабиться и вывести сообщение, не заботясь о дополнительном сохранении регистров:
lea r1, $is_str call _puts mov r0, r14[$seconds_count_lo] call _print_dec lea r1, $sec_mid_str call _puts
Далее следует магия перевода секунд в дни, часы, минуты и… секунды. На самом деле никакой магии тут нет — простая «сортириовка» остатков от деления:
mov r0, r14[$seconds_count_lo] xor r1, r1 mov r2, 60 call _div64 mov r14[$seconds], R2 xor r1, r1 mov r2, 60 call _div64 mov r14[$minutes], R2 xor r1, r1 mov r2, 24 call _div64 mov r14[$hours], R2
В этом месте, после исполнения вышеприведённого фрагмента кода, в регистре R0 находится количество дней, прошедших с момента старта прошивки. Часы, минуты и секунды сохранены в памяти в виде локальных переменных, размещённых на стеке.
Далее идёт вывод полученных результатов. Обратите внимание, что инструкция чтения памяти с проверкой флагов используется для исключения из вывода пустых минут и часов. Собственно, это и есть тестируемое расширение системы команд:
or r0, r0 je skip_days call _print_dec lea r1, $days_str call _puts skip_days: movt r0, r14[$hours] je skip_hours call _print_dec lea r1, $hours_str call _puts skip_hours: movt r0, r14[$minutes] je skip_minutes call _print_dec lea r1, $min_str call _puts skip_minutes: mov r0, r14[$seconds] call _print_dec lea r1, $sec_fin_str call _puts
Выход из функции (эпилог), затем объявление констант и подключение библиотечных функций:
mov r15, (r14) inc r14, 12 inc r14, 12 return end $tick_str db 'Tick ',0 $is_str db ' is ',0 $days_str db ' days ',0 $hours_str db ' hours ',0 $min_str db ' min ',0 $sec_mid_str db ' seconds = ',0 $sec_fin_str db ' sec',13,10,0 include ../lib/tty.asm include ../lib/div.asm include ../lib/sysclock.asm include ../lib/print_dec.asm
Да, этот код нельзя назвать образцовым и можно потратить много времени на его улучшение, но свою задачу он выполнил дважды — помог отладить новые возможности и послужил материалом для этой статьи. Размер сгенерированного бинарного файла составил 660 байт из которых 272 байта составил вышеприведённый код и 388 байт библиотечные функции — деления и работы с терминалом.
Если до прочтения этой статьи вы писали на каком-то ассемблере, то, скорее всего, сейчас вы знаете ещё один ассемблер. При желании самостоятельно проверить или исправить этот пример, вы можете воспользоваться новой прошивкой нашего устройства для платы Марсоход2 — в следующем архиве вы найдёте прошиву, макроассемблер, несколько библиотечных функций и примеров программ.
Загрузить архив со свежей прошивкой для Марсохода2 и новой версией МакроАссемблера для платформ Windows и Linux (ZIP-архив 159 Кб)
Оставить комментарий