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

Основные директивы ассемблера

Источник: https://zdamsam.ru/a47477.html

Лаборатория цифровых технологий » Директивы ассемблера

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

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

Все директивы предваряются точкой.

BYTE – Зарезервировать байты в ОЗУ

Директива BYTE резервирует байты в ОЗУ. Если Вы хотите иметь возможность ссылаться на выделенную область памяти, то директива BYTE должна быть предварена меткой.

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

директивы CSEG и DSEG). Выделенные байты не инициализируются.

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

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

.CSEG
ldi r30,low(var1)  ; Загружает младший байт регистра Z
ldi r31,high(var1) ; Загружает старший байт регистра Z
ld r1,Z            ; Загружает VAR1 в регистр 1

CSEG – Программный сегмент

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

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

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

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

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

Пример:
.DSEG                       ; Начало сегмента данных
vartab: .BYTE 4             ; Резервирует 4 байта в ОЗУ

.CSEG                       ; Начало кодового сегмента
const:  .DW 2               ; Разместить константу 0x0002 в памяти программ
mov r1,r0           ; Выполнить действия

DB – Определить байты во флэш или EEPROM

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

директива DB должна быть предварена меткой. Директива DB должна иметь хотя бы один параметр. Данная директива может быть размещена только в сегменте программ () или в сегменте EEPROM (ESEG).

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

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

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

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

директива DB.

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

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

.ESEG
const2: .DB 1,2,3

DEF – Назначить регистру символическое имя

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

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

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

Пример:
.DEF temp=R16
.DEF ior=R0

.CSEG
ldi temp,0xf0  ; Загрузить 0xf0 в регистр temp (R16)
in ior,0x3f  ; Прочитать SREG в регистр ior (R0)
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

.CSEG
push r30   ; Эта инструкция вызовет предупреждение
; поскольку AT90S1200 её не имеет

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

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

Сегмент данных обычно состоит только из директив BYTE и меток. Сегменты

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

использована для размещения переменных в необходимом месте ОЗУ. Директива не имеет параметров.

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

Пример:
.DSEG                        ; Начало сегмента данных
var1:  .BYTE 1               ; зарезервировать 1 байт для var1
table:  .BYTE tab_size       ; зарезервировать tab_size байт.

.CSEG
ldi r30,low(var1)    ; Загрузить младший байт регистра Z
ldi r31,high(var1)   ; Загрузить старший байт регистра Z
ld r1,Z              ; Загрузить var1 в регистр r1

DW – Определить слова во флэш или EEPROM

Директива DW резервирует необходимое количество слов в памяти программ или в EEPROM. Если Вы хотите иметь возможность ссылаться на выделенную область памяти, то

директива DW должна быть предварена меткой. Директива DW должна иметь хотя бы один параметр. Данная директива может быть размещена только в сегменте программ () или в сегменте EEPROM (ESEG).

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

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

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

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

.ESEG
eevarlst: .DW 0,0xffff,10

ENDMACRO – Конец макроса

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

Синтаксис:
.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 обычно состоит только из директив DB,  и меток. Сегменты EEPROM имеют свои собственные

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

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

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

Пример:
.DSEG                    ; Начало сегмента данных
var1:   .BYTE 1          ; зарезервировать 1 байт для var1
table:  .BYTE tab_size   ; зарезервировать tab_size байт.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

параметры.

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

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

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

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

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

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

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

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

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

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

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

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

Синтаксис:
.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 присваивает имени некоторое значение. Это имя позднее может быть использовано в выражениях. Причем в отличии от директивы  значение имени может быть изменено другой директивой SET.

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

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

.CSEG                 ; Начало кодового сегмента
clr r2        ; Очистить регистр 2
out porta,r2  ; Записать в порт AR31:R30)

Источник: http://digitallab.kpi.ua/?page_id=723

Глава 5 – ассемблер(директивы и операторы)

Замечание: по ходу этой главы мне придётся ссылаться на некоторые команды процессора, которые мы разберём в следующих главах.

Структура программы

Программа на ассемблере состоит из строк вида:
метка[:] команда/директива операнды ;коментарийВсе данные поля необязательны т.е. строка может быть и пустой! Метка может быть любой комбинацией символов но не должна начинаться с цифры, кроме того не стоит использовать знаки $ и ?. Коментарием может быть всё что угодно.

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

Вот например забегая вперёдloop: lodsw ;читаем слово в еаx
cmp eax,78;если 78 то прекратим
loopne loop

