Первая программа на Assembler. — it-black.ru
Первая программа на Assembler. — it-black.ruВот и пришло время написать нашу первую программу на языке Assembler. Начнем мы с процессора Intel 8086. Будем писать программу под DOS. Программирование под Windows и Linux сложнее. Поэтому начнем с простого и понятного 16-битного процессора 8086.
DOS (дисковая операционная система, ДОС) — семейство операционных систем для персональных компьютеров, ориентированных на использование дисковых накопителей, таких как жёсткий диск и дискета.
DOS является однозадачной операционной системой. После запуска управление передаётся прикладной программе, которая получает в своё распоряжение все ресурсы компьютера и может осуществлять ввод-вывод посредством как функций, предоставляемых операционной системой, так и функций базовой системы ввода-вывода (BIOS), а также работать с устройствами напрямую.
Практическая ценность от программирования под DOS в наше время не очень большая.
Необходимые инструменты:
Для программирования на ассемблере нам необходим компилятор. Наиболее известные компиляторы это TASM, MASM и FASM. В этом курсе я решил использовать FASM. Это довольно новый, удобный, быстро развивающийся компилятор ассемблера, написанный на себе самом. Его преимущества — это поддержка сложных макросов и мультиплатформенность. Есть версии под DOS, Windows и Linux.
С его помощью можно сгенерировать файл любого формата, не обязательно исполняемый файл, так что FASM — это превосходный инструмент для экспериментов и исследований. Скачать его вы можете на официальном сервере.
Если у вас Windows, тогда устанавливать надо на 32 битную версию (х86) т.к. на 64 битной версии (х64) будет выдавать ошибку. Мой компьютер 64 битной версии, поэтому я установил Windows 7 32 bit на виртуальную машину и уже туда установил FASM.
Как установить Windows на виртуальную машину я объяснял в своей статье “Установка Windows 7 на виртуальную машину“.Приступим к написанию первой программы:
После всех манипуляций открывайте FASM и начнем писать код. Как и всегда мы напишем приветствие и выведим его в консоль. Я напишу код и внизу него объясню каждую строчку.
use16 org 100h mov dx,hello mov ah,9 int 21h mov ax,4C00h int 21h hello db 'Hello, World!$'
«use16» – сообщает, что нужно генерировать 16-битный код. Нам нужен именно такой для нашей первой программы.
«org 100h» – объясняет, что следующие команды и данные будут располагаться в памяти, начиная с адреса 100h. Дело в том, что при загрузке нашей программы в память, DOS размещает в первых 256 байтах (с адресов 0000h — 00FFh) свои служебные данные. Нам эти данные изменять нежелательно.
“mov dx,hello” – Помещаем “hello” в регист dx. Делаем что-то типо переменной.
“mov ah,9” – пишем номер функции DOS.
“int 21h” – обращаемся к функции DOS.
“mov ax,4C00h и int 21h” — это стандартное завершение процесса в DOS. Так мы будем завершать все наши программы.
“hello db ‘Hello, World!$’” – сообщаем что в “hello” хранится наше приветствие, которое будет выведено в консоль.
Чтобы откомпилировать программу надо выбрать меню Run->Compile. FASM предложит сохранить файл, если вы этого ещё не сделали, а затем скомпилирует. То есть переведет текст, набранный нами, в машинный код и сделает его программой.
Далее откройте консоль на вашем компьютере (Пуск->Выполнить->cmd и нажимаем Enter, откроется консоль). Потом зайдите в папку где вы сохранили файлы программы, найдите файл с расширением . COM (приложение DOS) возьмите его мышкой перенесите в консоль (либо пропишите путь к этому файлу) и нажмите Enter. После таких манипуляций вы увидите в консоли наше приветствие. Вот так все и работает. Поздравляю все получилось!
Поделиться в facebook
Поделиться в twitter
Поделиться в vk
VK
Поделиться в google
Google+
- Виктор Черемных
- 11 августа, 2017
- 4 комментов
Группа в VK
Помощь проекту
Обнаружили опечатку?
Сообщите нам об этом, выделите текст с ошибкой и нажмите Ctrl+Enter, будем очень признательны!
Свежие статьи
Облако меток
Похожие статьи
Команды работы с битами.
Работать с отдельными битами операндов можно, используя логические операции и сдвиги. Также в системе команд x86 существуют специальные команды для работы с битами: это команды
Основы создания макросов в Assembler.
Макросы — это шаблоны для генерации кода. Один раз создав макрос, можно использовать его во многих местах в коде программы. Макросы делают процесс программирования на
Синтаксис объявления меток.
Метка в ассемблере – это символьное имя, обозначающее ячейку памяти, которая содержит некоторую команду. Метка может содержать следующие символы: Буквы (от A до Z и
Локальные переменные.
Локальные переменные в Assembler используются для хранения промежуточных результатов во время выполнения процедуры. В отличие от глобальных, эти переменные являются временными и создаются при запуске
Полезно знать
Рубрики
Авторы
© it-black.
ru | 2016 — 2022Hello World на Ассемблере
Главная / Ассемблер / Для чайников / Введение в Ассемблер /
После примеров простых программ на Паскале и С++ вы, наверно, не ожидали что я сразу перепрыгну на Ассемблер. Но вот перепрыгнул. И сегодня мы поприветствуем мир на языке ассемблера.
Итак, вот сразу пример, а потом его рассмотрим:
.model tiny .code ORG 100h begin: MOV AH, 9 ; Функция вывода строки на экран MOV DX, OFFSET Msg ; Адрес строки INT 21h ; Выполнить функцию RET ; Вернуться в операционную систему Msg DB 'Hello, World!!!$' ; Строка для вывода END begin
Как видите, программа на ассемблере, даже такая простая, содержит исходный текст значительно большего размера по сравнению с аналогичной программой на С++, а уж тем более на Паскале.
С другой стороны, это не так уж и страшно, как иногда думают те, кто никогда не программировал на Ассемблере.
Во всех подробностях разбирать этот код не будем — подробности в другой раз. Рассмотрим только основные инструкции.
Программа начинается с метки begin
. В отличие, например, от Паскаля, это слово может быть каким угодно,
например, start
. Это всего лишь метка, которая обозначает начало какого-то участка кода.
К конце программы мы видим END begin
. Инструкция
говорит ассемблеру,
что следующее за ней слово означает конец блока кода, обозначенного этим словом. В нашем примере это означает,
что здесь кончается блок кода, начинающийся со слова begin
.
Далее уже начинается программа. Сначала в регистр АН мы записываем номер функции, которую собираемся потом выполнить. Номер 9 — это функция BIOS, которая выполняет вывод на устройство вывода. По умолчанию это монитор.
Затем в регистр DX мы записываем адрес строки. Адрес вычисляется с помощью оператора OFFSET
. Например:
OFFSET Msg
Здесь мы получаем адрес первого байта блока данных, обозначенного идентификатором Msg
.
21h
. Это прерывание выполняет функцию, номер которой записан в регистре АН. Поскольку у нас там записана функция 9, то прерывание выведет на экран строку.
Команда RET
выполняет выход из процедуры или из программы. В нашем случае из программы. Таким образом программа завершается и мы возвращаемся в операционную систему.
Ещё несколько слов об объявлении строки:
Msg DB 'Hello, World!!!$'
Вначале мы записываем идентификатор (в нашем случае Msg
, но может быть и любой дугой),
чтобы было проще работать со строкой. Затем пишем DB — Define Byte — Определить Байт. В нашем случае это будет массив байтов, в котором каждый элемент имеет размер один байт.
Потом пишем саму строку. Каждый символ занимает один байт, поэтому мы и объявили массив байтов. Знак доллара означает конец строки. Так функция вывода понимает, где она должна завершить вывод. Если этот знак не поставить, то будет выведено множество символов — сначала наша строка, а потом разный мусор, содержащийся в ячейках памяти, следующих за нашей строкой. А в эмуляторах это вообще может считаться ошибкой и вывода не будет.
Ну вот мы и написали свою первую программу на языке ассемблера. Если что-то пропустили, то посмотрите видео:
avr-libc: avr-libc и программы на ассемблере
Может быть несколько причин для написания кода для микроконтроллеров AVR с использованием простого исходного кода на ассемблере. Среди них:
- Код для устройств, которые не имеют оперативной памяти и поэтому не поддерживаются компилятором C.
- Код для очень критичных ко времени приложений.
- Специальные настройки, которые нельзя выполнить в C.
Обычно все, кроме первого, можно легко сделать с помощью встроенного ассемблера компилятора.
Хотя avr-libc в первую очередь предназначена для поддержки программирования микроконтроллеров AVR с использованием языка C (и C++), существует также ограниченная поддержка прямого использования ассемблера. Преимущества этого:
- Использование препроцессора C и, следовательно, возможность использовать те же символические константы, которые доступны для программ C, а также гибкая концепция макроса, которая может использовать любой допустимый идентификатор C в качестве макроса (тогда как концепция макроса ассемблера в основном нацелена на использование макроса вместо инструкции ассемблера).
- Использование среды выполнения, например автоматическое назначение векторов прерываний. Для устройств с ОЗУ также можно использовать инициализацию переменных ОЗУ.
Для целей, описанных в этом документе, ассемблер и компоновщик обычно не вызываются вручную, а с помощью внешнего интерфейса компилятора C ( avr-gcc
), который, в свою очередь, вызывает ассемблер и компоновщик по мере необходимости.
Этот подход имеет следующие преимущества:
- Прямой вызов в основном только одной программы,
avr-gcc
независимо от фактического используемого исходного языка. - Вызов препроцессора C будет автоматическим и будет включать соответствующие параметры для поиска необходимых включаемых файлов в файловой системе.
- Вызов компоновщика будет автоматическим и будет включать в себя соответствующие параметры для поиска дополнительных библиотек, а также код запуска приложения (
crt
XXX.o
) и скрипт компоновщика.
Обратите внимание, что вызов препроцессора C будет автоматическим, когда имя файла, предоставленное для файла ассемблера, заканчивается на
. С (заглавная буква «с»). Это применимо даже к операционным системам, которые используют файловые системы без учета регистра, поскольку фактическое решение принимается на основе регистра суффикса имени файла, указанного в командной строке, а не на основе фактического имени файла из файловой системы.
В качестве альтернативы использованию
.S для этой цели распознается суффикс
.sx (начиная с GCC 4.3.0). Это в первую очередь предназначено для совместимости с другими средами компилятора, которые ранее предоставляли этот вариант, чтобы справиться с операционными системами, в которых имена файлов нечувствительны к регистру (и, с некоторыми версиями 9).0023 сделать , который не мог различить
.s и
.S в таких системах).
Кроме того, язык можно явно указать с помощью параметра -x assembler-with-cpp
.
В следующем аннотированном примере используется простой генератор прямоугольных сигналов с частотой 100 кГц, использующий AT90S1200 с тактовой частотой 10,7 МГц. Контакт PD6 будет использоваться для вывода прямоугольной волны.
#include
работа = 16 ; Примечание [2]
tmp = 17
inttmp = 19
intsav = 0
КВАДРАТ = PD6 ; Примечание [3]
; Примечание [4]:
tmconst= 10700000 / 200000 ; 100 кГц => 200000 фронтов/с
fuzz= 8 ; # часы в ISR, пока не будет установлен TCNT0
.section .text
.global main ; Примечание [5]
main:
rcall ioinit
1:
rjmp 1b ; Примечание [6]
.global TIMER0_OVF_vect ; Примечание [7]
TIMER0_OVF_vect:
ldi inttmp, 256 — tmconst + fuzz
out _SFR_IO_ADDR(TCNT0), inttmp ; Примечание [8]
в intsav, _SFR_IO_ADDR(SREG) ; Note [9]
sbic _SFR_IO_ADDR(PORTD), SQUARE
rjmp 1f
sbi _SFR_IO_ADDR(PORTD), SQUARE
rjmp 2f
1: cbi _SFR_IO_ADDR(PORTD), SQUARE
2:
out _SFR_IO_ADDR( SREG), intsav
reti
ioinit:
sbi _SFR_IO_ADDR(DDRD), SQUARE
ldi work, _BV(TOIE0)
out _SFR_IO_ADDR(TIMSK), work
ldi work, _BV(CS00) ; tmr0: CK/1
out _SFR_IO_ADDR(TCCR0), work
ldi work, 256 — tmconst
out _SFR_IO_ADDR(TCNT0), work
sei
2 ret
2 _; Примечание [10] __vector_default:
reti
. end
- Примечание [1]
Как и в программах на C, сюда входит файл для центрального процессора, содержащий определения портов ввода-вывода. Обратите внимание, что не все включаемые файлы могут быть включены в исходники на ассемблере.
- Примечание [2]
Назначение регистров символическим именам, используемым локально. Другим вариантом может быть использование вместо этого макроса препроцессора C:
#define work 16
- Примечание [3]
Номер бита для вывода прямоугольной волны. Обратите внимание, что правая часть состоит из макроса CPP, который будет заменен своим значением (в данном случае 6) перед фактической передачей ассемблеру.
- Примечание [4]
Ассемблер использует операции с целыми числами в определяемом хостом целочисленном размере (32 бита или более) при вычислении выражений. Это отличается от компилятора C, который по умолчанию использует тип C int
для вычисления константных целочисленных выражений.
Чтобы получить на выходе 100 кГц, нам нужно переключать линию PD6 200000 раз в секунду. Поскольку мы используем таймер 0 без каких-либо опций предварительного масштабирования, чтобы получить желаемую частоту и точность, мы уже упираемся в серьезные соображения по времени: при приеме и обработке прерывания переполнения таймер уже продолжает считать. При предварительной загрузке TCCNT0
, поэтому мы должны учитывать количество тактов, необходимых для подтверждения прерывания, и количество инструкций по перезагрузке TCCNT0
(4 такта для подтверждения прерывания, 2 такта для перехода от вектора прерывания, 2 такта для 2 инструкции, которые перезагружают TCCNT0
). Вот для чего нужна константа fuzz
.
- Примечание [5]
Внешние функции должны быть объявлены равными
.глобальный. main
— это точка входа приложения, к которой будет выполнен переход из процедуры инициализации в crts1200. o
.
- Примечание [6]
Основной цикл — это всего лишь один прыжок назад к самому себе. Сама генерация прямоугольных импульсов полностью обрабатывается службой прерывания по переполнению таймера 0. Также можно использовать команду sleep
(с использованием режима ожидания), но, вероятно, в любом случае это не сэкономит много энергии, поскольку служба прерывания выполняется довольно часто.
- Примечание [7]
Функции прерывания могут получать обычные имена, которые также доступны программам на C. Затем компоновщик поместит их в соответствующие слоты вектора прерывания. Обратите внимание, что они должны быть объявлены
.global, чтобы быть приемлемыми для этой цели. Это будет работать, только если
был включен. Обратите внимание, что ассемблер или компоновщик не имеют возможности проверить правильность написания функции прерывания, поэтому ее следует перепроверить. (При анализе полученного объектного файла с помощью AVR-ABJDUMP
или AVR-NM
, должно появиться имя, такое как __VECTOR_ N
, с N , являющимся небольшим целым номером.)
- Примечание [8]
- . раздел о регистрах специальных функций, фактический адрес порта ввода-вывода должен быть получен с помощью макроса
_SFR_IO_ADDR
. (AT90S1200 не имеет оперативной памяти, поэтому доступ к регистрам ввода-вывода с отображением памяти недоступен. Это будет медленнее, чем использование в
/ из
инструкций в любом случае.)
Поскольку операция перезагрузки TCCNT0
критична по времени, она выполняется даже перед сохранением SREG
. Очевидно, для этого требуется, чтобы задействованные инструкции не изменяли ни один из битов флага в SREG
. - Примечание [9]
Процедуры прерывания не должны затирать глобальное состояние ЦП. Таким образом, обычно необходимо сохранить хотя бы состояние флаговых битов в СРЕГ
. (Обратите внимание, что это служит здесь только в качестве примера, так как на самом деле все следующие инструкции также не изменяют SREG
, но обычно это не так.)
Кроме того, необходимо убедиться, что регистры, используемые внутри процедуры обработки прерывания, не конфликт с теми, которые используются снаружи. В случае устройства без ОЗУ, такого как AT90S1200, это можно сделать только путем согласования набора регистров, которые будут использоваться исключительно внутри процедуры прерывания; другого шанса «сохранить» реестр не было бы.
Если подпрограмма прерывания должна быть связана с модулями C, необходимо соблюдать правила использования регистров, установленные компилятором C. Кроме того, любой регистр, измененный внутри службы прерывания, необходимо сохранить, обычно в стеке.
- Примечание [10]
Как объяснено в разделе Прерывания, глобальный «универсальный» обработчик прерывания, который получает все неназначенные векторы прерывания, может быть установлен с использованием имени __vector_default
. Это должно быть
.global и, очевидно, должен заканчиваться инструкцией reti
. (По умолчанию вместо этого будет подразумеваться переход к ячейке 0.)
Доступные псевдооперации в ассемблере описаны в руководстве по ассемблеру GNU (gas). Руководство можно найти в Интернете как часть текущей версии binutils по адресу http://sources.redhat.com/binutils/.
Поскольку gas исходит из Unix, его псевдооперационный и общий синтаксис ассемблера немного отличается от того, который используется другими ассемблерами. Числовые константы следуют нотации C (префикс 0x
для шестнадцатеричных констант), выражения используют C-подобный синтаксис.
Некоторые распространенные псевдооперации включают:
-
.byte выделяет однобайтовые константы -
.ascii выделяет непрерываемую строку символов -
.asciz выделяет строку символов, заканчивающуюся \0 (строка C) -
. data переключается в раздел .data (инициализированные переменные RAM) -
.text переключается на раздел .text (код и константы ПЗУ) -
.set объявляет символ как постоянное выражение (идентично
.equ) -
.global (или
.globl) объявляет общедоступный символ, который виден компоновщику (например, точка входа в функцию, глобальная переменная) -
.extern объявляет символ, определяемый извне; это фактически только комментарий, так как газ все равно обрабатывает все неопределенные символы, с которыми он сталкивается, как глобально неопределенные
Обратите внимание, что
.org также доступен в газе, но это довольно бессмысленная псевдооперация в среде ассемблера, использующая перемещаемые объектные файлы, поскольку именно компоновщик определяет конечную позицию некоторого объекта в ПЗУ или ОЗУ.
Наряду с независимыми от архитектуры стандартными операторами доступны некоторые специфичные для AVR операторы, которые, к сожалению, еще не описаны в официальной документации. Наиболее известные операторы:
-
lo8
Берет младшие 8 битов 16-битного целого числа -
hi8
Берет старшие 8 битов 16-битного целого числа -
pm
Берет адрес памяти программ (ПЗУ) и преобразует его в адрес ОЗУ. Это подразумевает деление на 2, поскольку AVR обрабатывает адреса ПЗУ как 16-битные слова (например, в инструкции IJMP
или ICALL
), а также может обрабатывать перемещаемые символы с правой стороны.
Пример:
ldi r24, lo8(pm(somefunc))
ldi r25, hi8 (pm (somefunc))
назови что-нибудь
Это передает адрес функции somefunc
в качестве первого параметра функции something
.
Bixoft — Примеры программ на ассемблере
Bixoft — Примеры программ на ассемблере Эта страница содержит следующие фрагменты кода:
- Стандартная связь подпрограммы
- Условная сборка
- Самомодифицирующийся код
- Реентерабельная обработка наборов данных
Мы хотели бы отметить, хотя это может быть излишним, что
стандартный
назначения регистров следующие:
Регистр Функция Регистр 0 Рабочий регистр, в основном используется как параметр Регистр 1 Рабочий регистр, обычно используемый как указатель на список параметров или
поле результата Регистры 2-11 Может свободно назначаться внутри каждой программы Регистр 12 Базовый регистр для адресации внутри программы Регистр 13 Указатель на свободную область сохранения Регистр 14 Рабочий регистр, обычно используемый для обратных адресов Регистр 15 Рабочий регистр, используемый для адреса подпрограммы или (по возврату)
возвращаться-
и коды причин
Стандартный вызов подпрограммы.
Применяется даже самый распространенный способ вызова подпрограммы.
И все же это
рекомендуется следовать многолетней практике. Это улучшает оба
удобочитаемость и
ремонтопригодность ваших программ.
Обычное соглашение:
- При звонке:
- Регистр 0 содержит параметр (при необходимости)
- Регистр 1 содержит адрес списка параметров
- Регистр 13 содержит адрес области сохранения
- Регистр 14 содержит обратный адрес
- Регистр 15 содержит адрес точки входа вызываемого
программа
- По возвращении:
- Регистр 0 может быть изменен или не изменен
- Регистр 1 содержит адрес (если применимо) результатов
- Регистры со 2 по 14 без изменений
- Регистр 15 содержит возврат и (если применимо)
код причины
Область сохранения состоит из 18 полных слов (по 4 байта каждое) и занимает
этим
правила:
Местоположение Контент Х’00’ Зарезервировано для PL/I Х’04’ Указатель на предыдущую область сохранения (т. е. регистр 13) Х’08’ Указатель на следующую область сохранения Х’0С’ Регистр 14 Х’10’ Регистр 15 Х’14’ Регистр 0 и т. д. до регистра 12
Далее следуют три фрагмента кода:
- Вызов подпрограммы.
- Вызываемая многоразовая программа.
Этот пример можно использовать только для многоразовых и однократных
программы,
но не для повторно вводимых или обновляемых программ. - Вызываемая обновляемая программа.
Этот пример предназначен для программ с возможностью повторного ввода и обновления.
Конечно
его можно использовать как для многоразовых, так и для одноразовых программ.
*
* Пример вызова подпрограммы.
*
* Мы предполагаем, что список параметров настроен
LA R1,PLIST Reg.1 указывает на список параметров
L R15,=V(SUBPROG) Адрес подпрограммы в регистре 15
BALR R14,R15 Заполнить регистр 14 и вызвать подпрограмму
* Обработайте результат
LTR R0, R15 Сохранить и проверить код возврата
BNZ ERROR Код возврата не равен нулю: обработка ошибок
ХОРОШЕЕ ОБОРУДОВАНИЕ *
* . .. Другой программный код
ОШИБКА N R15,=X'00000FFF' Удалить код причины
L R15,ERRORS(R15) Получить адрес процедуры обработки ошибок из
стол
BR R15 И выполнить программу ошибок
*
* Процедуры обработки ошибок для обработки неожиданных результатов от SUBPROG
* При входе reg.0 содержит коды возврата и причины
ОШИБКИ DC AL4(хорошо) Результат в порядке
DC AL4(RETCD4) Код возврата 4: предупреждение
DC AL4 (RETCD8) Код возврата 8: проблема
DC AL4 (RETCD12) Код возврата 12: не определено
DC AL4 (RETCD16) Код возврата 16: фатальная ошибка
*
* Пример вызываемой программы (одноразовой или многоразовой)
* Этот пример нельзя использовать для программ с возможностью повторного ввода или обновления.
*
ПОДПРОГРАММА CSECT
USING SUBPROG,R15 Регистр 15 содержит адрес
ПОДПРОГ
B СТАРТ Пропустить данные
DC C'SUBPROG ' Имя программы
DC C'&SYSDATE' Дата
DC C'&SYSTIME' Время
DC C'V1R2. 05' Номер версии
DS 0H Повторное выравнивание по границе полуслова
*
START STM R14,R12,12(R13) Сохранить регистры
DROP R15 Больше не нужен в качестве базовой регистрации
LR R12,R15 Заполните регистр 12 базовым адресом
ИСПОЛЬЗОВАНИЕ ПОДПРОГРАММЫ, R12 Теперь используйте reg.12 в качестве основы.
LA R14,SAVEAREA Адрес новой области сохранения
ST R13,4(R14) Указатель на предыдущую область сохранения
ST R14,8(R13) Указатель на следующую область сохранения
LR R13,R14 R13 снова указывает на свободную область сохранения
* ... Другой программный код
EXIT L R13,4(R13) Получить адрес предыдущей области сохранения
LM R14,R12,12(R13) Восстановить все регистры (кроме 13)
LA R15,... Код возврата в регистре 15
BR R14 Возврат к вызывающему абоненту
База DROP R12 больше не нужна
*
LTORG Все литералы
SAVEAREA DS 18F Область сохранения
*
* Пример вызываемой программы (обновляемой или повторно вводимой)
* Этот пример также можно использовать для программ многократного или одноразового использования.
*
ПОДПРОГРАММА CSECT
USING SUBPROG,R15 Регистр 15 содержит адрес
ПОДПРОГ
B СТАРТ Пропустить данные
DC C'SUBPROG ' Имя программы
DC C'&SYSDATE' Дата
DC C'&SYSTIME' Время
DC C'V1R2.05' Номер версии
DC 0H Повторное выравнивание по границе полуслова
*
START STM R14,R12,12(R13) Сохранить все регистры
DROP R15 Больше не нужен в качестве базы
LR R12,R15 Заполните регистр 12 базовым адресом
ИСПОЛЬЗОВАНИЕ ПОДПРОГРАММЫ, R12 Используйте регистр 12 в качестве основы.
LA R1,PRIVATE_LEN Требуемый объем памяти
GETMAIN RU,LV=(R1) Выделить память для области сохранения и т.д.
* Адрес выделенного хранилища теперь в регистре 1
USING PRIVATE,R13 Сделать хранилище адресуемым
ST R13,4(R1) Указатель на предыдущую область сохранения
ST R1,8(R13) Указатель на следующую область сохранения
LR R13,R1 R13 снова указывает на свободную область сохранения
* ... Другой программный код
ВЫХОД LR R1,R13 Сохранить адрес нашей частной зоны
L R13,4(R13) Получить адрес предыдущей области сохранения
LA R2,ЧАСТНЫЙ_LEN
FREEMAIN A=(R1),LV=(R2) Свободное выделенное хранилище
LM R14,R12,12(R13) Восстановить все регистры (кроме 13)
LA R15,. .. Код возврата в регистре 15
BR R14 Возврат к вызывающему абоненту
База DROP R12 больше не нужна
*
LTORG Все литералы
*
* Эта dsect описывает все переменные, приватные для каждого вызывающего объекта.
ЧАСТНАЯ DSECT
SAVEAREA DS 18F
* ... Другие частные переменные
PRIVATE_LEN EQU *-ЧАСТНЫЙ
Условная сборка.
В следующем примере показано применение условного
сборка. Первый
показан макрос, который проверяет содержимое JCL-переменной
СИСПАРМ.
Это означает оптимизацию программы, которая должна быть сгенерирована и
включение
срабатывает отладочный код.
Макрос задает две переменные, которые можно тестировать на протяжении
программа
для генерации кода по желанию. Некоторые примеры такой логики приведены как
Что ж.
Ниже приведены следующие четыре фрагмента кода:
- Макрос, как описано выше.
- Вызов макроса.
Этот вызов присваивает глобальным переменным &DBG и &OPT их
намеревался
ценности. - Использование этих переменных для установки других
переменные.
Здесь мы демонстрируем, как можно использовать переменную &DBG для управления
в
вариант печати. - Направление кодогенерации с использованием
переменные.
Здесь мы демонстрируем, как параметр оптимизации можно использовать в открытых
код.
МАКРО
CHECKPRM
.*
.* Программа на ассемблере (ASMA90) принимает в качестве JCL-параметра a
.* спецификация переменной SYSPARM. Значение, введенное в
.* JCL будет передан символу глобального набора с именем &SYSPARM.
.* Значение, указанное в JCL, передается как одна строка.
.* Опции отделяются друг от друга запятой - без пробелов.
.* Этот макрос разбивает строку на отдельные параметры.
.* Затем параметры проверяются и обрабатываются. 4 разных ключевых слова
.* разрешены:
.* - DEBUG : генерировать отладочный код (процедура Snap и т. д.)
.* - NODEBUG: не генерировать отладочный код
.* - OPT: создать оптимизированную программу
. * - NOOPT: создание полнофункциональной программы.
.*
*
* Макрос CHECKPRM тестирует JCL-переменную SYSPARM и устанавливает два глобальных
* переменные для отображения содержимого SYSPARM:
* &DBG включен, чтобы включить код отладки, выключен, чтобы пропустить этот код
* &OPT включен для создания оптимизированного кода, выключен для полного
* функциональный код.
*
ГБЛБ &ДБГ,&ОПТ
&DBG SETB 0 По умолчанию: нет кода отладки
&OPT SETB 1 По умолчанию: полная оптимизация
AIF ('.&SYSPARM' EQ '.').ВЫХОД
*
* Сначала мы разбиваем строку SYSPARM на подстроки
*
LCLC &P(5) Массив, содержащий подстроки (пармы)
LCLA &I,&N,&X
&I SETA 0 Индекс символов для &SYSPARM
&N SETA 1 Следующая позиция для извлечения
&X SETA 1 Счетчик параметров (массив индексов &P)
*
.LOOP1 АНОП
&I SETA &I+1 Увеличить индекс символа
AIF (&I GT K'&SYSPARM.LOOP1X Конец строки?
AIF ('&SYSPARM'(&I,1) NE ',').LOOP1 Конец подстроки?
.* Поместить подстроку в массив &P
&P(&X) SETC '&SYSPARM'(&N,&I-&N) Извлечь подстроку
&N SETA &I+1 Установить указатель на начало следующей подстроки
&X SETA &X+1 Увеличение счетчика подстроки
НАЗАД . LOOP1 Перейти к следующему символу
.*
.LOOP1X ANOP Точка выхода для цикла 1
&P(&X) SETC '&SYSPARM'(&N,&I-&N) Извлечь последнюю подстроку
.*
.* Проверить действительность ключевых слов (теперь в массиве &P)
.*
&I SETA 0 Индекс в массиве &P
.LOOP2 АНОП
&I SETA &I+1 Увеличение указателя параметра
AIF (&I GT &X).LOOP2X Готово? (&X содержит количество параметров)
AIF ('.&P(&I)' EQ '.').LOOP2 Пропустить пустой параметр
AIF ('.&P(&I)' EQ '.OPT').OPT Включить оптимизацию
AIF ('.&P(&I)' EQ '.NOOPT').NOOPT Отключить оптимизацию
AIF ('.&P(&I)' EQ '.DEBUG').DEBUG Включить логику отладки
AIF ('.&P(&I)' EQ '.NODEBUG').NODEBUG Пропустить логику отладки
.* Недопустимое значение: выдать предупреждение и продолжить
MNOTE 4, 'Недопустимый операнд SYSPARM: &P(&I)'
AGO .LOOP2 Перейти к следующему параметру
.*
.ОПТ АНОП
&ОПТ НАБОР 1
MNOTE 0, «Будет создано оптимизированное кодирование»
НАЗАД .LOOP2
.*
.НООПТ АНОП
&ОПТ НАБОР 0
MNOTE 0, «Будет создано отказоустойчивое кодирование»
НАЗАД . LOOP2
.*
.ОТЛАДКА АНОП
&DBG НАБОР 1
MNOTE 0, 'Код отладки будет включен'
НАЗАД .LOOP2
.*
.NODEBUG ANOP
&DBG НАСТРОЙКА 0
MNOTE 0, 'Код отладки будет исключен'
НАЗАД .LOOP2
.*
.LOOP2X ANOP Точка выхода для цикла 2
.EXIT ANOP Точка выхода для пустого SYSPARM
ИСПРАВИТЬ
*
* Глобальный параметр &DBG управляет параметрами сборки отладки/узла.
* - Когда &DBG = 1, то отладка активна
* Оптимизация глобального управления &OPT
* - При &OPT = 1 происходит оптимизация
* - При &OPT = 0 будет включена отказоустойчивость
*
ГБЛБ &ДБГ,&ОПТ
CHECKPRM Проверьте &SYSPARM, чтобы установить &DBG и &OPT
*
SOMEPROG CSECT
НЕКОТОРЫЕ ПРОГРАММЫ 31
SOMEPROG RMODE ЛЮБОЙ
*
... Здесь следует кодировка.
* Теперь установите параметры печати
Вариант печати GBLC &PRT Controls
&PRT SETC 'NOGEN' По умолчанию nogen
AIF (NOT &DBG).NOGEN Отладка активна?
&PRT SETC 'GEN' Да: создать полный список
. НОГЕН АНОП
PRINT &PRT Активировать опцию печати
*
*
* Этот фрагмент кода перемещает данные, как указано в элементе управления перемещением.
* Перемещаемые данные могут иметь длину до 32767 байт.
*
* R6 теперь указывает на элемент управления движением.
* R8 будет использоваться как указатель источника, R9содержащий длину.
* R10 будет использоваться как указатель назначения.
*
ИСПОЛЬЗОВАНИЕ MOVECTL,R6 Сделать область управления перемещением адресной
L R8,MCRECPTR Указывает на начало записи
AH R8,MCRECOFS Добавить смещение к соответствующим данным
LH R9,MCDATLEN Загрузить длину данных
L R10,MCDEST Указать на область назначения
*
* Теперь для перемещения данных мы обычно кодируем MVCL-инструкцию,
* так как MCDATLEN может указывать любое количество данных до 32767 байт.
* Поскольку известно, что в настоящее время ни один MOVECTL-элемент не указывает
* длина более 256, мы можем оптимизировать код с помощью MVC
* вместо MVCL.
*
AIF (&OPT).OPTMOVE
ЛР Р11, Р9Целевая длина всегда экв. Источник-len
MVCL R10, R8 Переместить данные
НАЗАД .MOVEDONE
.*
.OPTMOVE ANOP
BCTR R9,R0 Уменьшить длину на 1 для MVC
EX R9,MOVEDATA Выполнить MVC-инструкцию
Б ПЕРЕМЕЩЕН
MOVEDATA MVC 0(0,R10),0(R8) Переместить данные
МОВЕДОН ЭКВ *
*
.MOVEDONE ANOP
DROP R6 MOVECTL больше не нужен
Самомодифицирующийся код.
Этот пример служит только в качестве иллюстрации. Обычно мы бы очень
сильно
рекомендуем никогда не применять самомодифицирующийся код, потому что он делает
ваш
программы нельзя повторно ввести, и потому что это делает программы очень трудными для
читать
и поддерживать.
Далее следуют два фрагмента кода:
- Процедура инициализации.
Эта подпрограмма выполняется только один раз путем изменения условия перехода
в
начало рутины. - Печать в две колонки.
В этом примере показано очень уродливое решение для печати в два столбца. проблема,
что делает изменение кода операции инструкции. Код операции
измененный
с SH на AH и наоборот, переводя указатель столбца в положение
и сюда.
Гораздо лучшим решением было бы выделить буфер, достаточно большой
к
разместить все строки на странице. Тогда можно было бы заполнить все
левый столбец
— сверху вниз — до заполнения крайней правой колонки. Самый
конечные пользователи предпочитают
такая вертикально перечисленная информация.
* ... Настройка адресации и т.д.
INIT BC X'00',INITDONE Эта ветвь никогда не
OI INIT+1,X'F0' Сделать предыдущую ветвь ветвью - всегда
* ... Здесь идет код инициализации
INITDONE EQU * Конец процедуры инициализации
L R1,LINEPTR Получить последний использованный указатель в LINE
SETPTR SH R1,=H'50' Переключиться на другой столбец (изначально: SH)
XI SETPTR,X'01' Изменить AH на SH или наоборот
ST R1,LINEPTR Сохранить обновленный указатель
MVC 0(40,R1),DATA Переместить данные в строку печати
. .. Другая кодировка для печати строки
ЛИНИЯ ПОСТОЯННОГО ТОКА CL133' '
DS 0F Повторное выравнивание по границе полного слова
LINEPTR DC AL4(LINE+67) Чтобы начать печать данных в
* левый столбик, делаем вид, что последний
* перемещение было в правую колонку.
Реентерабельная обработка набора данных.
Для обработки набора данных VSAM необходимы как ACB, так и RPL.
В приведенном ниже примере показано, как создать ACB и RPL в
возвращающийся
программа. Вы можете предположить, что все именованные места хранения были
распределяются динамически и что к ним можно обращаться с помощью
DSECT.
*
SUBROUTN STM R14,R12,12(R13) Сохранить все регистры
LA R1,SAVEAREA Адрес новой области сохранения
ST R13,4(R1) Указатель на предыдущую область сохранения
ST R1,8(R13) Указатель на следующую область сохранения
LR R13,R1 Reg.13 указывает на свободную зону сохранения
опять таки
*
* Сначала выделяем хранилище для ACB и RPL
*
GETMAIN RC,LV=IFGACBLV+IFGRPLLV Запрос хранилища для ACB + RPL
LTR R15, R15 Getmain в порядке?
BNE ERROR16 Ошибка обрабатывается в другом месте
ST R1,ACBPTR Сохранить указатель на ACB
LA R1,IFGACBLV(R1) Указывает на RPL-часть области
ST R1,RPLPTR Сохранить указатель на RPL
*
* Выделить хранилище для рабочей области
*
ПОЛУЧИТЬ RC, LV = 4096,BNDRY=PAGE Запрос хранилища для рабочей области
LTR R15, R15 Getmain в порядке?
BNE ERROR16 Ошибка обрабатывается в другом месте
ST R1,WORKPTR Сохранить указатель на рабочую область
*
* Создайте PLIST для GENCB ACB в области getmained.
* Для этого GENCB не предусмотрен код возврата.
*
SR R6,R6 Очистить регистр 6
IC R6,SHRPOOL, чтобы содержать номер shrpool
L R7,WORKPTR Указывает на рабочую область для создания PLIST
ИСПОЛЬЗОВАНИЕ РАБОЧЕЙ ЗОНЫ, R7
GENCB BLK=ACB, сгенерировать PLIST для GENCB ACB *
AM=VSAM, метод доступа *
WAREA=(R7), Место для сгенерированного ACB *
LENGTH=IFGACBLV, максимальная длина сгенерированного ACB *
DDNAME=(*,DDNAME), GENCB ACB копирует DDNAME *
SHRPOOL=(S,0(R6)), номер Shrpool варьируется от 0 до 15 *
MACRF=(KEY,DFR,SEQ,SKP,SIS,NSR), Опции для этого ACB *
BUFND=8, минимальное количество буферов данных *
BUFNI=1, минимальное количество индексных буферов *
RMODE31=ALL, Буфер и блоки управления выше 16M *
MF=(L,WORKAREA,GENACBLV) Создать PLIST в WORKAREA
*
* Теперь создайте ACB, используя PLIST в WORKAREA.
*
GENCB BLK=ACB,MF=(E,(R7)) Создать ACB с помощью PLIST в WORKAREA
LTR R15, R15 ACB создан?
BNZ ERROR17 Ошибка обрабатывается в другом месте
DROP R7 WORKAREA больше не нужен
*
* Создайте PLIST для GENCB RPL в области getmained.
* Для этого GENCB не предусмотрен код возврата.
*
SR R6,R6 Очистить регистр
IC R6,KEYLV для указания длины ключа
L R7,ACBPTR Указывает на ACB
LH R8,RECDLV Указать длину записи
Л Р9,RPLPTR И указать местоположение для сгенерированного RPL
L R10,WORKPTR Переадресация рабочей области
ИСПОЛЬЗОВАНИЕ РАБОЧЕЙ ЗОНЫ, R10
*
GENCB BLK=RPL, создать PLIST для GENCB RPL *
AM=VSAM, метод доступа *
WAREA=(R9), место для сгенерированного RPL *
LENGTH=IFGRPLLV, максимальная длина сгенерированного RPL *
ACB=(R7), Укажите ACB-адрес для RPL *
AREA=(S,RECDPTR), указать область данных для записей *
AREALEN=4, длина области данных *
ARG=(S,KEYPTR) Указать расположение ключа *
KEYLEN=(S,0(R6)), И длина ключа в байтах *
ECB=(S,ECBPTR) Указать ECB-адрес *
RECLEN=(R8), И длина записи *
OPTCD=(KEY,SEQ,ASY,NUP,KGE,GEN,LOC), Опции для RPL *
MF=(G,РАБОЧАЯ ОБЛАСТЬ,GENRPLLV)
*
* Теперь создайте RPL, используя PLIST в WORKAREA.
*
GENCB BLK=RPL,MF=(E,(R10)) Сгенерировать RPL, используя PLIST в WORKAREA
LTR R15, R15 RPL создан?
BNZ ERROR18 Ошибка обрабатывается в другом месте
*
* Теперь, когда мы создали и ACB, и RPL, мы можем открыть набор данных
*
L R2,=AL4(VSAMOPEN) Получить адрес открытой формы-списка
MVC WORKAREA(VSAMOPLV),0(R2) Скопировать в рабочую область
L R2,ACBPTR Перезагрузить указатель ACB
OPEN ((R2)),MF=(E,(R10)) Открытый ACB с изменяемым PLIST
LTR R15, R15 Набор данных успешно открыт?
БНЗ ОШИБКА19Ошибка обрабатывается в другом месте
*
* Возврат из подпрограммы
*
L R13,4(R13) Получить адрес предыдущей области сохранения
LM R14,R12,12(R13) Восстановить все регистры (кроме 13)
BR R14 Вернуться к основной линии
*
* Форма списка по умолчанию для открытого макроса
*
VSAMOPEN OPEN (0), ACB-адрес еще не известен *
MODE=31, включить 31-битную адресацию *
MF=L Генерировать только PLIST
Замечания? Вопросы? Дополнительная информация? Выберите тему вашего
выбор или
напишите нам с вашим
вопросы.
int
для вычисления константных целочисленных выражений. Чтобы получить на выходе 100 кГц, нам нужно переключать линию PD6 200000 раз в секунду. Поскольку мы используем таймер 0 без каких-либо опций предварительного масштабирования, чтобы получить желаемую частоту и точность, мы уже упираемся в серьезные соображения по времени: при приеме и обработке прерывания переполнения таймер уже продолжает считать. При предварительной загрузке
TCCNT0
, поэтому мы должны учитывать количество тактов, необходимых для подтверждения прерывания, и количество инструкций по перезагрузке TCCNT0
(4 такта для подтверждения прерывания, 2 такта для перехода от вектора прерывания, 2 такта для 2 инструкции, которые перезагружают TCCNT0
). Вот для чего нужна константа fuzz
.
.глобальный. main
— это точка входа приложения, к которой будет выполнен переход из процедуры инициализации в crts1200. o
. sleep
(с использованием режима ожидания), но, вероятно, в любом случае это не сэкономит много энергии, поскольку служба прерывания выполняется довольно часто.
.global, чтобы быть приемлемыми для этой цели. Это будет работать, только если
был включен. Обратите внимание, что ассемблер или компоновщик не имеют возможности проверить правильность написания функции прерывания, поэтому ее следует перепроверить. (При анализе полученного объектного файла с помощью AVR-ABJDUMP
или AVR-NM
, должно появиться имя, такое как __VECTOR_ N
, с N , являющимся небольшим целым номером.)- . раздел о регистрах специальных функций, фактический адрес порта ввода-вывода должен быть получен с помощью макроса
_SFR_IO_ADDR
. (AT90S1200 не имеет оперативной памяти, поэтому доступ к регистрам ввода-вывода с отображением памяти недоступен. Это будет медленнее, чем использованиев
/из
инструкций в любом случае.)
Поскольку операция перезагрузкиTCCNT0
критична по времени, она выполняется даже перед сохранениемSREG
. Очевидно, для этого требуется, чтобы задействованные инструкции не изменяли ни один из битов флага вSREG
. - Примечание [9]
- Примечание [10]
-
-
-
-
-
-
-
-
-
lo8
Берет младшие 8 битов 16-битного целого числа -
hi8
Берет старшие 8 битов 16-битного целого числа -
pm
Берет адрес памяти программ (ПЗУ) и преобразует его в адрес ОЗУ. Это подразумевает деление на 2, поскольку AVR обрабатывает адреса ПЗУ как 16-битные слова (например, в инструкцииIJMP
илиICALL
), а также может обрабатывать перемещаемые символы с правой стороны. - Стандартная связь подпрограммы
- Условная сборка
- Самомодифицирующийся код
- Реентерабельная обработка наборов данных
- При звонке:
- Регистр 0 содержит параметр (при необходимости)
- Регистр 1 содержит адрес списка параметров
- Регистр 13 содержит адрес области сохранения
- Регистр 14 содержит обратный адрес
- Регистр 15 содержит адрес точки входа вызываемого программа
- По возвращении:
- Регистр 0 может быть изменен или не изменен
- Регистр 1 содержит адрес (если применимо) результатов
- Регистры со 2 по 14 без изменений
- Регистр 15 содержит возврат и (если применимо) код причины
- Вызов подпрограммы.
- Вызываемая многоразовая программа.
Этот пример можно использовать только для многоразовых и однократных программы, но не для повторно вводимых или обновляемых программ. - Вызываемая обновляемая программа.
Этот пример предназначен для программ с возможностью повторного ввода и обновления. Конечно его можно использовать как для многоразовых, так и для одноразовых программ. - Макрос, как описано выше.
- Вызов макроса.
Этот вызов присваивает глобальным переменным &DBG и &OPT их намеревался ценности. - Использование этих переменных для установки других
переменные.
Здесь мы демонстрируем, как можно использовать переменную &DBG для управления в вариант печати. - Направление кодогенерации с использованием
переменные.
Здесь мы демонстрируем, как параметр оптимизации можно использовать в открытых код. - Процедура инициализации.
Эта подпрограмма выполняется только один раз путем изменения условия перехода в начало рутины. - Печать в две колонки.
В этом примере показано очень уродливое решение для печати в два столбца. проблема, что делает изменение кода операции инструкции. Код операции измененный с SH на AH и наоборот, переводя указатель столбца в положение и сюда.
Гораздо лучшим решением было бы выделить буфер, достаточно большой к разместить все строки на странице. Тогда можно было бы заполнить все левый столбец — сверху вниз — до заполнения крайней правой колонки. Самый конечные пользователи предпочитают такая вертикально перечисленная информация.
Процедуры прерывания не должны затирать глобальное состояние ЦП. Таким образом, обычно необходимо сохранить хотя бы состояние флаговых битов в СРЕГ
. (Обратите внимание, что это служит здесь только в качестве примера, так как на самом деле все следующие инструкции также не изменяют SREG
, но обычно это не так.)
Кроме того, необходимо убедиться, что регистры, используемые внутри процедуры обработки прерывания, не конфликт с теми, которые используются снаружи. В случае устройства без ОЗУ, такого как AT90S1200, это можно сделать только путем согласования набора регистров, которые будут использоваться исключительно внутри процедуры прерывания; другого шанса «сохранить» реестр не было бы.
Если подпрограмма прерывания должна быть связана с модулями C, необходимо соблюдать правила использования регистров, установленные компилятором C. Кроме того, любой регистр, измененный внутри службы прерывания, необходимо сохранить, обычно в стеке.
Как объяснено в разделе Прерывания, глобальный «универсальный» обработчик прерывания, который получает все неназначенные векторы прерывания, может быть установлен с использованием имени __vector_default
. Это должно быть
.global и, очевидно, должен заканчиваться инструкцией reti
. (По умолчанию вместо этого будет подразумеваться переход к ячейке 0.)
Доступные псевдооперации в ассемблере описаны в руководстве по ассемблеру GNU (gas). Руководство можно найти в Интернете как часть текущей версии binutils по адресу http://sources.redhat.com/binutils/.
Поскольку gas исходит из Unix, его псевдооперационный и общий синтаксис ассемблера немного отличается от того, который используется другими ассемблерами. Числовые константы следуют нотации C (префикс 0x
для шестнадцатеричных констант), выражения используют C-подобный синтаксис.
Некоторые распространенные псевдооперации включают:
Обратите внимание, что
.org также доступен в газе, но это довольно бессмысленная псевдооперация в среде ассемблера, использующая перемещаемые объектные файлы, поскольку именно компоновщик определяет конечную позицию некоторого объекта в ПЗУ или ОЗУ.
Наряду с независимыми от архитектуры стандартными операторами доступны некоторые специфичные для AVR операторы, которые, к сожалению, еще не описаны в официальной документации. Наиболее известные операторы:
Пример:
ldi r24, lo8(pm(somefunc)) ldi r25, hi8 (pm (somefunc)) назови что-нибудь
Это передает адрес функции somefunc
в качестве первого параметра функции something
.
Bixoft — Примеры программ на ассемблере
Bixoft — Примеры программ на ассемблереЭта страница содержит следующие фрагменты кода:
Мы хотели бы отметить, хотя это может быть излишним, что стандартный назначения регистров следующие:
Регистр | Функция |
---|---|
Регистр 0 | Рабочий регистр, в основном используется как параметр |
Регистр 1 | Рабочий регистр, обычно используемый как указатель на список параметров или поле результата |
Регистры 2-11 | Может свободно назначаться внутри каждой программы |
Регистр 12 | Базовый регистр для адресации внутри программы |
Регистр 13 | Указатель на свободную область сохранения |
Регистр 14 | Рабочий регистр, обычно используемый для обратных адресов |
Регистр 15 | Рабочий регистр, используемый для адреса подпрограммы или (по возврату) возвращаться- и коды причин |
Стандартный вызов подпрограммы.
Применяется даже самый распространенный способ вызова подпрограммы. И все же это рекомендуется следовать многолетней практике. Это улучшает оба удобочитаемость и ремонтопригодность ваших программ. Обычное соглашение:
Область сохранения состоит из 18 полных слов (по 4 байта каждое) и занимает этим правила:
Местоположение | Контент |
---|---|
Х’00’ | Зарезервировано для PL/I |
Х’04’ | Указатель на предыдущую область сохранения (т. е. регистр 13) |
Х’08’ | Указатель на следующую область сохранения |
Х’0С’ | Регистр 14 |
Х’10’ | Регистр 15 |
Х’14’ | Регистр 0 |
и т. д. | до регистра 12 |
Далее следуют три фрагмента кода:
* * Пример вызова подпрограммы. * * Мы предполагаем, что список параметров настроен LA R1,PLIST Reg.1 указывает на список параметров L R15,=V(SUBPROG) Адрес подпрограммы в регистре 15 BALR R14,R15 Заполнить регистр 14 и вызвать подпрограмму * Обработайте результат LTR R0, R15 Сохранить и проверить код возврата BNZ ERROR Код возврата не равен нулю: обработка ошибок ХОРОШЕЕ ОБОРУДОВАНИЕ * * . .. Другой программный код ОШИБКА N R15,=X'00000FFF' Удалить код причины L R15,ERRORS(R15) Получить адрес процедуры обработки ошибок из стол BR R15 И выполнить программу ошибок * * Процедуры обработки ошибок для обработки неожиданных результатов от SUBPROG * При входе reg.0 содержит коды возврата и причины ОШИБКИ DC AL4(хорошо) Результат в порядке DC AL4(RETCD4) Код возврата 4: предупреждение DC AL4 (RETCD8) Код возврата 8: проблема DC AL4 (RETCD12) Код возврата 12: не определено DC AL4 (RETCD16) Код возврата 16: фатальная ошибка
* * Пример вызываемой программы (одноразовой или многоразовой) * Этот пример нельзя использовать для программ с возможностью повторного ввода или обновления. * ПОДПРОГРАММА CSECT USING SUBPROG,R15 Регистр 15 содержит адрес ПОДПРОГ B СТАРТ Пропустить данные DC C'SUBPROG ' Имя программы DC C'&SYSDATE' Дата DC C'&SYSTIME' Время DC C'V1R2. 05' Номер версии DS 0H Повторное выравнивание по границе полуслова * START STM R14,R12,12(R13) Сохранить регистры DROP R15 Больше не нужен в качестве базовой регистрации LR R12,R15 Заполните регистр 12 базовым адресом ИСПОЛЬЗОВАНИЕ ПОДПРОГРАММЫ, R12 Теперь используйте reg.12 в качестве основы. LA R14,SAVEAREA Адрес новой области сохранения ST R13,4(R14) Указатель на предыдущую область сохранения ST R14,8(R13) Указатель на следующую область сохранения LR R13,R14 R13 снова указывает на свободную область сохранения * ... Другой программный код EXIT L R13,4(R13) Получить адрес предыдущей области сохранения LM R14,R12,12(R13) Восстановить все регистры (кроме 13) LA R15,... Код возврата в регистре 15 BR R14 Возврат к вызывающему абоненту База DROP R12 больше не нужна * LTORG Все литералы SAVEAREA DS 18F Область сохранения
* * Пример вызываемой программы (обновляемой или повторно вводимой) * Этот пример также можно использовать для программ многократного или одноразового использования. * ПОДПРОГРАММА CSECT USING SUBPROG,R15 Регистр 15 содержит адрес ПОДПРОГ B СТАРТ Пропустить данные DC C'SUBPROG ' Имя программы DC C'&SYSDATE' Дата DC C'&SYSTIME' Время DC C'V1R2.05' Номер версии DC 0H Повторное выравнивание по границе полуслова * START STM R14,R12,12(R13) Сохранить все регистры DROP R15 Больше не нужен в качестве базы LR R12,R15 Заполните регистр 12 базовым адресом ИСПОЛЬЗОВАНИЕ ПОДПРОГРАММЫ, R12 Используйте регистр 12 в качестве основы. LA R1,PRIVATE_LEN Требуемый объем памяти GETMAIN RU,LV=(R1) Выделить память для области сохранения и т.д. * Адрес выделенного хранилища теперь в регистре 1 USING PRIVATE,R13 Сделать хранилище адресуемым ST R13,4(R1) Указатель на предыдущую область сохранения ST R1,8(R13) Указатель на следующую область сохранения LR R13,R1 R13 снова указывает на свободную область сохранения * ... Другой программный код ВЫХОД LR R1,R13 Сохранить адрес нашей частной зоны L R13,4(R13) Получить адрес предыдущей области сохранения LA R2,ЧАСТНЫЙ_LEN FREEMAIN A=(R1),LV=(R2) Свободное выделенное хранилище LM R14,R12,12(R13) Восстановить все регистры (кроме 13) LA R15,. .. Код возврата в регистре 15 BR R14 Возврат к вызывающему абоненту База DROP R12 больше не нужна * LTORG Все литералы * * Эта dsect описывает все переменные, приватные для каждого вызывающего объекта. ЧАСТНАЯ DSECT SAVEAREA DS 18F * ... Другие частные переменные PRIVATE_LEN EQU *-ЧАСТНЫЙ
Условная сборка.
В следующем примере показано применение условного
сборка. Первый
показан макрос, который проверяет содержимое JCL-переменной
СИСПАРМ.
Это означает оптимизацию программы, которая должна быть сгенерирована и
включение
срабатывает отладочный код.
Макрос задает две переменные, которые можно тестировать на протяжении
программа
для генерации кода по желанию. Некоторые примеры такой логики приведены как
Что ж.
Ниже приведены следующие четыре фрагмента кода:
МАКРО CHECKPRM .* .* Программа на ассемблере (ASMA90) принимает в качестве JCL-параметра a .* спецификация переменной SYSPARM. Значение, введенное в .* JCL будет передан символу глобального набора с именем &SYSPARM. .* Значение, указанное в JCL, передается как одна строка. .* Опции отделяются друг от друга запятой - без пробелов. .* Этот макрос разбивает строку на отдельные параметры. .* Затем параметры проверяются и обрабатываются. 4 разных ключевых слова .* разрешены: .* - DEBUG : генерировать отладочный код (процедура Snap и т. д.) .* - NODEBUG: не генерировать отладочный код .* - OPT: создать оптимизированную программу . * - NOOPT: создание полнофункциональной программы. .* * * Макрос CHECKPRM тестирует JCL-переменную SYSPARM и устанавливает два глобальных * переменные для отображения содержимого SYSPARM: * &DBG включен, чтобы включить код отладки, выключен, чтобы пропустить этот код * &OPT включен для создания оптимизированного кода, выключен для полного * функциональный код. * ГБЛБ &ДБГ,&ОПТ &DBG SETB 0 По умолчанию: нет кода отладки &OPT SETB 1 По умолчанию: полная оптимизация AIF ('.&SYSPARM' EQ '.').ВЫХОД * * Сначала мы разбиваем строку SYSPARM на подстроки * LCLC &P(5) Массив, содержащий подстроки (пармы) LCLA &I,&N,&X &I SETA 0 Индекс символов для &SYSPARM &N SETA 1 Следующая позиция для извлечения &X SETA 1 Счетчик параметров (массив индексов &P) * .LOOP1 АНОП &I SETA &I+1 Увеличить индекс символа AIF (&I GT K'&SYSPARM.LOOP1X Конец строки? AIF ('&SYSPARM'(&I,1) NE ',').LOOP1 Конец подстроки? .* Поместить подстроку в массив &P &P(&X) SETC '&SYSPARM'(&N,&I-&N) Извлечь подстроку &N SETA &I+1 Установить указатель на начало следующей подстроки &X SETA &X+1 Увеличение счетчика подстроки НАЗАД . LOOP1 Перейти к следующему символу .* .LOOP1X ANOP Точка выхода для цикла 1 &P(&X) SETC '&SYSPARM'(&N,&I-&N) Извлечь последнюю подстроку .* .* Проверить действительность ключевых слов (теперь в массиве &P) .* &I SETA 0 Индекс в массиве &P .LOOP2 АНОП &I SETA &I+1 Увеличение указателя параметра AIF (&I GT &X).LOOP2X Готово? (&X содержит количество параметров) AIF ('.&P(&I)' EQ '.').LOOP2 Пропустить пустой параметр AIF ('.&P(&I)' EQ '.OPT').OPT Включить оптимизацию AIF ('.&P(&I)' EQ '.NOOPT').NOOPT Отключить оптимизацию AIF ('.&P(&I)' EQ '.DEBUG').DEBUG Включить логику отладки AIF ('.&P(&I)' EQ '.NODEBUG').NODEBUG Пропустить логику отладки .* Недопустимое значение: выдать предупреждение и продолжить MNOTE 4, 'Недопустимый операнд SYSPARM: &P(&I)' AGO .LOOP2 Перейти к следующему параметру .* .ОПТ АНОП &ОПТ НАБОР 1 MNOTE 0, «Будет создано оптимизированное кодирование» НАЗАД .LOOP2 .* .НООПТ АНОП &ОПТ НАБОР 0 MNOTE 0, «Будет создано отказоустойчивое кодирование» НАЗАД . LOOP2 .* .ОТЛАДКА АНОП &DBG НАБОР 1 MNOTE 0, 'Код отладки будет включен' НАЗАД .LOOP2 .* .NODEBUG ANOP &DBG НАСТРОЙКА 0 MNOTE 0, 'Код отладки будет исключен' НАЗАД .LOOP2 .* .LOOP2X ANOP Точка выхода для цикла 2 .EXIT ANOP Точка выхода для пустого SYSPARM ИСПРАВИТЬ
* * Глобальный параметр &DBG управляет параметрами сборки отладки/узла. * - Когда &DBG = 1, то отладка активна * Оптимизация глобального управления &OPT * - При &OPT = 1 происходит оптимизация * - При &OPT = 0 будет включена отказоустойчивость * ГБЛБ &ДБГ,&ОПТ CHECKPRM Проверьте &SYSPARM, чтобы установить &DBG и &OPT * SOMEPROG CSECT НЕКОТОРЫЕ ПРОГРАММЫ 31 SOMEPROG RMODE ЛЮБОЙ * ... Здесь следует кодировка.
* Теперь установите параметры печати Вариант печати GBLC &PRT Controls &PRT SETC 'NOGEN' По умолчанию nogen AIF (NOT &DBG).NOGEN Отладка активна? &PRT SETC 'GEN' Да: создать полный список . НОГЕН АНОП PRINT &PRT Активировать опцию печати *
* * Этот фрагмент кода перемещает данные, как указано в элементе управления перемещением. * Перемещаемые данные могут иметь длину до 32767 байт. * * R6 теперь указывает на элемент управления движением. * R8 будет использоваться как указатель источника, R9содержащий длину. * R10 будет использоваться как указатель назначения. * ИСПОЛЬЗОВАНИЕ MOVECTL,R6 Сделать область управления перемещением адресной L R8,MCRECPTR Указывает на начало записи AH R8,MCRECOFS Добавить смещение к соответствующим данным LH R9,MCDATLEN Загрузить длину данных L R10,MCDEST Указать на область назначения * * Теперь для перемещения данных мы обычно кодируем MVCL-инструкцию, * так как MCDATLEN может указывать любое количество данных до 32767 байт. * Поскольку известно, что в настоящее время ни один MOVECTL-элемент не указывает * длина более 256, мы можем оптимизировать код с помощью MVC * вместо MVCL. * AIF (&OPT).OPTMOVE ЛР Р11, Р9Целевая длина всегда экв. Источник-len MVCL R10, R8 Переместить данные НАЗАД .MOVEDONE .* .OPTMOVE ANOP BCTR R9,R0 Уменьшить длину на 1 для MVC EX R9,MOVEDATA Выполнить MVC-инструкцию Б ПЕРЕМЕЩЕН MOVEDATA MVC 0(0,R10),0(R8) Переместить данные МОВЕДОН ЭКВ * * .MOVEDONE ANOP DROP R6 MOVECTL больше не нужен
Самомодифицирующийся код.
Этот пример служит только в качестве иллюстрации. Обычно мы бы очень
сильно
рекомендуем никогда не применять самомодифицирующийся код, потому что он делает
ваш
программы нельзя повторно ввести, и потому что это делает программы очень трудными для
читать
и поддерживать.
Далее следуют два фрагмента кода:
* ... Настройка адресации и т.д. INIT BC X'00',INITDONE Эта ветвь никогда не OI INIT+1,X'F0' Сделать предыдущую ветвь ветвью - всегда * ... Здесь идет код инициализации INITDONE EQU * Конец процедуры инициализации
L R1,LINEPTR Получить последний использованный указатель в LINE SETPTR SH R1,=H'50' Переключиться на другой столбец (изначально: SH) XI SETPTR,X'01' Изменить AH на SH или наоборот ST R1,LINEPTR Сохранить обновленный указатель MVC 0(40,R1),DATA Переместить данные в строку печати . .. Другая кодировка для печати строки ЛИНИЯ ПОСТОЯННОГО ТОКА CL133' ' DS 0F Повторное выравнивание по границе полного слова LINEPTR DC AL4(LINE+67) Чтобы начать печать данных в * левый столбик, делаем вид, что последний * перемещение было в правую колонку.
Реентерабельная обработка набора данных.
Для обработки набора данных VSAM необходимы как ACB, так и RPL. В приведенном ниже примере показано, как создать ACB и RPL в возвращающийся программа. Вы можете предположить, что все именованные места хранения были распределяются динамически и что к ним можно обращаться с помощью DSECT.
* SUBROUTN STM R14,R12,12(R13) Сохранить все регистры LA R1,SAVEAREA Адрес новой области сохранения ST R13,4(R1) Указатель на предыдущую область сохранения ST R1,8(R13) Указатель на следующую область сохранения LR R13,R1 Reg.13 указывает на свободную зону сохранения опять таки * * Сначала выделяем хранилище для ACB и RPL * GETMAIN RC,LV=IFGACBLV+IFGRPLLV Запрос хранилища для ACB + RPL LTR R15, R15 Getmain в порядке? BNE ERROR16 Ошибка обрабатывается в другом месте ST R1,ACBPTR Сохранить указатель на ACB LA R1,IFGACBLV(R1) Указывает на RPL-часть области ST R1,RPLPTR Сохранить указатель на RPL * * Выделить хранилище для рабочей области * ПОЛУЧИТЬ RC, LV = 4096,BNDRY=PAGE Запрос хранилища для рабочей области LTR R15, R15 Getmain в порядке? BNE ERROR16 Ошибка обрабатывается в другом месте ST R1,WORKPTR Сохранить указатель на рабочую область * * Создайте PLIST для GENCB ACB в области getmained. * Для этого GENCB не предусмотрен код возврата. * SR R6,R6 Очистить регистр 6 IC R6,SHRPOOL, чтобы содержать номер shrpool L R7,WORKPTR Указывает на рабочую область для создания PLIST ИСПОЛЬЗОВАНИЕ РАБОЧЕЙ ЗОНЫ, R7 GENCB BLK=ACB, сгенерировать PLIST для GENCB ACB * AM=VSAM, метод доступа * WAREA=(R7), Место для сгенерированного ACB * LENGTH=IFGACBLV, максимальная длина сгенерированного ACB * DDNAME=(*,DDNAME), GENCB ACB копирует DDNAME * SHRPOOL=(S,0(R6)), номер Shrpool варьируется от 0 до 15 * MACRF=(KEY,DFR,SEQ,SKP,SIS,NSR), Опции для этого ACB * BUFND=8, минимальное количество буферов данных * BUFNI=1, минимальное количество индексных буферов * RMODE31=ALL, Буфер и блоки управления выше 16M * MF=(L,WORKAREA,GENACBLV) Создать PLIST в WORKAREA * * Теперь создайте ACB, используя PLIST в WORKAREA. * GENCB BLK=ACB,MF=(E,(R7)) Создать ACB с помощью PLIST в WORKAREA LTR R15, R15 ACB создан? BNZ ERROR17 Ошибка обрабатывается в другом месте DROP R7 WORKAREA больше не нужен * * Создайте PLIST для GENCB RPL в области getmained. * Для этого GENCB не предусмотрен код возврата. * SR R6,R6 Очистить регистр IC R6,KEYLV для указания длины ключа L R7,ACBPTR Указывает на ACB LH R8,RECDLV Указать длину записи Л Р9,RPLPTR И указать местоположение для сгенерированного RPL L R10,WORKPTR Переадресация рабочей области ИСПОЛЬЗОВАНИЕ РАБОЧЕЙ ЗОНЫ, R10 * GENCB BLK=RPL, создать PLIST для GENCB RPL * AM=VSAM, метод доступа * WAREA=(R9), место для сгенерированного RPL * LENGTH=IFGRPLLV, максимальная длина сгенерированного RPL * ACB=(R7), Укажите ACB-адрес для RPL * AREA=(S,RECDPTR), указать область данных для записей * AREALEN=4, длина области данных * ARG=(S,KEYPTR) Указать расположение ключа * KEYLEN=(S,0(R6)), И длина ключа в байтах * ECB=(S,ECBPTR) Указать ECB-адрес * RECLEN=(R8), И длина записи * OPTCD=(KEY,SEQ,ASY,NUP,KGE,GEN,LOC), Опции для RPL * MF=(G,РАБОЧАЯ ОБЛАСТЬ,GENRPLLV) * * Теперь создайте RPL, используя PLIST в WORKAREA. * GENCB BLK=RPL,MF=(E,(R10)) Сгенерировать RPL, используя PLIST в WORKAREA LTR R15, R15 RPL создан? BNZ ERROR18 Ошибка обрабатывается в другом месте * * Теперь, когда мы создали и ACB, и RPL, мы можем открыть набор данных * L R2,=AL4(VSAMOPEN) Получить адрес открытой формы-списка MVC WORKAREA(VSAMOPLV),0(R2) Скопировать в рабочую область L R2,ACBPTR Перезагрузить указатель ACB OPEN ((R2)),MF=(E,(R10)) Открытый ACB с изменяемым PLIST LTR R15, R15 Набор данных успешно открыт? БНЗ ОШИБКА19Ошибка обрабатывается в другом месте * * Возврат из подпрограммы * L R13,4(R13) Получить адрес предыдущей области сохранения LM R14,R12,12(R13) Восстановить все регистры (кроме 13) BR R14 Вернуться к основной линии * * Форма списка по умолчанию для открытого макроса * VSAMOPEN OPEN (0), ACB-адрес еще не известен * MODE=31, включить 31-битную адресацию * MF=L Генерировать только PLIST
Замечания? Вопросы? Дополнительная информация? Выберите тему вашего выбор или напишите нам с вашим вопросы.