Ну вот ты решил делать прошивку, как все-таки организовать программу, чтобы она легко читалась и была возможность ее расширять? В этом плане лучше всего разделить ее на части (модули), отвечающие за отдельные функции системы/периферию. Рассмотрим пример:
Что здесь важно: программа начинает выполняться с метки Reset (адрес 0x0000), далее перескакивает на метку Init. Сегменте кода между rjmp Init и. самой меткой Init располагается таблица векторов прерываний (int_vectors.asm) и определяются подпрограммы их обработки (int_routines.asm). Так вот если в нашей программе используются прерывания, мы должны определить таблицу прерываний и сами обработчики прерываний/или поставить заглушки на них. Если мы этого не сделаем и разрешим прерывания, программа будет глючить и вот почему: в каждом контроллере есть прерывания и зарезервированы адреса, куда передается управление программы в случае возникновения оных. Эти адреса располагаются в самом начале сегмента кода за адресом 0x0000. То есть, если разрешены прерывания и не определены обработчики в положенном месте, то в случае прерывания управление передастся по вектору прерывания а там будет не обработчик, а просто какой-нибудь код.
В чипе AtMega32 определены следующие векторы прерываний:
Видим, что по адресу 0x0002 располагается точка перехода по внешнему прерыванию. Теперь включим эти векторы прерываний в тело программы и посмотрим листинг.
По адресам 0x0002 и 0x0004 расположены заглушки для внешних прерываний. Теперь рассмотрим программу без определения таблицы прерываний.
Что ты видишь? теперь по адресам прерываний 0x0002 и 0x0004 расположены команды загрузки в регистры ввода/вывода SPH и SPL регистра общего назначения r16. При этом значение r16 не определяется. Теперь рассмотрим ситуацию, что мы не определили векторы прерываний и их обработчики, глобально разрешили прерывания и получили аппаратно INT0 или INT1. Программа прекратит свое выполнение в перейдет по адресу прерывания, где ожидает обработчик, но там его нет. В итоге указатель на стек изменится и программа словит глюк, который трудноуловим.
За определением векторов прерываний идут определения процедур их обработки, ставим их поближе к началу кода, чтобы перехода rjmp хватало.
После этого инициализируем стек, он нужен при вызове подпрограммы, в него помещается адрес инструкции возврата из подпрограммы.
Далее за меткой MainLoop идет собственно основной цикл приложения.
В конце программы, за rjmp MainLoop, помещаем подпрограммы общего характера, такие как работа с периферией или задержками, вычисление функций и прочего.
Собственно все.
В процессе сборки ассемблер сперва создает единый файл, подставляя вместо .include текст включаемого файла, затем продолжает сборку.