Если метка ставится перед директивой то двоеточние не нужно. Директивы работающие с метками это label и equ. Первая определяет метку и задаёт её тип. А вторая присваивает метке значение (то есть имя метки это синоним выражения в правой части, в общем аналог #define d с/с++).

Вообще на практике их используют не часто.
метка label тип
метка equ выражение
Типом может быть BYTE(байт), WORD(слово), DWORD(двойное слово), FWORD(6 байт),QWORD(учетверённое слово),TBYTE(10 байт),NEAR(ближняя метка),FAR(дальня метка).

Есть одна очень полезная предопределённая метка: $.  Она определяет текущий адрес. Вот как лекго организуется вечный цикл:
jmp $

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

А куда-же без этого? Перед использованием данные надо определять. Это хороший тон. Вот что для этого есть.

  • DB – определить одни байт
  • DW – определить два байта(слово)
  • DD – определить двойное слово
  • DF – определить 6 байт
  • DQ – определить учетверённое слово (8 баёт)
  • DT – определить 10 байт (80 битные типы данных для FPU)

Вот примеры определения данных
text db 'Hi! i'm string!'
number dw 7878
tbl db 1,2,3,4,5,6,7,8,9,10,11,12
float dd 3.5e7
empty dw ? 

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

Вот обращение к первому символу: mov al,text.Пустое значение задается знаком вопроса. Кроме этого для массива можно использовать оператор DUP.

Его формат счётчик DUP (значение). 
tbl dw 1024 dup (?) задаёт массив неинициализированных слов размеров 1Кб.
В ассемблере также можно определять структуры данных аналогично языкам высокого уровня! Кроме того структуры могут быть вложенными.

Рассмотрим на примере:point struct ;вводим новый тип данных
x dw 0 ;здесь два поля но их количество 
y dw 0 ;на самом деле произвольно, как и тип данных!
point ends ;закончили описание нового типа данных
cur_point point ; объявляем переменную нового типа и указываем значения
……………………….
mov ax,cur_point.x; а так можно обратиться к элементу структуры. 

Записи

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

Объявление записи в программе на языке ассемблера включает в себя 2 действия: 1) объявление шаблона или типа записи директивой RECORD; 2) объявление собственно записи. Формат директивы RECORD: имя_записи RECORD имя_поля:длина[[=выражение]],…

Директива RECORD определяет вид 8- или 16-битовой записи, содержащей одно или несколько полей. Имя_записи представляет собой имя типа записи, которое будет использоваться при объявлении записи. Имя_поля и длина (в битах) описывает конкретное поле записи.

Выражение, если оно указано задает начальное (умалчиваемое) значение поля. Описания полей записи в директиве RECORD, если их несколько, должны разделяться запятыми. Для одной записи может быть задано любое число полей, но их суммарная длина не должна превышать 16 бит.

Длина каждого поля задается константой в пределах от 1 до 16. Если общая длина полей превышает 8 бит, ассемблер выделяет под запись 2 байта, в противном случае – 1 байт. Если задано выражение, оно определяет начальное значение поля. Если длина поля не меньше 7 бит, в качестве выражения может быть использован символ в коде ASCII.

Выражение не должно содержать ссылок вперед.

Пример: item RECORD char:7='Q',weight:4=2 Запись item будет иметь следующий вид: char weight 0000 1010001 0010 При обработке директивы RECORD формируется шаблон записи, а сами данные создаются при объявлении записи, которое имеет следующий вид: [[имя]] имя_записи

По такому объявлению создается переменная типа записи с 8- или 16-битовым значением и структурой полей, соответствующей шаблону, заданному директивой RECORD с именем имя_записи. Имя задает имя переменной типа записи. Если имя опущено, ассемблер распределяет память, но не создает переменную, которую можно было бы использовать для доступа к записи.

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

Для каждого поля может быть задано одно значение. Скобки обязательны, даже если начальные значения не заданы.

Пример: table item 10 DUP()

Если для описания шаблона записи использовалась директива RECORD из предыдущего примера, то по этому объявлению создается 10 записей, объединенных именем table.

Организация программы – сегменты

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

Вот как описывают сегмент на ассемблере
имя сегмента segment readonly выравнивание тип разрядность класс
………………………….
имя сегмента ends

