Синтаксис ассемблера. Метки. Программирование в машинных кодах и на языке ассемблера. NOLIST – Выключить генерацию листинга

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

Компилятор поддерживает ряд директив. Директивы не транслируются непосредственно в код. Вместо этого они используются для указания положения в программной памяти, определœения макросов, инициализации памяти и т.д. Все директивы предваряются точкой.

Список директив приведён в следующей таблице.

Директива Описание
BYTE Зарезервировать байты в ОЗУ
CSEG Программный сегмент
DB Определить байты во флэш или EEPROM
DEF Назначить регистру символическое имя
DEVICE Определить устройство для которого компилируется программа
DSEG Сегмент данных
DW Определить слова во флэш или EEPROM
ENDM, ENDMACRO Конец макроса
EQU Установить постоянное выражение
ESEG Сегмент EEPROM
EXIT Выйти из файла
INCLUDE Вложить другой файл
LIST Включить генерацию листинга
LISTMAC Включить разворачивание макросов в листинге
MACRO Начало макроса
NOLIST Выключить генерацию листинга
ORG Установить положение в сегменте
SET Установить переменный символический эквивалент выражения

BYTE - зарезервировать байты в ОЗУ. Директива BYTE резервирует байты в ОЗУ. В случае если вы хотите иметь возможность ссылаться на выделœенную область памяти, то директива BYTE должна быть предварена меткой. Директива принимает один обязательный параметр, который указывает количество выделяемых байт. Эта директива может использоваться только в сегменте данных(смотреть директивы CSEG и DSEG). Выделœенные байты не инициализируются.

Синтаксис:

МЕТКА: .BYTE выражение

DSEG var1: .BYTE 1 ; резервирует 1 байт для var1

table: .BYTE tab_size ; резервирует tab_size байт

Ldi r30,low(var1) ; Загружает младший байт регистра Z

Ldi r31,high(var1) ; Загружает старший байт регистра Z

Ld r1,Z ; Загружает var1 в регистр 1

DB - определить байты во флэш или EEPROM. Директива DB резервирует крайне важно е количество байт в памяти программ или в EEPROM. В случае если вы хотите иметь возможность ссылаться на выделœенную область памяти, то директива DB должна быть предварена меткой. Директива DB должна иметь хотя бы один параметр.
Размещено на реф.рф

Параметры, передаваемые директиве - это последовательность выражений разделённых запятыми. Каждое выражение должно быть или числом в диапазоне (-128..255), или в результате вычисления должно давать результат в данном же диапазоне, в противном случае число усекается до байта͵ причём БЕЗ выдачи предупреждений.

В случае если директива получает более одного параметра и текущим является программный сегмент, то параметры упаковываются в слова (первый параметр - младший байт). В случае если число параметров нечётно, то последнее выражение будет усечено до байта и записано как слово со старшим байтом равным нулю, даже если далее идет ещё одна директива DB.

Синтаксис:

МЕТКА: .DB список_выражений

CSEG consts: .DB 0, 255, 0b01010101, -128, 0xaa

const2: .DB 1,2,3

DW - определить слова во флэш или EEPROM. Директива DW резервирует крайне важно е количество слов в памяти программ или в EEPROM. В случае если вы хотите иметь возможность ссылаться на выделœенную область памяти, то директива DW должна быть предварена меткой. Директива DW должна иметь хотя бы один параметр.
Размещено на реф.рф
Данная директива должна быть размещена только в сегменте программ (CSEG) или в сегменте EEPROM (ESEG).

Параметры, передаваемые директиве, - это последовательность выражений разделённых запятыми. Каждое выражение должно быть или числом в диапазоне (-32768..65535), или в результате вычисления должно давать результат в данном же диапазоне, в противном случае число усекается до слова, причем БЕЗ выдачи предупреждений.

Синтаксис:

МЕТКА: .DW expressionlist

varlist:═ .DW 0, 0xffff, 0b1001110001010101, -32768, 65535

eevarlst: .DW 0,0xffff,10

· Сегменты

DSEG - сегмент данных. Директива DSEG определяет начало сегмента данных. Исходный файл может состоять из нескольких сегментов данных, которые объединяются в один сегмент при компиляции. Сегмент данных обычно состоит только из директив BYTE и меток. Сегменты данных имеют свои собственные побайтные счётчики положения. Директива ORG должна быть использована для размещения переменных в крайне важно м месте ОЗУ. Директива не имеет параметров.

Синтаксис:

ldi r30,low(var1) ; Загрузить младший байт регистра Z

ldi r31,high(var1) ; Загрузить старший байт регистра Z

ld r1,Z ; Загрузить var1 в регистр r1

CSEG - программный сегмент. Директива CSEG определяет начало программного сегмента. Исходный файл может состоять из нескольких программных сегментов, которые объединяются в один программный сегмент при компиляции. Программный сегмент является сегментом по умолчанию. Программные сегменты имеют свои собственные счётчики положения, которые считают не побайтно, а по словно. Директива ORG должна быть использована для размещения кода и констант в крайне важно м месте сегмента. Директива CSEG не имеет параметров.

Синтаксис:

DSEG ; Начало сегмента данных

vartab: .BYTE 4 ; Резервирует 4 байта в ОЗУ

CSEG ; Начало кодового сегмента

const: .DW 2 ; Разместить константу 0x0002 в памяти программ

mov r1,r0 ; Выполнить действия

ESEG - сегмент EEPROM. Директива ESEG определяет начало сегмента EEPROM. Исходный файл может состоять из нескольких сегментов EEPROM, которые объединяются в один сегмент при компиляции. Сегмент EEPROM обычно состоит только из директив DB, DW и меток. Сегменты EEPROM имеют свои собственные побайтные счётчики положения. Директива ORG должна быть использована для размещения переменных в крайне важно м месте EEPROM. Директива не имеет параметров.

Синтаксис:

DSEG ; Начало сегмента данных

var1: .BYTE 1 ; зарезервировать 1 байт для var1

table: .BYTE tab_size ; зарезервировать tab_size байт.

eevar1: .DW 0xffff ; проинициализировать 1 слово в EEPROM

