Primula C Compiler     Xameleon Project        |        In English

Как измерить время с момента старта устройства

В предыдущей статье мы рассказали о новом расширении системы команд «Эверест». Теперь поведаем о том, как мы реализовали это расширение на языке описания аппаратуры. Ниже показан результат работы шестого пункта меню — печать количества тактов с момента старта устройства:

uptime_20150608

Алгоритм работы довольно простой — при нажатии на клавишу «цифра шесть» на терминал выводится время, полученное преобразованием данных из счётчика тактов. Для решения этой задачи были использованы последние расширения системы команд.

Для удобства работы с кодом, с помощью имен описываем индексы локальных переменных. Для этого используется директива 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 Кб)

 


Оставить комментарий