Не все операнды обязаны присутствовать. Если написано readonly то в сегмент нельзя ничего писать, выравнивание может быть BYTE (с любого адреса), WORD(с адреса кратного двум),DWORD(c кратного четырём),PARA (с кратного 16), PAGE(с кратного 256). Тип определяет комбинирование сегментов:

  • PUBLIC – все сегменты с одинаковым именем но разными классами объединяются в один
  • STACK тоже самое но используется для стэка
  • COMMON – сегменты объединяются в одни но не последовательно а с одинакового адреса, для формирования оверлеев
  • AT – указывает фиксированый абсолютный адрес в памяти
  • PRIVATE – такие сегменты не объединяются ни с какими другими

Разрядность может быть USE16 или USE32. Шестнадцатиразрядные сегменты не могут превышать 64Кб, в них всё равно можно применять 32-х разрядные регистры и команды, но они будут работать медленне из-за префикса переопределения разрядности! Класс сегмента это любая метка, взятая в кавычки.

Для обращения к сегменту его сначала надо загрузить в какой нибудь сегментный регистр. Если в программе много сегентов то надо создать их группу:
name_group group seg1,seg2,…..
Имя группы можно применять для получения адреса и для директивы ASSUME
assume регистр:связь…..


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

Модели памяти и упрощенные директивы распределения сегментов

Модели памяти задаются директивой .MODEL модель,язык,модификатор 
Модель это одно из слов

  • TINY – код данные и стек размещаются в одном и томже сегменте размером до 64Кб.
  • SMALL – код в одном сегменте а данные и стек в другом.
  • COMPACT – код в одном сегменте а под данные выделяется несколько сегментов
  • MEDIUM – код в нескольких сегментах а данные в одном
  • LARGE и HUGE – и код и данные в нескольких сегментах
  • FLAT – то же что и TINY но 32-х разрядный

Язык определяет что процедуры рассчитаны на вызов из подпрограмм на языке высокого уровня. Модификатор определяет будет ли сегмент объединяться с сегментом стэка: NEARSTAK и FARSTACK. После установления модели памяти сегменты можно определить упрощенно!
Сегмент кода: .

code имя_сегмента
Сегмент стэка: .stack размер
Сегмент данных: .data
Сегмент неинициализированых данных: .data? Сегмент констант: .

const
Вообще говоря мы так и будем определять модели памяти и сегменты, так компактнее и нагляднее!

 

Другие директивы

Директива INCLUDE.
Встречая директиву INCLUDE, ассемблер весь текст, хранящийся в указанном файле, подставит в программу вместо этой директивы.

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

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

Директивы задания допустимого набора команд. 
По умолчанию разрешено использовать набор команд только процессора 80×86. Для использования новых команд, разрешение нужно явно указать:

  • .186
  • .286 и .286c
  • .286p
  • .386 и .386c  – разрешены непривелигированые команды 80286
  • .386p  – разрешены все команды 80286
  • .MMX

И так можно указывать вплоть до .686. Конечно если версия ассемблера более ранняя чем выход соответствующего процесора то директиву указывать нельзя. На практике достаточно указать .386

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

org выражение
Забегая вперёд, можно сказать что эта директива с параметром 100h используется при написании COM программ которые загружаются в память после блока параметров размеров 256 байт.

Источник: http://iamalone.ru/index.php/blog/dokumentatsiya/samouchitel-po-assembleru/item/242-glava-5-assembler-direktivy-i-operatory

открытая библиотека учебной информации

Ассемблер имеет ряд операторов, которые позволяют управлять процессом ассемблирования и формирования листинга. Эти операторы называются псевдокомандами или директивами. Οʜᴎ действуют только в процессе ассемблирования программы и не генерируют машинных кодов.

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

Директива задания исходных данных:

[] d [,, , . . .]

· – имя массива данных, по которому к ним можно обратиться из команды;

· d(define)– определяет начало массива данных;

· – размер констант, входящих в массив:

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

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

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

имя 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 5352 – 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 — присвоить), например:

codeseg sedment

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

..

codeseg ends

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

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

mov AX, dataseg

mov DS, AX

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

имя_процедуры proc [far] …

ret

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

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

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

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

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

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

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

Ниже мы кратко остановимся на вопросах программирования ввода-вывода лишь прерываний для отображения информации на дисплее и ввода с клавиатуры.

Вопросы вывода информации на принтер и работы с файлами рассмотрены в работах [8,15].

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

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

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

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

mov AH, 6

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

int 21H

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

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

mov AH, 9

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

int 21H

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 AH, 40H

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

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

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

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

int 21H