ORG - Установить положение в сегменте. Директива ORG устанавливает счётчик положения равным заданной величинœе, которая передаётся как параметр.
Размещено на реф.рф
Важно заметить, что для сегмента данных она устанавливает счётчик положения в SRAM (ОЗУ), для сегмента программ это программный счётчик, а для сегмента EEPROM это положение в EEPROM. В случае если директиве предшествует метка (в той же строке) то метка размещается по адресу указанному в параметре директивы. Перед началом компиляции программный счётчик и счётчик EEPROM равны нулю, а счётчик ОЗУ равен 32 (поскольку адреса 0-31 заняты регистрами). Обратите внимание что для ОЗУ и EEPROM используются побайтные счётчики а для программного сегмента - пословный.

Синтаксис:

ORG выражение

DSEG ; Начало сегмента данных

ORG 0x37 ; Установить адрес SRAM равным 0x37

variable: .BYTE 1 ; Зарезервировать байт по адресу 0x37H

CSEG .ORG 0x10 ; Установить программный счётчик равным 0x10

mov r0,r1 ; Данная команда будет размещена по адресу 0x10

Синтаксис:

· Макросы

MACRO - начало макроса. С директивы MACRO начинается определœение макроса. В качестве параметра директиве передаётся имя макроса. При встрече имени макроса позднее в тексте программы компилятор заменяет это имя на тело макроса. Макрос может иметь до 10 параметров, к которым в его телœе обращаются через @0-@9. При вызове параметры перечисляются через запятые. Определœение макроса заканчивается директивой ENDMACRO.

По умолчанию в листинг включается только вызов макроса, для разворачивания макроса крайне важно использовать директиву LISTMAC. Макрос в листинге показывается знаком +.

Синтаксис:

MACRO макроимя

MACRO SUBI16 ; Начало макроопределœения

subi @1,low(@0) ; Вычесть младший байт параметра 0 из параметра 1

sbci @2,high(@0) ; Вычесть старший байт параметра 0 из параметра 2

CSEG ; Начало программного сегмента

SUBI16 0x1234,r16,r17 ; Вычесть 0x1234 из пары r17:r16

ENDMACRO - конец макроса. Директива определяет конец макроопределœения, и не принимает никаких параметров. Для информации по определœению макросов смотрите директиву MACRO.

Синтаксис:

MACRO SUBI16 ; Начало определœения макроса

subi r16,low(@0) ; Вычесть младший байт первого параметра

sbci r17,high(@0) ; Вычесть старший байт первого параметра

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

Синтаксис:

MACRO MACX ; Определœение макроса

add r0,@0 ; Тело макроса

ENDMACRO ; Конец макроопределœения

LISTMAC ; Включить разворачивание макросов

MACX r2,r1 ; Вызов макроса (в листинге будет показано тело макроса)

· Выражения

EQU - установить постоянное выражение. Директива EQU присваивает метке значение. Эта метка может позднее использоваться в выражениях. Метка которой присвоено значение данной директивой не должна быть переназначена и её значение не должна быть изменено.

Синтаксис:

EQU метка = выражение

EQU io_offset = 0x23

EQU porta = io_offset + 2

CSEG ; Начало сегмента данных

clr r2 ; Очистить регистр r2

SET - Установить переменный символический эквивалент выражения. Директива SET присваивает имени неĸᴏᴛᴏᴩᴏᴇ значение. Это имя позднее должна быть использовано в выражениях. Причем в отличие от директивы EQU значение имени должна быть изменено другой директивой SET.

Синтаксис:

SET имя = выражение

SET io_offset = 0x23

SET porta = io_offset + 2

CSE ; Начало кодового сегмента

clr r2 ; Очистить регистр 2

out porta,r2 ; Записать в порт A

DEF - назначить регистру символическое имя. Директива DEF позволяет ссылаться на регистр через неĸᴏᴛᴏᴩᴏᴇ символическое имя. Назначенное имя может использоваться во всœей нижеследующей части программы для обращений к данному регистру. Регистр может иметь несколько различных имен. Символическое имя должна быть переназначено позднее в программе.

Синтаксис:

DEF Символическое_имя = Регистр

ldi temp,0xf0 ; Загрузить 0xf0 в регистр temp (R16)

eor temp,ior ; Регистры temp и ior складываются по исключающему или

DEVICE - определить устройство. Директива DEVICE позволяет указать, для какого устройства компилируется программа. При использовании данной директивы компилятор выдаст предупреждение, в случае если будет найдена инструкция, которую не поддерживает данный микроконтроллер.
Размещено на реф.рф
Также будет выдано предупреждение, в случае если программный сегмент, либо сегмент EEPROM превысят размер, допускаемый устройством. В случае если же директива не используется, то всœе инструкции считаются допустимыми, и отсутствуют ограничения на размер сегментов.

Синтаксис:

DEVICE AT90S1200 | AT90S2313 | AT90S2323 | AT90S2333 | AT90S2343 | AT90S4414 | AT90S4433 | AT90S4434 | AT90S8515 | AT90S8534 | AT90S8535 | ATtiny11 | ATtiny12 | ATtiny22 | ATmega603 | ATmega103

DEVICE AT90S1200 ; Используется AT90S1200

push r30 ; инструкция вызовет предупреждение, AT90S1200 её не имеет

EXIT - выйти из файла. Встретив директиву EXIT, компилятор прекращает компиляцию данного файла. В случае если директива использована во вложенном файле (см. директиву INCLUDE), то компиляция продолжается со строки следующей после директивы INCLUDE. В случае если же файл не является вложенным, то компиляция прекращается.

Синтаксис:

EXIT ; Выйти из данного файла

INCLUDE - вложить другой файл. Встретив директиву INCLUDE компилятор открывает указанный в ней файл, компилирует его пока файл не закончится или не встретится директива EXIT, после этого продолжает компиляцию начального файла со строки следующей за директивой INCLUDE. Вложенный файл может также содержать директивы INCLUDE.

Синтаксис:

INCLUDE "имя_файла"

; файл iodefs.asm:

EQU sreg = 0x3f ; Регистр статуса

EQU sphigh = 0x3e ; Старший байт указателя стека

EQU splow = 0x3d ; Младший байт указателя стека

; файл incdemo.asm

INCLUDE iodefs.asm ; Вложить определœения портов

· Листинги

