Создание программы
Программа на Ассемблере

Создание программы

Сегодня необычно создавать автономную программу, полностью написанную на языке

ассемблера. Сборка обычно используется для ключевых определенных критических

под программ. Почему? это много проще программировать на языке более высокого уровня,

чем на ассемблере. Кроме того, использование сборки затрудняет перенос программы на

другие платформы. На самом деле сборку вообще использовать редко.

Итак, зачем вообще кому-то изучать сборку?

 

1. Иногда код, написанный на ассемблере, может быть быстрее и меньше кода,

созданного компилятором.

2. Сборка обеспечивает доступ к прямым аппаратным функциям системы, которые могут быть

трудными или невозможными для использования с языка более высокого уровня.

3. Обучение программированию на сборке помогает получить более глубокое понимание

того, как работают компьютеры.

4. Изучение программирования на ассемблере помогает лучше понять, как работают

компиляторы и языки высокого уровня, такие как C.

Эти последние два пункта демонстрируют, что обучающая сборка может быть полезна, даже если впоследствии никто не будет программировать на ней. На самом деле автор редко программирует на ассемблере, но каждый день использует идеи, которые он извлек из нее.

Первая программа. Пример сборки

Формат исходного кода

Типичная строка в программе на языке ассемблера может быть следующей:

LOOP: MOV.B r0, # 80; инициализировать счетчик

Эта строка будет собрана в одну инструкцию (в данном случае 11 0000 1000 0000 в двоичном формате или 3080); язык ассемблера и машинный код соответствуют друг другу.

Он состоит из четырех частей; метка, мнемоника, операнд, комментарий; не все присутствуют в каждой строке.

Первая часть (в данном примере LOOP) – это метка ; это слово, придуманное программистом, которое определяет эту точку в программе. Он будет установлен равным значению адреса, где хранится эта инструкция. Так, например, если позже в программе есть оператор JMP LOOP, программа на ассемблере заменит метку LOOP фактическим значением LOOP, которое является адресом, по которому хранится эта инструкция. (Чтобы ассемблер распознал это как метку, метка должна начинаться с первого символа в строке., В некоторых ассемблерах двоеточие “:” следует за меткой) Итак, если адрес, по которому хранится инструкция, – 4F, LOOP принимает значение 4F. Если LOOP используется позже в программе, ассемблер присвоит ему значение 4F.

Вторая часть – мнемоника. Она соответствует конкретному виду инструкции (код операции, отправляемый Dispatch Unit ). Цель состоит в том, чтобы слово, выбранное (производителями) для мнемоники, было легко запомнить и указывало на то, что делает инструкция. В этом случае инструкция перемещает буквальное значение (один байт) в регистр 0, следовательно, MOV.B r0, # 80

Третья часть строки – это операнд (их может быть два); в этом случае операндом является значение 80 (в шестнадцатеричном формате) и регистр r0.

Последняя часть строки – комментарий . Это вообще не влияет на фактическую инструкцию ; он не входит в состав инструкции и не собирается; вместо этого он помогает программисту запомнить, что делает эта часть программы. Комментарий предваряется точкой с запятой.

Когда вы пишете программу на языке ассемблера, она фактически состоит из множества символов ASCII; это будет храниться в файле и называться « исходный код» . Затем это формирует входные данные для программы, называемой «ассемблер» (MPASM для PIC), которая может переводить «исходный код» в машинный код . или объектный код. Когда ассемблер выполнит свою работу, эта строка исходного кода будет преобразована в двоичный шаблон, связанный с конкретным адресом в памяти программы.

Режимы адресации

Какие существуют способы указать, откуда поступают данные (чтение) или куда они собираются (запись)? Мы уже видели два из них:

Непосредственные данные фактически указываются в инструкции; эти данные являются фиксированной частью программы.

Прямая адресация – это то, где указывается адрес данных (то есть адрес регистра). Таким образом

MOV.B r0 # 0F; немедленные данные: значение F
MOV.B r0 0F; прямая адресация: адрес F

Во втором примере 0F – это не данные, а адрес, по которому они будут отправляться.

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

MOV.B r0, (r1)

копирует значение r1 как адрес и использует этот адрес как адрес источника в памяти данных, которые должны быть помещены в r0.

Зачем нужно это делать? Ну, например, предположим, что набор символов ASCII был сохранен в последовательных регистрах, начиная с 0C; тогда может пригодиться следующий код:

MOV r0, # 0C; загрузить базовый адрес строки в r0
LOAD: MOV r1, (r0); загрузить содержимое в r1
CALL PRINT; вызвать процедуру печати для печати символа в r1
INC r0; указать на следующий символ
JMP LOAD; загрузить следующий символ

Таким образом, мы могли бы использовать этот набор инструкций для распечатки списка символов. (На практике нам нужен способ остановить цикл!)

Более сложные процессоры имеют гораздо более полный набор «режимов адресации» и обычно позволяют добавлять смещение к значению индекса (r0 выше) перед его использованием в качестве адреса. Однако базовый набор прямых, прямых и косвенных методов очень эффективен.

Инструкции по регистру байтового файла

Многие инструкции работают с восемью битами данных, хранящихся в одном из регистров. Мы уже рассмотрели инструкции MOV, которые это делают. Например, MOV r0, r1 берет содержимое файлового регистра r1 и копирует его в r0.