Не нашли то, что искали? Воспользуйтесь поиском гугл на сайте:

b байт,
w слово(два байта),
d двойное слово,
q учетверённое слово,
t десять байтов;

· – числовой или символьный элемент массива дан­ных.

В ассемблере используется несколько типов констант:

десятичные – последовательность цифр от до 9;

шестнадцатеричные – последовательность шестнадцатеричных цифр от до 9 и от А или а до Fили fзавершающаяся буквой H или h, первой должна быть десятичная цифра или ;

восьмеричные – последовательность цифр от до 7, завершающаяся буквами Q или q;

двоичные – последовательность цифр от до 1, завершающаяся буквой B или b;

символьные – символ или группа символов, заключённые в кавычки.

· знак ? – используется для резервирования места для данных.

· К примеру,

· data1 db 123, 0a2h, 75q, 110011b, 'a', 'пример', ?, ?

· Для заполнения больших массивов используется директива dup (duplicate):

· dup()

· – задаёт количество размещаемых в памяти данных, определяемых образцом;

· – любая допустимая группа констант.

· К примеру,

· data2 db 23 dup(1, 2, 'x')

· выделяет в памяти 23 · 3=69 байтов и заносит в них образец 1, 2, 'x', 1, 2, 'x', … .

Директива использования сегментных регистров по умолчанию:

assume:[, :, …]

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

Транслятор сопоставляет имя массива данных и автоматически подставляет сегментный регистр, заданный для сегмента͵ в котором расположен данный массив. Слово nothingпоказывает, что данный сегментный регистр не адресуется по умолчанию.

Директива assume может использоваться в программе при каждом изменении сегмента для данного сегментного регистра, но обязательно в начале сегмента͵ где она задаёт по умолчанию сегментный регистр для сегмента кодов.

К примеру,

assumecs:code, ds:data1, es:nothing

Здесь code и data1 – имена сегментов кодов и данных, соответственно.

Для простых программ, содержащих по одному сегменту для кода, данных и стека в трансляторы MASM и TASM ввели возможность использования упрощенных директив сегментации.

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

Для этого совместно с упрощенными директивами сегментации стали использовать директиву указания модели памяти MODEL, которая частично стала управлять размещением сегментов и выполнять функции директивы ASSUME (в связи с этим при использовании упрощенных директив сегментации директиву ASSUME можно не использовать). Эта директива связывает сегменты, которые в случае использования упрощенных директив сегментации имеют предопределœенные имена, с сегментными регистрами (хотя явно инициализировать dsвсœе равно придется).

Использование упрощенных директив сегментации.model small ;модель памяти.data ;сегмент данныхmessage db 'Введите две шестнадцатеричные цифры,$'.stack ;сегмент стека db 256dup ('?') ;сегмент стека.

code ;сегмент кодаmain ;начало процедуры main mov ax,@data ;заносим адрес сегмента данных в регистр ax mov ds,ax ;ax в ds;далее текст программы mov ax,4c00h ;пересылка 4c00h в регистр ax int 21h ;вызов прерывания с номером 21hend main ;конец программы с точкой входа main

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

