Работа с операционной системой в ассемблере
Ассемблер М6800 разработан в двух вариантах: для машины Пылден
предназначены два файла UniASM.CMD и UniLINK.CMD, для машины
IBM предназначен UniCross в виде файла M6800.EXE. Таким образом
прграммы для машины Пылден могут быть созданы как на самой
машине Пылден, так и на машине IBM. Текст программы может быть
записан в любом текстовом редакторе, для Пылден это редактор UE.
Первая строка программы определяет какими модулями она должна
обрабатываться и какие файлы будут получены в результате обработки.
Для получения файлов типа .CMD первая строка программы должна
иметь вид
CMD
Метка в этой строке запрешена. Далее идет текст программы, который
записывается в UE по клавише F2 в файл с расширением .ASM. Далее
вызываем
A:\> UASM имя.ASM имя.CMD
После работы компилятора получается .CMD файл, готовый к исполнению.
Если первая строка не содержит CMD, то вызов компилятора делается
так
A:\> UASM имя.ASM
После работы создается файл имя.OBJ, он должен быть обработан
линковщиком
A:\> ULINK имя
При трансляции на IBM первая строка программы должна быть записана
в виде
org $100
для позиционирования программы на начальный адрес 100. Текст
программы может быть набран в любом редакторе, например в Турбо-
Паскале. Все метки в программе должны находится с первой позиции,
":" не ставится, между операторами и комментарием должен быть
хотя бы один пробел. Затем запускается программа 6800, она
запрашивает имя файла,затем запрашивает имя файла LST,затем
запрашивает имя файла CMD. После этого идет процесс компиляции.
Ее результаты можно посмотреть по CTRL+O.
Программирование
Для упрощения программирования разработчики создали большую
библиотеку готовых программ-системных функций, которые помещены
в BIOS. Поэтому программирование часто сводится к засыланию
необходимой информации в регистры и вызову соответствующей
функции с помощью int. Программирование также предполагает
знание распределения и назначения системных ячеек рабочей
области и использование памяти компьютера. Использование
памяти DRAM показано на рис. . Область адресов F000-FFFF
занята ПЗУ, в котором записана резидентная часть BIOS системы.
Ее можно только читать. По этим же адресам расположен системный
текстовый экран, т.е. видеопамять текстового режима, в которую
можно только писать. К области C000-DFFF размером 8 Кб может
быть подключена одна из 16-ти страниц дополнительной памяти.
Подключением управляет 4-х битный регистр, расположенный в
младшем полубайте ячейки E6F0. Значение 0 посылается туда
системой BIOS для блокирования переключения страниц, а значение
7 определяет режим включения основной памяти ( т.е. выключение
расширений). Страницы 8-F содержат расширение ПЗУ с набором
системных функций. Содержимое этих страниц можно просмотреть
с помощью программы READROM.CMD, написанной Чуковым А.В.
Назначение и распределение остальных областей памяти зависит
от режима работы компьютера. Подробное изложение состава
системных функций дано в документации [ ] вместе с примерами
их использования, поэтому мы остановимся лишь на отдельных
примерах.
Допустим нам нужно распечатать какой-нибудь текст, для этого
можно использовать int $23 следующим образом.
cmd
ldx #text
int $23
rts
text db 'это первая программа'
Данная программа просто напечатает указанный текст в том месте,
где стоит курсор. Оператор ldx загружает адрес начала текста
в индексный регистр и далее вызывается системная функция $23
печати текста. Для печати текста в нужную позицию на экране
используют функцию позиционирования курсора int $15, для
которой в регистр А нужно загрузить номер колонки, а в регистр В
номер строки
cmd
ldaa #8
ldab #5
int $15
ldx #text
int $23
rts
text db 'это вторая программа'
Для рисования рамки на экране можно использовать циклы и функцию
вывода символа на экран int $22
cmd
ldaa #1 ;позиционируем курсор в точку 1,1
ldab #1
int $15
ldaa #'_' ;в а код символа "_"
lp1 int $22
incb ;в играет роль счетчика
cmpb #40
bne lp1 ;рисуем горизонтальную линию
ldab #2
lp2 ldaa #1
int $15
ldaa #'1'
int $22
incb
cmpb #25
bne lp2 ;рисуем правую вертикальную линию
ldaa #'_'
ldab #2
lp3 int $22
incb
cmpb #40
bne lp3 ;рисуем нижнюю горизонтальную линию
lp4 ldaa #40
int $15
ldaa #'1'
int $22
decb
cmpb #1
bne lp4 ;рисуем правую вертикальную линию
rts ;снизу вверх
Для рисования графических изображений в библиотеке специально
предусмотрены несколько функций. Работа с графическим режимом
начинается с резервирования буфера для графического экрана с
помощью функции int $60 и инициализации видеорежима с помощью
функции int $12. Рисование графических примитивов происходит с
помощью функции int $66.
cmd
int $60 ;аллокируем буфер для гр. экрана
stx gr_scr ;запоминаем адрес гр. экрана
ldaa #3 ;задаем SCREEN 3
ldx gr_scr
int $12 ;инициализируем видеорежим
ldx #c1
ldaa #1
int $66 ;операция move (x,y)
ldx #c2
ldaa #2
int $66 ; операция lineto(x,y)
ldx #c3
ldaa #4
int $66 ; операция circle (r1,r2)
int $61
rts
c1 dw 0,0
c2 dw 320,200
c3 dw 100
При подготовке к выводу графического примитива мы должны в
соответствии с описанием функции int $66 в регистр а поместить
число, характеризуещее тип примитива, а в регистр Х поместить
адрес указателя 4-х байтовой структуры, содержащей параметры
для примитива (координатную пару Х,У или радиусы r1,r2). В данном
примере будет проведена линия из левого верхнего угла в середину
экрана и из него нарисован круг, радиусом 100.
Резервирование и алокирование памяти
При распределении памяти имеется несколько операторов, позволяющих
указать транслятору в какие места памяти помещать программу. Для
помещения программы в область между HIMEM и RESID ее нужно
объявить как .PGM файл или использовать функцию int $2A для
алокирования памяти из текущей программы. Например, распределение
памяти под графический экран делается слеудющим образом
cmd
ldx #grscr
ldab #3
int $2A
cpx #0 ;проверка на ошибку
beq error
stx grbase
rts
error ldx #message
int $23
rts
message db 'не хватает памяти',7,0
grbase ds 2 ;адрес графического экрана
grscr =8000 ;размер гр. экрана
int $35 резервирует память для больших буферов так, чтобы они не
размещалисть в коде самой программы. В ВА задается размер в байтах,
на выходе в Х получают адрес резервированной памяти, а ВА
содержит действительный размер области, которая выделяется в
диапазоне от 0100 до LOMEM. Для создания резидентных программ,
т.е. программ код которых остается в памяти машины после их работы
возможно резервирование памяти с помощью int $26 в области RESID-
$BFFF. Это обычно используется для создания драйверов принтера.
Перехват векторов
Выполнение int-ов происходит путем указания адреса вектора и
страницы памяти, на которой он размещается. Заменяя адреса
функций на свои адреса, можно изменять работу функций. Для
этого предназначениы функции : int $2E -определяющая адрес и
страницу исходной функции (в А задают номер функции,а в В
получают станицу и в Х- адрес), int $2F используется для
замены адреса и страницы на свои,а int $37 служит для выполнения
функции по заданному вектору. Значения регистров получается через
ячейки SWIA,SWIB и SWIX в нулевой странице. Приведем пример
создания драйвера для принтера, имеющего кодировку символов,
отличную от принятой в компьютере Пылден. Принтер СМ6337 имеет
другую кодировку и для перевода необходима резидентная программа
start ldaa #$26 ;определяем адрес функции $26
int $2e
stab page ;запоминаем страницу
stx addr ;и адрес
ldaa #$26
clrb ;указываем страницу 0
ldx #new_int26
int $2F ; заменяем на новый адрес
int $2C ; объявляем программу резидентной
ldx #text
int $23
rts
text db $0a,0d,'(C) Gorchakov L.V.'
db $0d,$0d
db $0d,0a,'Driver is load',7,$0a,$0d,0
page ds 1
addr ds 2
new_int26
tstb ;если в В 0 то работаем с кодом
beq next
old ldab page ;иначе выполняем старую функцию
ldx addr
int $37
next cmpa #$80 ;если (а)<80, то печатаем тоже
blt old ;что и раньше
cmpa #$C0 ;иначе сравниваем с С0
bge tab_cod ;если >С0 идем дальше
adda #$30 ;добавляем к коду 30
new staa $0006 ;помещаем его в SWIA
bra old ;и идем на печать
tab_cod cmpa#$F0 ;код в а>F0?
bge old ;да-печатаем старое значение
suba #$30 ;нет-вычитаем 30
bra new ; и идем на печать
end
Программа работает следующим образом, так как int $26 обслуживает
печать символа на принтер, причем в в задается тип операции (если
0-вывод символа на печать, 1-получение статуса принтера, 2-
инициализация принтера), а в (а)-код символа для вывода на печать,
то программа перехватывает это обращение и анализирует содержимое
аккумулятора в и а. Если в (в) не 0, значит команда не является
командой печати и поэтому разрешается выполнение обычной функции
обработки. В случае если в (в) содержится 0, значит затребована
операция печати символа и программа переходит к анализу того,
какой символ требуется напечатать. Если кодировка символа
совпадает с кодировкой для обоих устройств, то работает обычная
функция, если же не совпадает, то содержимое регистра а
преобразуется к нужному виду и уже затем вызывается обычная
функция int $26.
Перемещение блоков в памяти
Int $2D позволяет переносить блок информации из одного места
памяти в другое, в ВА помещается число переносимых байтов, в Х
адрес 4-х байтной таблицы, содержащий начальные адреса источника
и приемника. Функция используется для перемещения закодированной
картинки в область графического экрана, что позволяет быстро
получить изображение без его построения.
Определение атрибутов файла
Каждый файл ,хранимый в каталоге имеет атрибуты, которые не
распечатываются при выдаче каталога на экран с помощью команды
DIR опеpационной системы. Но эти атрибуты хранятся в виде
байта в строке каталога и определяют тип файла следующим образом
7 6 5 4 3 2 1 0
0 0 х х
| | | |______доступность только на чтение
| | |__________скрытный
| |_____________системный
|______________________архивный
Функция int $54 может быть использована для определения и
изменения атрибутов файла. В А задается тип операции:0-задание
атрибутов,1-чтение, в В -новые атрибуты (если А=0), Х-указатель
к строке с именем файла. На выходе функция выдает в А- код
ошибки, В-атрибуты файла (если А=1). Пример программы
cmd
ldaa #1;получим атрибуты файла
ldx #name
int $54
tsta ;проверка на ошибку чтения
bne error ;переход на обработку ошибки
tba ; засылка в а содержимого в
int $25 ;печать байта атрибутов
rts
error ldx #meserr
int $23
rts
meserr db 'ошибка при чтении каталога',0
name db 'file.ext',0
Примеры разработанных программ
Объективной трудностью при демонстрации и объяснения работы
программ на ассемблере является их размер: маленькие программы
могут демонстрировать лишь работу отдельных функций системы BIOS,
как было показано выше, а большие программы труднообозримы из-за
своих размеров, поэтому мы ограничимся средними программами,
выбрав золотую середину. Рассмотрим программу изменения атрибутов
файла уже в полном объеме вместе с меню. Согласно описанию [ ]
байт 11 записи в директории содержит атрибуты файла. Каждый бит
этого байта управляет определенным атрибутом и его строение мы
показали выше. Программа, разработанная Чуковым А.В., позволяет
увидеть эти атрибуты и изменить их по желанию пользователя.
Приведем ее текст с подробными комментариями.
cmd
start ldaa #$0C
int $22 ;очистка экрана по управляющему коду
ldaa #5
ldab #5 ;позиционирование меню
ldx #text ;адрес текста
jsr window ;вызов п/п вывода меню на экран
ldab #5
ldaa #12
jsr cursor ;позиционирование курсора в меню
ldx #buff ;подготовка буфера длиной 16 байт
ldab #16 ;для приема имени файла
int $21 ;ожидание ввода имени файла
ldaa #0 ;задание типа операцци для int $54
clrb ;0-получение атрибута,очистка в
ldx #buff ;указатель к имени файла
int $54;получаем атрибуты файла,в а-код ошибки
; в В-атрибуты файла
tsta ;проверяем на ошибку
beq d1; если а=0, чтение прошло нормально
jmp error ; случилась ошибка,обрабатываем ее
d1 stab old_attr
stab new_attr;запоминаем атрибуты файла,
; пока они одинаковы и старые и новые
ldaa #21
ldab #10 ;позиционируем курсор
jsr cursor
ldaa #old_attr
int $25 ;печатаем старые атрибуты
d2 ldaa #29
ldab #10
jsr cursor ;позиционируем курсор
ldaa new_attr
int $25 ;печатаем новые атрибуты
int $20 ; ожидаем нгажантия на клавиши
;стрелка вверх и стрелка вниз, с помощью которых
;происходит изменение атрибутов
cmpa #$C3;это стрелка вниз?
bne d3 ;нет,проверяем дальше
dec new_attr ;умненьшаем атрибут на 1
bra d2 ;идем на печать атрибута
d3 cmpa #$C4 ;это стрелка вверх?
bne d4 ;нет,проверяем дальше
inc new_attr ;увеличиваем атрибут на 1
bra d2 ;переход на печать атрибута
d4 cmpa #$C0 ;это ввод,окончание изменений?
bne d2 ; нет,идем на ожидание ввода
ldab new_attr ;посылаем новый в В
cmpb old_attr :сравниваем его со старым
beq d5 ;если они равны идем на конец
ldaa #1 ;они не равны, записываем
ldx #buff ;его в каталог
int $54
tsta ;ошибка есть ?
beq d5 ;нет
jmp error ;случилась ошибка, обрабатываем
d5 int $38 ;завершаенм работу
error ldx #bufer ;задаем адрес буфера ошибки
int $3D ;определяем текст ошибки
ldx #bufer
int $23 ;печатаем ее
int $38 ;завершаем работу
bufer ds 66 ;буфер для текста ошибки
buff ds 17 ;буфер для имени файла
old_attr ds 1
new_attr ds 1 ;ячейки для атрибутов
; подпрограмма печати окна меню для ассемблера,
;печатает текст в окно
window staa begx
stab begy ;запоминаем начальные х и у
;позиции курсора
win1 int $25 ;позиционируем курсор
psha ;а запоминаем в стеке
win1_1 ldaa $0,X ;в А загружаем код символа по
;адресу из Х
tsta ;в А признак конца строки=0?
beq win2 ;да,переход на новую строку
int $22 ;нет,печатаем символ
inx :смещаем указатель адреса на 1 байт
bra win1_1 ;идем на печать следующего символа
win2 inx ;смещаемся на следующий адрес
ldaa $0,X
cmpa #$FF ;это не конец всего текста, $FF?
beq win3 ;да-заканчиваем печать
pula ;нет, вспоминаем а
incb ;переходим на следующую строку
bra win1 ;идем на позиционирование курсора
win3 pula ;вспоминаем а, чтобы не попутались
;переходы
rts
begx ds 1
begy ds 1 ;ячейки для начальных координат
cursor adda begx ;п/п позиционирования курсора
addb begy ;наращивание координат
int $15
rts
text db '|||||||||||||||||||||||||||||||||||||',0
db '| Получение /определение атрибутов |',0
db '| файла (@ Чуков А.25-мая-1993) |',0
db '|___________________________________|',0
db '| |',0
db '| имя файла : |',0
db '| ----------- |',0
db '|___________________________________|',0
db '| операция || атрибут |',0
db '| ----------------------------------|',0
db '| определение || старый новый |',0
db '| получение || ------ ----- |',0
db '|___________________________________|',0
db '| Выход (Д/Н)? |',0
db '|___________________________________|',0
db '|||||||||||||||||||||||||||||||||||||',0
db $FF
end