Рассмотрим сначала те инструкции, как MOVF, входными данными которых являются данные, хранящиеся в одном файловом регистре, и оставьте на потом те инструкции, которые также принимают содержимое W.

В любом случае ответ можно записать в W (d = 0) или обратно в f (d = 1); если ответ записан в W, то содержимое f остается неизменным.

AND.B r0, # 0; дает ответ 0

XOR.B r0, # FF; инвертирует каждый бит

DEC.B r0; уменьшает число на 1

INC.B r0; увеличивает число на 1

В каждом случае операция выполняется только с одним байтом; и что иногда затрагиваются биты регистра состояния (например, Z).

Вторая группа этих инструкций принимает слово в качестве входных данных:

ADD.W r0, r1; r0 + r1

SUB.W r0, r1; r0 – r1

Логические операции: каждая из них работает с каждым битом входных данных.

AND.W r0, r1; каждый бит является результатом операции И двух битов

OR.W r0, r1; каждый бит является результатом операции ИЛИ двух бит

XOR.W r0, r1; каждый бит является результатом операции XOR для двух бит

 


Директивы языка ассемблера

 

Есть еще несколько основных идей, которые необходимо рассмотреть, прежде чем вы сможете написать полную программу на языке ассемблера, хотя вам, вероятно, это не понадобится, пока вы действительно не придете к написанию реальной программы. Одна из важных идей – это «директивы ассемблера». Это операторы, которые являются частью исходного кода, используемого ассемблером, но не соответствуют фактическим инструкциям . Я расскажу о директивах ORG, EQU, END, INCLUDE, PROCESSOR и RADIX. Полный список приведен в руководстве пользователя MPASM.

ПРОЦЕССОР

Оператор PROCESSOR 16C71 явно не является частью программы; 16C71 не нужно объяснять, что это за процессор! Дело в том, что программе ассемблера MPASM действительно нужно знать: она может иметь дело с множеством различных процессоров в диапазоне PIC, поэтому вы должны сообщить ей, какой из них вы используете. Это очень наглядный пример директивы ассемблера, которая не транслируется в реальный код, но влияет на способ, которым транслируется код.

RADIX

Например, RADIX HEX. Это сообщает ассемблеру, что, если вы не укажете иное, числа будут в шестнадцатеричном формате. Другие возможности – DEC и OCT для десятичных и восьмеричных чисел. Обратите внимание, что в программе вы все равно можете использовать другую систему счисления: например, вы можете сказать B’10 ‘, что будет интерпретировано как двоичное число 10, то есть 210; если вы просто написали 10, он интерпретировал бы его как шестнадцатеричное число 10, то есть 1610. В приведенных мной примерах я всегда буду предполагать, что числа находятся в шестнадцатеричном формате и явно указывать, когда они находятся в двоичном или десятичном формате. Иногда стоит поставить 0 впереди, чтобы было совершенно ясно, что 0FF – это число, а не метка «FF».

ORG

Директива ORG не переводится ни в какой код; он устанавливает адрес, по которому будет сохранена следующая инструкция.

Пример:

ORG 0
GOTO 10

Инструкция GOTO 10
транслируется в машинный код 10 1000 0001 0000.

Но он будет размещен по адресу 0. ORG 0 не транслируется ни в какой код; но это влияет на расположение кода для GOTO 10.

EQU

Пример:
START EQU 10

Директива EQU расшифровывается как «равно»; он позволяет присвоить метке числовое значение. В этом примере ассемблер всегда заменяет START на 1016. Следовательно, вы можете написать:

НАЧАТЬ УРОВЕНЬ 10
ОРГ НАЧАТЬ НАЧАТЬ
10

Ассемблер переведет START как 10 и загрузит код по цене 10 долларов, как и раньше. Использование меток часто упрощает понимание и изменение программы.

Например, легче запомнить имена регистров специальных функций, чем их адреса.

Пример

PCL EQU 2; младший байт счетчика программ
STATUS EQU 3; регистр состояния
PORTA EQU 5; Порт A
PORTB EQU 6; Порт B

Также легче запомнить имена, чем адреса файловых регистров общего назначения. Предположим, вы хотите использовать один из них как счетчик; тогда вы могли бы определить

COUNT EQU 0C; счетчик

а затем инструкция вроде

DECF COUNT; уменьшение счетчика

легче понять.

ВКЛЮЧАЮТ

Это полезная директива, особенно когда у вас есть ряд стандартных определений, с которых вы всегда хотите начинать свои программы; там может быть оператор PROCESSOR, оператор RADIX и ряд операторов EQU, определяющих все стандартные регистры специальных функций. Если вы однажды написали их в файле ассемблера, например, PIC16C71.H, то оператор

INCLUDE PIC16C71.H
добавит эти стандартные определения без необходимости повторять их.

КОНЕЦ

Это не конец программы, выполняемой PIC!
Такие программы обычно представляют собой бесконечные циклы; как только PIC включен, он работает в цикле навсегда. Вместо этого END – это инструкция или директива ассемблеру, говорящая: «Это конец моей программы; теперь вы можете закончить!»

 

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Пролистать наверх