Читайте также

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

    Ассемблер имеет ряд операторов, которые позволяют управлять процессом ассемблирования и формирования листинга. Эти операторы называются псевдокомандами или директивами. Они действуют только в процессе ассемблирования программы и не генерируют машинных кодов. … [читать подробенее]

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

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

  • – ДИРЕКТИВЫ АССЕМБЛЕРА

    СТРУКТУРА МАШИННОГО ОПЕРАТОРА ВЫРАЖЕНИЯ АССЕМБЛЕР Ассемблер – язык программирования низкого уровня, в котором в большинстве случаев одному оператору соответствует одна машинная команда. Это позволяет создавать оптимальные программы специального… [читать подробенее]

  • Источник: http://oplib.ru/random/view/268561

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

     

    Физически сегмент представляет собой область памяти, занятую командами и (или) данными, адреса которых вычисляются относительно значения в соответствующем сегментном регистре.

    Каждая программа содержит 3 типа сегментов:

    • Сегмент  кодов – содержит машинные команды для выполнения. Обычно первая выполняемая команда находится в начале этого сегмента, и операционная система передает управление по адресу данного сегмента  для выполнения программы. Регистр сегмента кодов (CS) адресует данный сегмент.
    • Сегмент данных – содержит данные, константы и рабочие  области, необходимые программе. Регистр сегмента данных (DS) адресует данный сегмент.
    • Сегмент стека — содержит адреса возврата как для программы (для возврата в операционную систему), так и для  вызовов подпрограмм (для возврата в главную программу), а также используется для передачи параметров в процедуры. Регистр сегмента стека (SS) адресует данный сегмент. Адрес текущей вершины стека задается регистрами SS:ESP.

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

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

    Именно для реализации различных вариантов такого объединения и предназначены директивы сегментации.

    Упрощенные директивы сегментации

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

    • .CODE — для указания начала сегмента кода;
    • .DATA — для указания начала сегмента данных;
    • .STACK — для указания начала сегмента стека.

    Однако использование упрощенных директив сегментации не позволяет создать более трех сегментов для одной программы.

    Стандартные директивы сегментации

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

    ИмяСегмента SEGMENT align combine dim ‘class’ …

    ИмяСегмента ENDS

    Директива ENDS определяет конец сегмента.

    Атрибут выравнивания сегмента (тип выравнивания) align сообщает компоновщику о том, что нужно обеспечить размещение начала сегмента на заданной границе. Это важно, поскольку при правильном выравнивании доступ к данным в процессорах, совместимых с базовым i8086, выполняется быстрее. Допустимые значения этого атрибута следующие:

    • BYTE — выравнивание не выполняется. Сегмент может начинаться с любого адреса памяти;
    • WORD — сегмент начинается по адресу, кратному двум, то есть последний (младший) значащий бит физического адреса равен 0 (выравнивание на границу слова);
    • DWORD — сегмент начинается по адресу, кратному четырем, то есть два последних (младших) значащих бита равны 0 (выравнивание по границе двойного слова);
    • PARA — сегмент начинается по адресу, кратному 16, то есть последняя шестнадцатеричная цифра адреса должна быть 0h (выравнивание по границе параграфа);
    • PAGE — сегмент начинается по адресу, кратному 256, то есть две последние шестнадцатеричные цифры должны быть 00h (выравнивание по границе страницы размером 256 байт);
    • MEMPAGE — сегмент начинается по адресу, кратному 4 Кбайт, то есть три последние шестнадцатеричные цифры должны быть 000h (адрес следующей страницы памяти размером 4 Кбайт);

    По умолчанию тип выравнивания имеет значение PARA.
    Атрибут комбинирования сегментов (комбинаторный тип) combine сообщает компоновщику, как нужно комбинировать сегменты различных модулей, имеющие одно и то же имя. По умолчанию атрибут комбинирования принимает значение PRIVATE. Значениями атрибута комбинирования сегмента могут быть:

    • PRIVATE — сегмент не будет объединяться с другими сегментами с тем же именем вне данного модуля;
    • PUBLIC — заставляет компоновщик соединить все сегменты с одинаковым именем. Новый объединенный сегмент будет целым и непрерывным. Все адреса (смещения) объектов, а это могут быть, в зависимости от типа сегмента, команды или данные, будут вычисляться относительно начала этого нового сегмента;
    • COMMON — располагает все сегменты с одним и тем же именем по одному адресу. Все сегменты с данным именем будут перекрываться и совместно использовать память. Размер полученного в результате сегмента будет равен размеру самого большого сегмента;
    • AT xxxx — располагает сегмент по абсолютному адресу параграфа (параграф — объем памяти, кратный 16, поэтому последняя шестнадцатеричная цифра адреса параграфа равна 0). Абсолютный адрес параграфа задается выражением хххx. Компоновщик располагает сегмент по заданному адресу памяти (это можно использовать, например, для доступа к видеопамяти или области ПЗУ), учитывая атрибут комбинирования. Физически это означает, что сегмент при загрузке в память будет расположен, начиная с этого абсолютного адреса параграфа, но для доступа к нему в соответствующий сегментный регистр должно быть загружено заданное в атрибуте значение. Все метки и адреса в определенном таким образом сегменте отсчитываются относительно заданного абсолютного адреса;
    • STACK — определение сегмента стека. Заставляет компоновщик соединить все одноименные сегменты и вычислять адреса в этих сегментах относительно регистра SS. Комбинированный тип STACK (стек) аналогичен комбинированному типу PUBLIC, за исключением того, что регистр SS является стандартным сегментным регистром для сегментов стека. Регистр SP устанавливается на конец объединенного сегмента стека. Если не указано ни одного сегмента стека, компоновщик выдаст предупреждение, что стековый сегмент не найден. Если сегмент стека создан, а комбинированный тип STACK не используется, программист должен явно загрузить в регистр SS адрес сегмента (подобно тому, как это делается для регистра DS).

    Атрибут размера сегмента dim. Для процессоров i80386 и выше сегменты могут быть 16- или 32-разрядными. Это влияет прежде всего на размер сегмента и порядок формирования физического адреса внутри него. Атрибут может принимать следующие значения:

    • USE16 — это означает, что сегмент допускает 16-разрядную адресацию. При формировании физического адреса может использоваться только 16-разрядное смещение. Соответственно, такой сегмент может содержать до 64 Кбайт кода или данных;
    • USE32 — сегмент будет 32-разрядным. При формировании физического адреса может использоваться 32-разрядное смещение. Поэтому такой сегмент может содержать до 4 Гбайт кода или данных. В модели памяти FLAT используется по умолчанию именно это значение атрибута размера сегмента

    Атрибут класса сегмента (тип класса) ‘class’ — это заключенная в кавычки строка, помогающая компоновщику определить соответствующий порядок следования сегментов при сборке программы из сегментов нескольких модулей.

    Компоновщик объединяет вместе в памяти все сегменты с одним и тем же именем класса (имя класса, в общем случае, может быть любым, но лучше, если оно будет отражать функциональное назначение сегмента).

    Типичным примером использования имени класса является объединение в группу всех сегментов кода программы (обычно для этого используется класс ‘code’). С помощью механизма типизации класса можно группировать также сегменты инициализированных и неинициализированных данных.

    Все сегменты сами по себе равноправны, так как директивы SEGMENT и ENDS не содержат информации о функциональном назначении сегментов. Для того чтобы использовать их как сегменты кода, данных или стека, необходимо предварительно сообщить транслятору об этом, для чего используют специальную директиву ASSUME.

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

    Привязка сегментов к сегментным регистрам осуществляется с помощью операндов этой директивы, в которых ИмяСегмента должно быть именем сегмента, определенным в исходном тексте программы директивой SEGMENT или ключевым словом nothing.

    Если в качестве операнда используется только ключевое слово nothing, то предшествующие назначения сегментных регистров аннулируются, причем сразу для всех шести сегментных регистров.

    Но ключевое слово nothing можно использовать вместо аргумента ИмяСегмента, в этом случае будет выборочно разрываться связь между сегментом с именем ИмяСегмента и соответствующим сегментным регистром.
    Директива SEGMENT может применяться с любой моделью памяти. При использовании директивы SEGMENT с моделью flat требуется указать транслятору, что все сегментные регистры устанавливаются в соответствии с моделью памяти flat. Это можно сделать при помощи директивы ASSUME:

    ASSUME CS:FLAT, DS:FLAT, SS:FLAT, ES:FLAT, FS:ERROR, GS:ERROR

    Регистры FS и GS программами не используются, поэтому для них указывается атрибут ERROR.

    Назад

    Назад: Язык ассемблера

    Источник: https://prog-cpp.ru/asm-segment/

    Директивы ассемблера, “шапка” программы

    «Шапка» программы начинается с подключения специального файла с расширением INC, в виде строчки #include

    Далее идет строчка с указанием типа микроконтроллера в виде директивы LIST p=16F628A.

    После нее строчка __CONFIG 3F01h ( __CONFIG – директива установки битов конфигурации), здесь двухбайтным шестнадцатеричным числом 3F01h (значение слова конфигурации) настраиваются биты конфигурации микроконтроллера (выбор кварцевого генератора, сторожевой таймер, защита памяти программ и др.). Для лучшей наглядности при настройке битов можно прописать значение слова конфигурации в двоичной форме __CONFIG b’11111100000001′.

    Теперь необходимо присвоить названия адресам регистров ОЗУ, которые будут использоваться в программе. Делается это для удобного написания программы.

    Присвоение название осуществляется директивой equ (определение константы), например, var equ 0020h.

    Записи var в этом случае будет соответствовать число 0020h, которое является адресом регистра ОЗУ в памяти данных, кстати, в файле P16F628A.INC названия регистрам специального назначения присвоены таким же образом.

    Я также использую директиву #define (определение текстовой последовательности для замены), чтобы присвоить названия линиям порта микроконтроллера. Например, строка #define led PORTВ,0 означает что, запись led будет заменяться на PORTB,0. Составляя текст программы не надо везде писать PORTB,0, а пишем просто led.

    Все вышесказанное относится к “шапке” программы, после которой следует сам текст программы. Директивой org (определение начального адреса) задают начальный адрес в памяти программ, после которого будет располагаться код программы.

    Строка org 0000h означает что, код программы расположиться, начиная с 0-го адреса памяти программ.

    Подпрограмма обработчика прерываний должна располагаться, начиная с адреса 0004h, поэтому в начале подпрограммы необходимо написать строку org 0004h, при этом до этой строки надо написать org 0000h и команду перехода GOTO на основную программу, иначе исполнение основной программы начнется с подпрограммы обработчика прерываний. Если прерывания не задействованы, можно не пользоваться директивой org, программа автоматически расположиться с нулевого адреса памяти программ. Вообще с помощью этой директивы можно расположить требуемый участок кода в любую область памяти программ.

    Директива end в конце программы указывает на окончание программы, конец всех команд.
    Таким образом, шапка программы выглядит примерно следующим образом:

                  #include     ;подключение файла P16F628A.INC              LIST        p=16F628A      ;указание типа процессора              __CONFIG    3F18h          ;установка битов конфигурацииSec           equ         0020h          ;присвоение названий регистрам ОЗУ#define       led         PORTA,7         ;присвоение названий линиям порта              org         0000h           ;начать выполнение программы с адреса 0000h              goto        Start           ;переход на метку Start;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;Подпрограмма обработки прерываний              org         0004h           ;начать выполнение подпрограммы с адреса 0004h              ……………..           ;тело подпрограммы                  retfie                      ;выход из подпрограммы прерывания;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;Start         ……………..           ;тело основной программы                  end                         ;конец всей программы

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

    Последние записи:

    Источник: http://radiolaba.ru/programmirovanie-pic-kontrollerov/direktivyi-assemblera-shapka-programmyi.html