LIST - включить генерацию листинга. Директива LIST указывает компилятору на крайне важно сть создания листинга. Листинг представляет из себя комбинацию ассемблерного кода, адресов и кодов операций. По умолчанию генерация листинга включена, однако данная директива используется совместно с директивой NOLIST для получения листингов отдельных частей исходных файлов.

Синтаксис: .LIST

NOLIST - выключить генерацию листинга. Директива NOLIST указывает компилятору на крайне важно сть прекращения генерации листинга. Листинг представляет из себя комбинацию ассемблерного кода, адресов и кодов операций. По умолчанию генерация листинга включена, однако должна быть отключена данной директивой. Кроме того данная директива должна быть использована совместно с директивой LIST для получения листингов отдельных частей исходных файлов

Синтаксис: .NOLIST

Пример: .NOLIST ; Отключить генерацию листинга

INCLUDE "macro.inc" ; Вложенные файлы не будут

INCLUDE "const.def" ; отображены в листинге

LIST ; Включить генерацию листинга

Директивы ассемблера - понятие и виды. Классификация и особенности категории "Директивы ассемблера" 2017, 2018.

Здесь представлена информация по ассемблеру всей серии AVR, т.к. все микроконтроллеры этой серии программно совместимы.
Ассемблер – это инструмент, с помощью которого создаётся программа для микроконтроллера. Ассемблер транслирует ассемблируемый исходный код программы в объектный код, который может использоваться в симуляторах или эмуляторах AVR. Также ассемблер генерирует код, который может быть непосредственно введен в программную память микроконтроллера.
При работе с ассемблером нет никакой необходимости в непосредственном соединении с микроконтроллером.

Исходный файл, с которым работает ассемблер, должен содержать мнемоники, директивы и метки.
Перед каждой строкой программы можно ставить метку, которая является алфавитно-цифровой строкой, заканчивающейся двоеточием. Метки используются как указания для безусловного перехода и команд условного перехода.

Строка программы может быть в одной из четырёх форм:

[ Метка:] директива [операнды] [Комментарий]
[ Метка:] команда [операнды] [Комментарий]
Комментарий
Пустая строка

Комментарий имеет следующую форму:

Таким образом любой текст после символа “ ; ” игнорируется ассемблером и имеет значение только для пользователя.

Операнды можно задавать в различных форматах:

Десятичный (по умолчанию): 10,255
- шестнадцатеричный (два способа): 0x0а, $0а
- двоичный: 0b00001010, 0b11111111
- восьмеричный (впереди ноль): 010, 077

2 Система команд

Система команд микроконтроллеров ATMEL семейства AVR очень большая и в то же время эффективная. Одной из отличительных особенностей микроконтроллеров AVR является то, что почти все команды выполняются за 1 тактовый цикл. Исключение составляют команды перехода. Это существенно увеличивает производительность микроконтроллера даже при относительно невысокой тактовой частоте.

Все команды можно классифицировать на 5 типов:
1. арифметические команды;
2. логические команды;
3. команды перехода;

  • команды передачи данных;
  • побитовые команды и команды тестирования битов.

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

Ассемблер поддерживает множество директив. Директивы не транслируются непосредственно в коды операции. Напротив, они используются, чтобы корректировать местоположение программы в памяти, определять макрокоманды, инициализировать память и так далее. То есть это указания самому ассемблеру, а не команды микроконтроллера.
Все директивы ассемблера приведены в табл. 1.2.

Таблица 1.2.
Директивы ассемблера


Директива

Описание

Зарезервировать байт под переменную

Сегмент кодов

Задать постоянным(и) байт(ы) в памяти

Задать символическое имя регистру

Задать для какого типа микроконтроллера компилировать

Сегмент данных

Задать постоянное(ые) слово(а) в памяти

Сегмент EEPROM

Выход из файла

Включить исходный код из другого файла

Включить генерацию.lst - файла

Выключить генерацию.lst - файла

Начальный адрес программы

Установите символ равный выражению

Синтаксис всех директив следующий:
.[директива]
То есть перед директивой должна стоять точка. Иначе ассемблер воспринимает это как метку.
Дадим несколько пояснений наиболее важным директивам ассемблера

CSEG - Code segment

Директива CSEG указывает на начало сегмента кодов. Ассемблируемый файл может иметь несколько кодовых сегментов, которые будут объединены в один при ассемблировании.
Синтаксис:

CSEG
Пример:
vartab: .BYTE 4 ; Резервируется 4 байта в СОЗУ
const: .DW 2 ; Записать 0x0002 в программной памяти
mov r1,r0 ; Что-то делать

DSEG - Data Segment

Директива DSEG указывает на начало сегмента данных. Ассемблируемый файл может содержать несколько сегментов данных, которые потом будут собраны в один при ассемблировании. Обычно сегмент данных состоит лишь из директив BYTE и меток.

Синтаксис:

DSEG
Пример:

table: .BYTE tab_size ; Резервировать tab_size байтов.
.CSEG
ldi r30,low(var1)
ldi r31,high(var1)
ld r1,Z

ESEG - EEPROM Segment

Директива ESEG указывает на начало сегмента EEPROM памяти. Ассемблируемый файл может содержать несколько EEPROM сегментов, которые будут собраны в один сегмент при ассемблировании. Обычно сегмент EEPROM состоит из DB и DW директив (и меток). Сегмент EEPROM памяти имеет свой собственный счетчик. Директива ORG может использоваться для размещения переменных в нужной области EEPROM.
Синтаксис:

DSEG ; Начало сегмента данных
var1: .BYTE 1 ; Резервировать 1 байт под переменную var1
table: .BYTE tab_size ; Зарезервировать tab_size байт.
.ESEG
eevar1: .DW 0xffff ; Записать 1 слово в EEPROM

ORG - Установить адрес начала программы

Директива ORG присваивает значения локальным счетчикам. Используется только совместно с директивами.CSEG, .DSEG, .ESEG.
Синтаксис:

DSEG ; Начало сегмента данных
.ORG 0x37 ; Установить адрес СОЗУ на 37h
variable: .BYTE 1 ; Зарезервировать байт СОЗУ по адресу 37h
.CSEG
.ORG 0x10 ; Установить счетчик команд на адрес 10h
mov r0,r1 ; Чего-нибудь делать

DB - определить байт(ы) в программной памяти или в EEPROM

Директива DB резервирует ресурсы памяти в программной памяти или в EEPROM. Директиве должна предшествовать метка. DB задает список выражений, и должна содержать по крайней мере одно выражение. Размещать директиву следует в сегменте кодов или в EEPROM сегменте.

Список выражений представляет собой последовательность выражений, разделенных запятыми. Каждое выражение должно быть величиной между –128 и 255.

Если директива указывается в сегменте кодов и список выражений содержит более двух величин, то выражения будут записаны так, что 2 байта будут размещаться в каждом слове Flash-памяти.

Синтаксис:

LABEL: .DB список выражений

CSEG
consts: .DB 0, 255, 0b01010101, -128, 0xaa
.ESEG
const2: .DB 1,2,3

DW – Определить слово(а) в программной памяти или в EEPROM

Директива DW резервирует ресурсы памяти в программной памяти или в EEPROM. Директиве должна предшествовать метка. DW задает список выражений, и должна содержать по крайней мере одно выражение. Размещать директиву следует в сегменте кодов или в EEPROM сегменте.

Список выражений представляет собой последовательность выражений, разделенных запятыми. Каждое выражение должно быть величиной между –32768 и 65535.

Синтаксис:

LABEL: .DW список выражений

CSEG
varlist: .DW 0, 0xffff, 0b1001110001010101, -32768, 65535
.ESEG
eevarlst: .DW 0,0xffff,10

DEF – Присвоить имя регистру

Директива DEF позволяет присвоить символическое имя регистру. Регистр может иметь несколько символических имен.
Синтаксис:

DEF Имя=Регистр

DEF temp=R16
.DEF ior=R0
.CSEG
ldi temp,0xf0 ; Загрузить 0xf0 в регистр temp
in ior,0x3f ; Прочитать SREG в регистр ior
eor temp,

EQU – Присвоить имя выражению

Директива EQU присваивает значение метке. Эта метка может быть использована в других выражениях. Значение этой метки нельзя изменить или переопределить.

Синтаксис:

EQU метка=выражение

EQU io_offset = 0x23
.EQU porta = io_offset + 2
.CSEG ; Начало сегмента кодов
clr r2 ; Очистить регистр r2
out porta,r2 ; Записать в порт А

INCLUDE –вставить другой файл

Директива INCLUDE говорит Ассемблеру начать читать из другого файла. Ассемблер будет ассемблировать этот файл до конца файла или до директивы EXIT. Включаемый файл может сам включать директивы INCLUDE.
Синтаксис:

INCLUDE "имя файла"

; iodefs.asm:
.EQU sreg = 0x3f ; Регистр статуса
.EQU sphigh = 0x3e ; Старший байт указателя стека.
.EQU splow = 0x3d; ; Младший байт указателя стека.
; incdemo.asm
.INCLUDE iodefs.asm ; Включить файл «iodefs.asm»
in r0,sreg ; Прочитать регистр статуса

EXIT – выйти из файла

Директива EXIT позволяет ассемблеру остановить ассемблирование текущего файла. Обычно ассемблер работает до конца файла. Если он встретит директиву EXIT, то продолжит ассемблировать со строки, следующей за директивой INCLUDE.
Синтаксис:

EXIT ; выйти из этого файла

DEVICE - Указать для какого микроконтроллера ассемблировать
Директива позволяет пользователю сообщить ассемблеру, для какого типа устройства пишется программа. Если ассемблер встретит команду, которая не поддерживается указанным типом микроконтроллера, то будет выдано сообщение. Также сообщение появится в случае, если размер программы превысит объем имеющейся в этом устройстве памяти.
Синтаксис:

DEVICE AT90S1200 |AT90S2313 | AT90S2323 | AT90S2333 | AT90S2343 | AT90S4414 | AT90S4433 | AT90S4434 | AT90S8515 | AT90S8534 | AT90S8535 | ATtiny11 | ATtiny12 | ATtiny22 | ATmega603 | ATmega103

DEVICE AT90S8535 ; использовать AT90S8535
.CSEG
.ORG 0000
jmp label1; При ассемблировании появиться сообщение, что;AT90S8535 не поддерживает команду jmp
1.5.2.4 Некоторые особенности программирования

Память данных почти полностью доступна программе пользователя и большинство команд ассемблера предназначено для обмена данными с ней. Команды пересылки данных предоставляют возможность непосредственной и косвенной адресации ячеек СОЗУ, непосредственной адресации регистров ввода/вывода и регистров общего назначения. Так как каждому регистру сопоставлена ячейка памяти, то обращаться к ним можно не только командами адресации регистров, но и командами адресации ячеек СОЗУ.

Например, команда:

MOV R10,R15 - скопировать регистр R15 в регистр R10
делает абсолютно то же самое, что и команда:
LDS R10,$0015 - загрузить в регистр R10 содержимое ячейки с адресом $0015
То же самое относится и к регистрам ввода/вывода. Для них предусмотрены специальные команды:
IN Rd,P - загрузить данные из порта I/O с номером Р в регистр Rd
OUT P,Rd - записать данные из регистра Rd в порт I/O с номером Р.
При использовании этих команд номер порта указывается в диапазоне 0Пример применения разных команд:

LDI R16,$FF
OUT $12,R16 - записать в PORTD число 255.
STS $0032,R16 - записать непосредственно в ячейку $0032 число 255.
Адрес регистра ввода/вывода в СОЗУ получается прибавлением к номеру порта числа $20.
Память программ является ПЗУ и изменяется только при программировании кристалла. Константы можно располагать в памяти программ в виде слов.

Например: .dw $033f,$676d,$7653,$237e,$777f
Для работы с данными, расположенными в памяти программ, предусмотрена команда
LPM - загрузить байт памяти программ, на который указывает регистр Z в регистр R0.
Адрес байта константы определяется содержимым регистра Z. Старшие 15 битов определяют слово адреса (от 0 до 4к) состояние младшего бита определяет выбор младшего байта (0) или старшего байта (1).
При работе с портами ввода/вывода следует учитывать следующую особенность. Если вывод порта сконфигурирован как выход, то его переключение производится через регистр данных (PORTA, PORTB, PORTC, PORTD), если вывод сконфигурирован как вход, то его опрос следует производить через регистр выводов входа порта (PINA, PINB, PINC, PIND).
Особенностью использования арифметических и логических команд является то, что некоторые из них работают только с регистрами R16-R31.