Ссылка на основную публикацию
Adblock
detector
",css:{backgroundColor:"#000",opacity:.6}},container:{block:void 0,tpl:"
"},wrap:void 0,body:void 0,errors:{tpl:"
",autoclose_delay:2e3,ajax_unsuccessful_load:"Error"},openEffect:{type:"fade",speed:400},closeEffect:{type:"fade",speed:400},beforeOpen:n.noop,afterOpen:n.noop,beforeClose:n.noop,afterClose:n.noop,afterLoading:n.noop,afterLoadingOnShow:n.noop,errorLoading:n.noop},o=0,p=n([]),h={isEventOut:function(a,b){var c=!0;return n(a).each(function(){n(b.target).get(0)==n(this).get(0)&&(c=!1),0==n(b.target).closest("HTML",n(this).get(0)).length&&(c=!1)}),c}},q={getParentEl:function(a){var b=n(a);return b.data("arcticmodal")?b:(b=n(a).closest(".arcticmodal-container").data("arcticmodalParentEl"),!!b&&b)},transition:function(a,b,c,d){switch(d=null==d?n.noop:d,c.type){case"fade":"show"==b?a.fadeIn(c.speed,d):a.fadeOut(c.speed,d);break;case"none":"show"==b?a.show():a.hide(),d();}},prepare_body:function(a,b){n(".arcticmodal-close",a.body).unbind("click.arcticmodal").bind("click.arcticmodal",function(){return b.arcticmodal("close"),!1})},init_el:function(d,a){var b=d.data("arcticmodal");if(!b){if(b=a,o++,b.modalID=o,b.overlay.block=n(b.overlay.tpl),b.overlay.block.css(b.overlay.css),b.container.block=n(b.container.tpl),b.body=n(".arcticmodal-container_i2",b.container.block),a.clone?b.body.html(d.clone(!0)):(d.before("
"),b.body.html(d)),q.prepare_body(b,d),b.closeOnOverlayClick&&b.overlay.block.add(b.container.block).click(function(a){h.isEventOut(n(">*",b.body),a)&&d.arcticmodal("close")}),b.container.block.data("arcticmodalParentEl",d),d.data("arcticmodal",b),p=n.merge(p,d),n.proxy(e.show,d)(),"html"==b.type)return d;if(null!=b.ajax.beforeSend){var c=b.ajax.beforeSend;delete b.ajax.beforeSend}if(null!=b.ajax.success){var f=b.ajax.success;delete b.ajax.success}if(null!=b.ajax.error){var g=b.ajax.error;delete b.ajax.error}var j=n.extend(!0,{url:b.url,beforeSend:function(){null==c?b.body.html("
"):c(b,d)},success:function(c){d.trigger("afterLoading"),b.afterLoading(b,d,c),null==f?b.body.html(c):f(b,d,c),q.prepare_body(b,d),d.trigger("afterLoadingOnShow"),b.afterLoadingOnShow(b,d,c)},error:function(){d.trigger("errorLoading"),b.errorLoading(b,d),null==g?(b.body.html(b.errors.tpl),n(".arcticmodal-error",b.body).html(b.errors.ajax_unsuccessful_load),n(".arcticmodal-close",b.body).click(function(){return d.arcticmodal("close"),!1}),b.errors.autoclose_delay&&setTimeout(function(){d.arcticmodal("close")},b.errors.autoclose_delay)):g(b,d)}},b.ajax);b.ajax_request=n.ajax(j),d.data("arcticmodal",b)}},init:function(b){if(b=n.extend(!0,{},a,b),!n.isFunction(this))return this.each(function(){q.init_el(n(this),n.extend(!0,{},b))});if(null==b)return void n.error("jquery.arcticmodal: Uncorrect parameters");if(""==b.type)return void n.error("jquery.arcticmodal: Don't set parameter \"type\"");switch(b.type){case"html":if(""==b.content)return void n.error("jquery.arcticmodal: Don't set parameter \"content\"");var e=b.content;return b.content="",q.init_el(n(e),b);case"ajax":return""==b.url?void n.error("jquery.arcticmodal: Don't set parameter \"url\""):q.init_el(n("
"),b);}}},e={show:function(){var a=q.getParentEl(this);if(!1===a)return void n.error("jquery.arcticmodal: Uncorrect call");var b=a.data("arcticmodal");if(b.overlay.block.hide(),b.container.block.hide(),n("BODY").append(b.overlay.block),n("BODY").append(b.container.block),b.beforeOpen(b,a),a.trigger("beforeOpen"),"hidden"!=b.wrap.css("overflow")){b.wrap.data("arcticmodalOverflow",b.wrap.css("overflow"));var c=b.wrap.outerWidth(!0);b.wrap.css("overflow","hidden");var d=b.wrap.outerWidth(!0);d!=c&&b.wrap.css("marginRight",d-c+"px")}return p.not(a).each(function(){var a=n(this).data("arcticmodal");a.overlay.block.hide()}),q.transition(b.overlay.block,"show",1*")),b.overlay.block.remove(),b.container.block.remove(),a.data("arcticmodal",null),n(".arcticmodal-container").length||(b.wrap.data("arcticmodalOverflow")&&b.wrap.css("overflow",b.wrap.data("arcticmodalOverflow")),b.wrap.css("marginRight",0))}),"ajax"==b.type&&b.ajax_request.abort(),p=p.not(a))})},setDefault:function(b){n.extend(!0,a,b)}};n(function(){a.wrap=n(document.all&&!document.querySelector?"html":"body")}),n(document).bind("keyup.arcticmodal",function(d){var a=p.last();if(a.length){var b=a.data("arcticmodal");b.closeOnEsc&&27===d.keyCode&&a.arcticmodal("close")}}),n.arcticmodal=n.fn.arcticmodal=function(a){return e[a]?e[a].apply(this,Array.prototype.slice.call(arguments,1)):"object"!=typeof a&&a?void n.error("jquery.arcticmodal: Method "+a+" does not exist"):q.init.apply(this,arguments)}}(jQuery)}var debugMode="undefined"!=typeof debugFlatPM&&debugFlatPM,duplicateMode="undefined"!=typeof duplicateFlatPM&&duplicateFlatPM,countMode="undefined"!=typeof countFlatPM&&countFlatPM;document["wri"+"te"]=function(a){let b=document.createElement("div");jQuery(document.currentScript).after(b),flatPM_setHTML(b,a),jQuery(b).contents().unwrap()};function flatPM_sticky(c,d,e=0){function f(){if(null==a){let b=getComputedStyle(g,""),c="";for(let a=0;a=b.top-h?b.top-h{const d=c.split("=");return d[0]===a?decodeURIComponent(d[1]):b},""),c=""==b?void 0:b;return c}function flatPM_testCookie(){let a="test_56445";try{return localStorage.setItem(a,a),localStorage.removeItem(a),!0}catch(a){return!1}}function flatPM_grep(a,b,c){return jQuery.grep(a,(a,d)=>c?d==b:0==(d+1)%b)}function flatPM_random(a,b){return Math.floor(Math.random()*(b-a+1))+a}