CPI Rd,K - сравнить регистр Rd с константой К. 16 Команды CBI и SBI работают только с младшими 32-мя регистрами ввода/вывода.
При использовании подпрограмм нужно обязательно определять стек! Для этого нужно занести значения адреса вершины стека в регистры SPH и SPL.

Напомним, что директивы (псевдооператоры) - это инструкции ассемблеру, они обрабатываются только при ассемблировании (трансляции) программы. Приведем некоторые из часто используемых директив.

Директивы определения идентификаторов

Присваивают идентификатору с данным именем некоторое текстовое или числовое значение (выражение). Формат директив:

имя EQU текст

имя = числовое значение (выражение)

Разница между псевдооператорами EQU и =:

l EQU - присваивает значение постоянно (изменять нельзя), текст может быть символьным, числовым или смешанным выражением, определяющим константу, адрес, другое символьное имя, метку и т.д.;

l = - выполняет текущее присваивание (значение может быть переназначено, но только при трансляции, естественно); присваивает только числовое выражение, содержащее простые математические преобразования, которые при трансляции и будут выполнены (например: const + 1, 15H*4, 3*12/4 и т.п.).

Директивы определения данных

Используются для идентификации переменных и полей памяти. Формат директивы

[имя] D* выражение [,выражение] [,...].

Ключевые слова D* могут быть следующими:

l DB - определить байт (1 байт);

l DW - определить слово (2 байта);

l DD - определить двойное слово (4 байта);

l DQ - определить 8 байтов;

l DT- определить 10 байтов.

Рассматриваемые директивы объявляют переменную (имя) или присваивают полям (ячейкам) памяти начальные значения; резервируют в памяти (с более поздним присвоением значения) один или несколько байтов - DB, слов - DW, двойных слов - DD и т.д.

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

l константой:
const DB 56; const DW 1936; const DD 3FFH.
Обязательно следует учитывать диапазон и вместимость байта, слова и т.д.; так, для DB константа не может быть больше 255, для DW - 65 535, для DD -



l 65 535 2 – 1 = 4 294 967 295;

l вектором или таблицей:
table1 DB 30, 4, –15, 0, 0, 0, 56; table2 DW 1936, 3004, 56, 15.
В одном псевдооператоре допускается поместить строку до 132 позиций, причем вместо повторения одного и того же значения несколько раз (0 в table1) можно использовать псевдооператор DUP (duplicate - дублировать):
table1 DB 30, 4, –15, 3 dup(0), 56);

l строкой символов:
str1 DB "Вы ввели слишком большое число";
str2 DB "Bad command";
в псевдооператоре DB строка может содержать 255 символов, во всех остальных (DW, DD, DQ, DT) - только 2 символа.

l пустым полем:
pole1 DB ?; pole2 DW 12 dup(?),
при этом в элементы резервируемой памяти при загрузке программы ничего не записывается (заносится не 0, как, например, в директиве pole3 DW 5 dup(0), а просто резервируются ячейки памяти);

l символическим именем переменной:
var1 DW disp; var2 DD vector
(одна переменная определяется адресом другой, в директивах указывать offset не надо, поскольку имя переменной воспринимается как ее адрес). Такой вариант подходит, например, для хранения адресов ячеек памяти, меток, на которые допустимо ссылаться в программе (var1 DW disp), причем, если переменная находится в том же сегменте, что и ссылающаяся команда, то достаточно в качестве адреса указать только смещение (2 байта), то есть обойтись DW; если же переменная находится в другом сегменте, то необходимо указать и сегмент, и смещение (всего 4 байта), то есть следует использовать уже DD (var2 DD vector);

l простым выражением:
fn1 DB 80*3; fn2 DW (disp) + 256, вычисляемым, разумеется, только при трансляции программы.

Директивы определения сегментов и процедур

Сегмент определяется псевдооператорами:

имя_сег segment

имя_сег ends

В программе можно использовать 4 сегмента (по числу сегментных регистров) и для каждого указать соответствующий регистр сегмента псевдооператором ASSUME(assume - присвоить), например:

assume CS:codeseg, DS:dataseg, SS:stackseg

В директиве ASSUME регистр_сег:имя_сег [,..], в частности, ASSUME cs:codeseg, указывается, что для сегмента имя_сег (codeseg) выбран регистр регистр_сег (CS).

После директивы ASSUME следует явным образом загрузить адрес начала сегмента данных в регистр DS:

Процедура определяется псевдооператорами:

имя_процедуры proc ...

имя_процедуры endp

При определении процедуры после ключевого слова proc должен быть указан атрибут дистанции nearили far; если этого атрибута нет, то по умолчанию подразумевается near. Обычно процедура должна заканчиваться командой ret (return). Если процедура объявлена как near, то обращение к ней (call) должно производиться из того же сегмента; если procfar, то из любого сегмента (в этом случае командой ret из стека при возврате будет извлечено два слова: для IP и для CS).

Директивы управления трансляцией

Их несколько, наиболее часто используется END. Директива END отмечает конец программы и указывает ассемблеру, где завершить трансляцию. Формат: END [имя_программы].

Программирование процедур работы с устройствами ввода-вывода

Процедуры ввода-вывода в ПК выполняются, как правило, по прерываниям. Состав и использование основных видов прерываний и служебных функций DOS прерывания 21H рассмотрены в работах . Ниже мы кратко остановимся на вопросах программирования ввода-вывода лишь прерываний для отображения информации на дисплее и ввода с клавиатуры. Вопросы вывода информации на принтер и работы с файлами рассмотрены в работах .

Программирование работы с дисплеем

Задание режимов работы и обмен данными с дисплеем можно выполнять при прерываниях BIOS типа 10H, а вывод данных на дисплей и при прерываниях DOS типа 21H.

Видеооперации с прерыванием 21H DOS

l Вывод символа на экран дисплея: AH = 2 или AH = 6 и DL <> 0FFh. В регистре DL должен быть ASCII-код символа. Пример фрагмента программы (вывод символа «C»):

mov DL, 43H ; 43H - это ASCII-код символа C

l Вывод строки символов : AH = 9 (чаще всего используемая функция). В регистрах DS:DX должен находиться начальный адрес строки символов, которая обязана заканчиваться символом $. Пример фрагмента программы (отображение текста "вывод строки символов$"):

Text db "вывод строки символов$"

mov DX, offset text ; это адрес выводимой строки

l Ввод/вывод из файла через логический номер. Стандартные файловые логические номера определяют тип и устройство ввода-вывода:

l 0 - ввод с клавиатуры;

l 1 - вывод на экран дисплея;

l 2- вывод на экран сообщения об ошибке;

l 3- ввод-вывод на внешнее устройство;

l 4- вывод на печать.

Для ввода предназначена функция AH = 3Fh прерывания 21H, для вывода служит функция AH = 40h прерывания 21H. В регистр CX предварительно заносится число вводимых-выводимых байтов, а в регистр DX записывается начальный адрес поля памяти для ввода-вывода. В случае успешного завершения процедуры ввода-вывода обнуляется флаг переноса CF, а в регистре AX возвращается количество фактически переданных байтов. При неудачной операции флаг CF устанавливается в 1, а в регистр AX заносится код ошибки.

Приведенный ниже пример содержит фрагмент программы для вывода на экран текстового файла Text, содержащего 50 байтов.

text db 50 dup(" ")

mov BX, 1 ; указание устройства вывода

mov CX, 50 ;указание числа выводимых байт

mov DX, offset text ; указание начального адреса

; поля памяти, содержащего текст

Определить устройство для которого компилируется

Параметры передаваемые директиве – это последовательность выражений разделённых запятыми. Каждое выражение должно быть или числом в диапазоне (-128..255), или в

результате вычисления должно давать результат в этом же диапазоне, в противном случае число усекается до байта, причём БЕЗ выдачи предупреждений.

Если директива получает более одного параметра и текущим является программный сегмент, то параметры упаковываются в слова (первый параметр – младший байт), и если

число параметров нечётно, то последнее выражение будет усечено до байта и записано как слово со старшим байтом равным нулю, даже если далее идет ещё одна

директива DB.

Синтаксис:
МЕТКА: .DB список_выражений

Пример:
.CSEG
consts: .DB 0, 255, 0b01010101, -128, 0xaa

Параметры передаваемые директиве – это последовательность выражений разделённых запятыми. Каждое выражение должно быть или числом в диапазоне (-32768..65535), или

в результате вычисления должно давать результат в этом же диапазоне, в противном случае число усекается до слова, причем БЕЗ выдачи предупреждений.

Синтаксис:
МЕТКА: .DW expressionlist

Пример:
.CSEG
varlist: .DW 0, 0xffff, 0b1001110001010101, -32768, 65535

Синтаксис:
.ENDMACRO

Пример:
.MACRO SUBI16 ; Начало определения макроса
subi r16,low(@0) ; Вычесть младший байт первого параметра
sbci r17,high(@0) ; Вычесть старший байт первого параметра
.ENDMACRO

EQU – Установить постоянное выражение

Директива EQU присваивает метке значение. Эта метка может позднее использоваться в выражениях. Метка которой присвоено значение данной директивой не может быть

переназначена и её значение не может быть изменено.

Синтаксис:
.EQU метка = выражение

Пример:
.EQU io_offset = 0x23
.EQU porta = io_offset + 2

CSEG ; Начало сегмента данных
clr r2 ; Очистить регистр r2
out porta,r2 ; Записать в порт A

ESEG – Сегмент EEPROM

Директива ESEG определяет начало сегмента EEPROM. Исходный файл может состоять из нескольких сегментов EEPROM, которые объединяются в один сегмент при компиляции.

Сегмент EEPROM обычно состоит только из директив ,

href="#DW - Define constant word(s) in program memory and EEPROM">DW

и меток. Сегменты EEPROM имеют свои собственные

побайтные счётчики положения. Директива может быть использована для размещения

переменных в необходимом месте EEPROM. Директива не имеет параметров.

Синтаксис:
.ESEG

Пример:
var1: .BYTE 1 ; зарезервировать 1 байт для var1
table: .BYTE tab_size ; зарезервировать tab_size байт.

ESEG
eevar1: .DW 0xffff ; проинициализировать 1 слово в EEPROM

EXIT – Выйти из файла

Встретив директиву EXIT компилятор прекращает компиляцию данного файла. Если директива использована во вложенном файле (см. директиву

href="#INCLUDE - Include another file">INCLUDE

), то компиляция продолжается со строки следующей после директивы INCLUDE.

Если же файл не является вложенным, то компиляция прекращается.

Синтаксис:
.EXIT

Пример:
.EXIT ; Выйти из данного файла

INCLUDE – Вложить другой файл

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

href="#EXIT - Exit this file">EXIT

, после этого продолжает компиляцию начального файла со строки следующей за директивой

INCLUDE. Вложенный файл может также содержать директивы INCLUDE.

Синтаксис:
.INCLUDE "имя_файла"

Пример:
; файл iodefs.asm:
.EQU sreg = 0x3f ; Регистр статуса
.EQU sphigh = 0x3e ; Старший байт указателя стека
.EQU splow = 0x3d ; Младший байт указателя стека

; файл incdemo.asm
.INCLUDE iodefs.asm ; Вложить определения портов
in r0,sreg ; Прочитать регистр статуса

LIST – Включить генерацию листинга

Директива LIST указывает компилятору на необходимость создания листинга. Листинг представляет из себя комбинацию ассемблерного кода, адресов и кодов операций. По

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

Синтаксис:
.LIST

Пример:

LISTMAC – Включить разворачивание макросов в листинге

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

параметры.

Синтаксис:
.LISTMAC

Пример:
.MACRO MACX ; Определение макроса
add r0,@0 ; Тело макроса
eor r1,@1

LISTMAC ; Включить разворачивание макросов
MACX r2,r1 ; Вызов макроса (в листинге будет показано теломакроса)

MACRO – Начало макроса

С директивы MACRO начинается определение макроса. В качестве параметра директиве передаётся имя макроса. При встрече имени макроса позднее в тексте программы,

компилятор заменяет это имя на тело макроса. Макрос может иметь до 10 параметров, к которым в его теле обращаются через @0-@9. При вызове параметры перечисляются

через запятые. Определение макроса заканчивается директивой .

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

Синтаксис:
.MACRO макроимя

Пример:
.MACRO SUBI16 ; Начало макроопределения
subi @1,low(@0) ; Вычесть младший байт параметра 0 из параметра 1
sbci @2,high(@0) ; Вычесть старший байт параметра 0 из параметра 2
.ENDMACRO ; Конец макроопределения

CSEG ; Начало программного сегмента
SUBI16 0x1234,r16,r17 ; Вычесть 0x1234 из r17:r16

NOLIST – Выключить генерацию листинга

Директива NOLIST указывает компилятору на необходимость прекращения генерации листинга. Листинг представляет из себя комбинацию ассемблерного кода, адресов и

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

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

исходных файлов

Синтаксис:
.NOLIST

Пример:
.NOLIST ; Отключить генерацию листинга
.INCLUDE "macro.inc" ; Вложенные файлы не будут
.INCLUDE "const.def" ; отображены в листинге
.LIST ; Включить генерацию листинга

ORG – Установить положение в сегменте

Директива ORG устанавливает счётчик положения равным заданной величине, которая передаётся как параметр. Для сегмента данных она устанавливает счётчик положения в

SRAM (ОЗУ), для сегмента программ это программный счётчик, а для сегмента EEPROM это положение в EEPROM. Если директиве предшествует метка (в той же строке) то

метка размещается по адресу указанному в параметре директивы. Перед началом компиляции программный счётчик и счётчик EEPROM равны нулю, а счётчик ОЗУ равен 32

(поскольку адреса 0-31 заняты регистрами). Обратите внимание что для ОЗУ и EEPROM используются побайтные счётчики а для программного сегмента – пословный.

Синтаксис:
.ORG выражение

Пример:
.DSEG ; Начало сегмента данных

ORG 0x37 ; Установить адрес SRAM равным 0x37
variable: .BYTE 1 ; Зарезервировать байт по адресу 0x37H

CSEG
.ORG 0x10 ; Установить программный счётчик равным 0x10
mov r0,r1 ; Данная команда будет размещена по адресу 0x10

SET – Установить переменный символический эквивалент выражения

Директива SET присваивает имени некоторое значение. Это имя позднее может быть использовано в выражениях. Причем в отличии от директивы

href="#EQU - Set a symbol equal to an expression">EQU

значение имени может быть изменено другой директивой SET.

Синтаксис:
.SET имя = выражение

Пример:
.SET io_offset = 0x23
.SET porta = io_offset + 2

CSEG ; Начало кодового сегмента
clr r2 ; Очистить регистр 2

Одним из важнейших элементов этой части книги являются примеры программ в машинных кодах и на языке АССЕМБЛЕРа. В распечатках этих программ нам придется употреблять так

называемые ДИРЕКТИВЫ АССЕМБЛЕРа и сейчас, наверное, самое удобное время для того, чтобы дать представление о том, что это такое.

Мы рассмотрим следующие директивы: ORG, EQU, DEFB, DEFW, DEFM и END, но прежде чем начать их рассмотрение, надо твердо для себя понять:

1. Директивы АССЕМБЛЕРа не являются командами процессора Z8 0 и в этом смысле отношения к машинному коду Z8 0 не имеют.

2. АССЕМБЛЕР - это программа, которая переводит (транслирует) текст, написанный Вами в виде мнемоник в объектный код, являющийся машинным. И эти директивы АССЕМБЛЕРа - это некоторые команды ассемблирующей программе. Они не транслируются и в объектный код не войдут, но упростят Вам написание, и самое главное - чтение программы, записанной в мнемониках.

3. Программ-АССЕМБЛЕРов существует великое множество и каждая из них может иметь свои собственные директивы. Они могут иметь и одинаковые директивы, но предъявлять разные требования к их употреблению. Одним словом, конкретно способы использования директив АССЕМБЛЕРа Вам надо устанавливать по инструкции к ассемблирующей программе, которой Вы пользуетесь (напр. EDITAS, GENS 3, GENS 4, ZEUS и т.п.). И хотя стандартов не существует, тем не менее некоторые основополагающие понятия все же выделить можно, вот на них-то мы и остановимся.

3.1. Комментарии.

Мы начнем с самого простого - с комментариев. Они записываются после символа ";" (точка с запятой) .

Вам, конечно понятно, что все, что является комментариями, АССЕМБЛЕРом в машинный код не компилируется - это ни к чему. Они служат только для того, чтобы Вам было удобнее разбираться с листингом, который составил кто-то другой или Вы сами, но давным-давно.

Например:

10 60001 LD E,A 2 0

Как видите, строка может

; Загрузили в регистр E содер-; жимое аккумулятора. ; Уменьшили его на единицу.

остоять только из комментария.

Метки.

Метки значительно упрощают написание программ в мнемониках АССЕМБЛЕРа. В операциях перехода JP, JR, DJNZ, вызова подпрограмм CALL Вы можете не указывать адрес, в который Вы хотите совершить переход, а вместо него подставить метку. С другой стороны, когда будете писать команды для этого адреса, подставите метку и там, Например:

10 60001 BEGIN LD B,0 4

20 60003 AGAIN INC HL

40 60005 DJNZ, AGAIN

3.2.

250 260 270

60110 60111 60113

LD A,(HL) CP 80H JR NZ,BEGIN

Как видите, очень удобно. Сразу видно, что из строки 40 возврат осуществляется к метке AGAIN, если регистр B не достиг нуля. Из строки 270 возврат осуществляется к метке BEGIN.

Определенно имеет смысл выбирать для метки такое имя, которое соответствовало бы смыслу исполняемой операции - это облегчает чтение и понимание листинга программы.

При компиляции ассемблирующая программа сама подсчитает величины необходимых смещений в командах процессора и подставит их вместо меток. Так, например, в строке 40 вместо DJNZ AGAIN в объектный код пойдет DJNZ FCH, что то же самое.

В предыдущем примере мы использовали метки очень ограниченно. Дело в том, что и обращение по метке и сама метка находились в одной и той же процедуре. А как быть, если Вы хотите обратиться к метке, которая находится в другой процедуре, которую Вы написали и откомпилировали еще вчера, а как быть, если Вам надо сделать переход к процедуре ПЗУ и Вы при этом хотите воспользоваться меткой? В этом случае Вам поможет директива EQU. Она присваивает метке числовое значение. Конечно, при компиляции эта директива никак в машинный код не преобразовывается, но если по тексту программы есть ссылки на эту метку, то вместо нее будет подставлено значение, взятое из директивы EQU.

Например, Вам в Вашей программе неоднократно приходится вызывать процедуры ПЗУ, скажем CLEAR (1EACH=7 8 52) и OUT-LINE (1856H=6230) . Тогда в начале Вашей программы Вы задаете

например назвав их CLEAR

директивой

значения своим меткам, н

и OUT L.

CLEAR

EQU 7 8 52

OUT L

EQU 62 3 0

LABEL

EQU 60016

выз

ываете эти

процедуры или

по метке.

60001

LD HL, (LABEL)

60004

LD BC, 0008

60007

LD DE, (04 52)

60010

CALL CLEAR

60013

CALL OUT L

60016

Сразу

должны Вас

предупредить,

примеры

с точки зрения

программной

примеры того, как используются те или иные директивы АССЕМБЛЕРа и если Вам нужен в примерах реальный смысл, то Вы его получите чуть позже, в последующих главах, где мы будем разбирать практические приемы программирования.

Давайте еще раз взглянем на предыдущий пример. В строке 30 мы засылаем в регистровую пару HL то, что содержится в адресе, на который указывает метка LABEL, а она, согласно директиве EQU указывает на адрес 60016.

Итак, в ячейках 60016 и 60017 содержатся некоторые данные, которые впоследствии могут использоваться программой. Эти данные Вы можете заслать в ячейки сами перед компиляцией. И совсем не надо для этого привлекать машинный код. Первоначальные значения в ячейках памяти Вы можете выставить с помощью директив DEFB, DEFW и DEFM.

DEFB - DEFINE BYTE - задать байт.

DEFW - DEFINE WORD - задать "слово" ("слово" - это два последовательно расположенных байта. Обычно это адрес.) DEFM - DEFINE MESSAGE - задать сообщение (это несколько подряд идущих байтов) . Обычно ассемблирующие программы накладывают ограничение на то, сколько байтов можно задать одной директивой DEFM, скажем не более пяти. Но Вас это не должно волновать. Если Вы хотите задать длинное сообщение, то можете ставить подряд столько строк DEFM, сколько хотите.

Итак, DEFB задает один одиночный байт (0...255) , DEFW -два подряд идущих байта (0...65535), а DEFM - группу подряд идущих байтов - текстовое сообщение, числовая таблица и т. п.

В нашем предыдущем примере, если мы хотим хранить в адресе 60016 и 60017 некоторое двухбайтное число, строку 80 следовало бы записать, например так:

80 60016 DEFW 5C92H

90 60018

Предположим, Вы хотите начиная с адреса 60135 хранить слово "Spectrum".

Код буквы "S" Код буквы "p" "e" "c" "t" " r "

"u" "m"

60135

60136

60137

60138

60139

60140

60141

60142

53H 7 0H 65H 63H 7 4H 72H 75H 6DH

DEFB DEFB DEFB DEFB DEFB DEFB DEFB DEFB

можете его задать парами байтов:

Но проще и правильнее задать его как сообщение:

60135 DEFM 5370656374 ; "Spect"

60140 DEFM 72756D ; "rum"

Есть особый случай при программировании на АССЕМБЛЕРе, когда текст программы тоже приходится вводить через DEFB или DEFM. Это случай, когда Вы пишете программу для встроенного калькулятора. Ведь ассемблирующая программа может перевести в машинный код мнемоники АССЕМБЛЕРа, но она ничего не знает о кодах калькулятора и не знает его мнемоник. Код калькулятора -это внутреннее "Синклеровское" дело, его интерпретацией

занимаются программы, размещенные в ПЗУ и к процессору и к его командам код калькулятора не имеет никакого отношения. Посему ввести команды калькулятору в ассемблирующую программу Вам удастся только как последовательность независимых байтов, т. е. через DEFB или DEFM.

Мы с Вами в первой части книги употребляли мнемонические обозначения команд калькулятора, типа add, stk_data s_lt и т. п., и писали их с маленькой буквы в отличие от команд процессора. Но делали это ранее и будем делать впредь только ради понимания и удобства записи. Программа-АССЕМБЛЕР таких мнемоник не знает, их нет в ее словаре.

Итак, с помощью DEFB, DEFW и DEFM задают начальные значения программным переменным, вводят в программу таблицы, сообщения и любые прочие последовательности данных, даже

графику, а также такие кодовые последовательности, которые ассемблирующая программа не понимает, как команды АССЕМБЛЕРа.

3.5. Директивы ORG, END.

Нам осталось рассмотреть две самые тривиальные директивы. Директива ORG объявляет адрес, начиная с которого будет ассемблироваться программа. Она должна быть первой директивой в исходном тексте, хотя в принципе, перед ней могут быть комментарии.

Вы обратили внимание на то, что в вышеприведенных примерах мы слева писали столбец адресов, в которых будут размещаться те или иные команды. Так вот, этого при программировании на АСЕМБЛЕРе делать не надо. Достаточно в самом начале дать директиву

10 ORG 63000

и далее ассемблирующая программа сама рассчитает в какой ячейке памяти будет находиться та или иная команда. Это очень упрощает процесс программирования. А если Вы внесете изменения в готовый текст, АССЕМБЛЕР сам подправит все адреса.

Директива END отмечает конец программы. Если после него что-то еще и будет стоять, то АССЕМБЛЕР при компиляции это проигнорирует.

Вот пожалуй и все, что для начала стоит знать о директивах АССЕМБЛЕРа. Это не все директивы, какие могут встретиться в жизни, да и правила их использования для разных АССЕМБЛЕРов -разные, но по большому счету этот минимум удовлетворит 90 процентов Ваших потребностей в информации, а остальное Вы должны почерпнуть из инструкции к тому АССЕМБЛЕРу, с которым работаете.