Продолжение цикла статей по работе с расширенным экраном компьютера "Profi". Первый практикум.
С чего начинается изучение нового? Правильно с "базы". Как правило база - это загрузка и простейший вывод тестовой информации. По этому и тут начнём с загрузки и вывода картинки. Научимся не только выводить данные на экран, но и читать их с носителей.
Статья была опубликована в 2019 году в 26 номере журнала по ZX Spectrum'у "ЗаRulem Печатное Слово".
Статья написана в соавторстве с Вадимом Чертковым.
В предыдущий раз мы рассмотрели строение расширенного экрана компьютера «Profi», этим же материалом открываем раздел «Практикум» в котором будем рассматривать основные (базовые) задачи которые встают при работе с ним. Не планирую давать подробный разбор или универсальные решения. Задача данного материала преодолеть страхи и неуверенность «мол - это сложно, это долго, не смогу разобраться и т.п.» у тех, кто решит заняться этой замечательной машиной.
Весь материал будет ориентирован на работу в ОС CP/M с использованием ассемблера M80. Несмотря на свой почтенный возраст, ассемблер M80 весьма не плох и вполне успешно справляется со своими задачами. Если возникнут вопросы по настройке ОС CP/M или ассемблера M80, обращайтесь, ответим в личном порядке. Возможно, в будущем будет подготовлен материал и на эти темы. Но сейчас эти вопросы выходят за рамки поставленной задачи.
Несмотря на ориентацию материала на ОС CP/M он будет полезен и для работы с расширенным экраном в режиме Спектрум. Подробнее различия между режимами CP/M и Спектрум будет рассмотрены отдельно. Разницы же в этих режимах непосредственно при работе с расширенным экраном нет.
Одной из самой частой задачей при работе с любым экраном, это загрузка на него картинки с внешнего носителя. В настоящее время такими носителями могут быть (касаемо версии Profi 5.06): дисковод FDD, винчестер HDD, CD-ROM, карточки SD Card, карточки CompactFlash, RAM диск. При работе в ОС CP/M, с точки зрения программирования нет ни какой разницы от куда загружается информация, система берёт на себя всю головную боль. Отличаться будет только скорость загрузки данных.
Основных графических форматов в ОС CP/M является формат GRF, его и будем вводить на экран.
Файл GRF состоит из заголовка (1 или более секторов) и графических данных. Формат заголовка выглядит так:
+0 |
Слово |
DW |
HSIZE |
горизонтальный размер картинки в точках, пикселей |
+2 |
Слово |
DW |
VSIZE |
вертикальный размер в строках картинки, пикселей |
+4 |
Байт |
DB |
BPP |
бит на точку или точек в байте (в зависимости от AMOD) |
+5 |
Байт |
DB |
AMOD |
1 - цвет на каждую точку, 0 - байт атрибутов на байт точек |
+6 |
Слово |
DW |
BPS |
длина образа одной строки растра в байтах |
+8 |
Байт |
DB |
HLEN |
длина заголовка в записях по 128 байт (и 0, и 1 соответствует 128 байт) |
+9 |
Байт |
DB |
|
0 - признак стандартного формата (если формат будет изменяться, изменится и этот байт) 19 (#13) - файл с палитрой GGGRRRBB |
+10 |
|
DB |
|
Резерв 118 х DB 0 или палитра (при +9=19(#13)). 16 байт по 1 байту на цвет в формате GGGRRRBB, далее нули. |
![](https://habrastorage.org/getpro/habr/upload_files/1b3/c33/59e/1b3c3359eade58acdd6263e4c7bcb2b5.png)
Как видно размерность картинки может быть до 65535 точек по ширине и высоте, а цветность до 256 цветов на точку, что значительно выше возможностей «Profi». Но пока ещё не неизвестно случаев, что бы кто-либо видел файлы иного формата кроме PROFI-color и PROFI-mono. Конечно, это не означает, что их не бывает.
Для упрощения примем, что выводим картинку размерностью в расширенный экран «Profi» 512х240 пикселей, причём высота может быть любой (в этом случае будет выведены первые 240 строк). Принимаем, что работаем на компьютере «Profi» с палитрой 16 из 256 цветов. И будем различать, только формат «Profi» черно-белые, цветные (стандартные 16 цветов) и с палитрой.
Начнём с беглого разбора (подробный разбор, опять же, выходит за рамки текущего материала), как в ОС CP/M происходит работа с файлами.
Операции с файлами (и все другие) происходят через вызов функций BDOS, для работы с которыми используют стандартные соглашения по вызовам операций BDOS. Доступ, к которым осуществляется через точку входа в ячейке 0005H. При входе в BDOS регистр С содержит номер операции BDOS, а регистровой паре DE содержат байт, слово или адрес информации. BDOS возвращает 1-байтовый результат в регистре А, а 2-байтовые в регистровой паре HL. Кроме того, при возврате регистр А=L, B=H.
BDOS не сохраняет входные значения регистров вызывающей программы, поэтому необходимые значения должны сохраняться самой программой.
Для удобства обращения к функциям BDOS был написан макрос «.bdos» (смотри приложение). При его вызове в первом параметре указывается номер операции, а втором дополнительный параметр, если он есть.
Что бы не запоминать номера всех функций, присваиваем им имена. Здесь приведу только те, которые будут использованы в примерах. Полный список можно увидеть в файле «BIOSK.INC» находящемся в архиве с практическими материалами. Сами функции рассмотрим по ходу статьи.
bdExit equ 0 ; Сброс системы
bdWrite equ 9 ; Вывод последовательности символов
bdOpen equ 15 ; Открытие файла
bdReadCon equ 20 ; Последовательное чтение
bdPDPInst equ 26 ; Установка адреса буфера ПДП
bdBSVV equ 50 ; Вызов операции БСВВ или операций расширенной BDOS
Так же в подпрограмме установки палитры используются ресурсы BIOS’а.
BIOS (Basic Input/Output System) - базовая система ввода-вывода (БСВВ). Слово "Concurrent" в названии видимо было выбрано по аналогии с названиями систем фирмы Digital Research: Concurrent DOS и Concurrent DOS/86. Первая версия CBIOS была написана Крестьяниковым А.А. (KiiA) в 1992 году.
С адреса F800h расположен керналь, т.е. группа точек входа. Драйверы подключаются на этапе загрузки через файл CONFIG.SYS. Это более гибкая схема, чем в системах CP/M, где все драйверы "зашиты" в тело BIOS и для изменения какого-либо драйвера необходимо пересоздавать весь модуль.
Мы будем использовать две подпрограммы BIOS’a: SETCMRS и GETADR, а так же вектор (таблицу) TIME.
SETCMRS – задать значения CMRS. На входе DE=CMRS. Регистр D значение для CMR0 (7FFDh), регистр E значение для CMR1 (DFFDh). Функция выдаёт значения в порты и сохраняет DE в переменной MCMRS. На Profi для управления памятью используется два регистра, в них же задаются биты управления периферией. Т.е. и для изменения доступа к периферии, включение/отключение ПЗУ и для подключения страниц используется данная функция. Мы, в этой программе, будем использовать её для доступа к страницам экранной области. Дополнительно скажем, что все утилиты и драйверы под SP-DOS & CBIOS используют эту процедуру, т.к. регистры CMR0 & CMR1 доступны только на запись. Если все программы будут оставлять последние выданные значения в переменной в ОЗУ, можно легко узнать эти значения.
GETADR – Получить адрес вектора (таблицы). На входе в рег. C номер вектора (таблицы CBIOS), на выходе процедура возвращает в HL адрес начала вектора (таблицы).
TIME – вектор (таблица), состоящий из TIK, SEC, MIN, HOUR, DAY, MONTH, YEAR-1980. Кроме того, со смещением -1 лежит BORD - текущее содержимое регистра бордюра, звука и магнитофона, а со смещением -2 находится INTRFLAG, равный 0FFH во время обработки прерываний, и 0 в остальное время.
Начнем работу программы с установки адреса буфера прямого доступа к памяти ПДП. Это то место, куда будет считываться данные с диска. Устанавливается он операцией BDOS «26. Установка адреса буфера ПДП», которой присвоили имя «bdPDPInst» В нашем случае мы будем читать данные с диска по 1 сектору (128 байт). Прописываем буфер в конце листинга «PDP_SCR: ds 128» и устанавливаем «.bdos bdPDPInst, PDP_SCR».
За число единовременно считываемых с диска секторов отвечает мультиселекторный счётчик, его значение можно изменить операцией BDOS «44. Установка мультиселекторного счётчика» в пределах 1…128 секторов. По умолчанию стоит значение 1, так что в нашем случае менять ни чего не нужно.
Работа с файлами происходит через «Блок управления файлом» (БУФ). Это структура данных, которая организуется и инициализируется транзитной программой, а также используется файловой системой при доступе к файлам через оглавление. Все операции с файлами обращаются за исходной информацией к БУФ. Система хранит текущее состояние файла в БУФ во время исполнения файловых операций.
При вызове операций, осуществляющих доступ к файлам или оглавлению, регистровой паре DE должны содержать БУФ указывающего на файл или файлы, с которыми должна производится операция. Для большинства операций длина БУФ равна 33 байт, а для операций произвольного доступа, вычисление размера файла и свободного места на диске размер БУФ равен 36 байт.
Создать БУФ можно специальной функцией BDOS «152. Подготовка БУФ», но в нашем случае это не потребуется. Для упрощения задачи имя выводимого файла будем передавать в качестве параметра при вызове нашей программы. В «Базовой странице памяти» есть БУФ область 1 (005Ch-0068h), после запуска программ с параметром в него будет занесено-то имя файла, которое идет сразу за именем вызываемой программы. Что нас в данном случае более чем устраивает. Для удобства присвоим буферу символьное имя «scr_buf EQU 005CH».
Теперь у нас всё готово для работы с файлом. Открываем его для доступа. Используем операцию BDOS «15. Открытие файла» «.bdos bdOpen, scr_buf». Операция активизирует указанный БУФ.
В случае ошибки операция «15. Открытие файла» возвращает в регистре A значение 255, а в регистре H код ошибки. Для простоты примем, что любой возврат в регистре A значения 255 означает отсутствие файла на диске. Так что после выполнения операции BDOS «15. Открытие файла» нужно проверить регистр A на равенство содержимого 255, и если это так вывести сообщение об ошибки, выйти из программы.
Для вывода сообщения воспользуемся макросом «say» (смотри приложение), который выводит на экран по текущим координатам текстовую строку, указанную в первом параметре. Если в качестве второго параметра указано имя регистра, то его значение будет выведено в десятичном формате сразу за строкой. Макрос универсальный и сохраняет основные регистры, а значит, не оптимален по скорости и памяти. Но избавляет от многих головных болей и может использоваться при откладке совместно с макросом «pause» (смотри приложение). Который ждет нажатия любой клавиши, если нажата Esc, то производит холодный рестарт системы (то есть выход из текущей программы), а по любой другой продолжает выполнение программы. Макрос «pause» не используется в текущей программе, но уж больно он удобен при отладке, так что я решил его привести.
Теперь, когда мы убедились, что файл существует, и открыли его, читаем из него первый сектор. Это заголовок файла. Для чтения воспользуемся операцией BDOS «20. Последовательное чтение». В качестве параметров передаем БУФ нашего файла. Команда выглядит так «.bdos bdReadCon scr_buf». После выполнения, которой по адресу «PDP_SCR» будет лежать первые 128 байт файла.
Анализ заголовка проводим упрощенный. Текст программы с подробными комментариями приведен в конце статьи, здесь просто опишу порядок и логику действий.
Первым делом проверяем размерность (в пикселях) файла по горизонтали (слово +0 от начала заголовка) на равность 512, то есть ширине экрана. Если они не равны, то выходим с сообщением об ошибке.
Далее, нужно понять, с каким файлом мы имеем дело, для чего читаем байт +9 «Признак стандартности файла». Если в нём содержится значение 19 (#13), то файл с палитрой и нужно её установить.
После чего читаем байт +4 «BPP - число точек в байте», если здесь 8, то файл черно-белый, если 4, то цветной 16 цветов. При этом в переменной «FColor» сохраняем цветность файла: 0 – цвета нет; 1 - стандартные цвета; 2 – палитра 16 из 256 цветов. Это пригодиться в дальнейшем.
Теперь отключаем вывод часов на экран, что бы не портили нам изображение. Для чего воспользуемся макросом «.timeOFF» (смотри приложение). Перед выходом из программы нужно будет, воспользовавшись макросом «.timeON» (смотри приложение) вернуть часы на экран.
Теперь командой «ld de, 0207h or 08D8h; call 0f82dH» включаем экран в нижние 64 кб, графика с #8000, цвет с #4000.
Читаем второй сектор из файла, подпрограммы переброски данных на экран, требуют, что бы начало данных было загружен перед входом в них.
Для ускорения вывода применяем таблицу адресов начал линий по знакоместам. Это избавит от расчетов при переходе между знакоместами. В регистровую пару IX, заносим начало таблицы.
В регистровую пару HL заносим начало буфера прямого доступа к памяти ПДП. А в регистр B – заносим число выводимых знакомест, в данном случае 30.
Инсталляционные работы закончились. Приступаем к непосредственному выводу изображения. Для чего анализируем переменную «FColor», если в ней 0, то выводим черно-белую картинку, иначе цветную.
Вывод цветного и черно-белого изображения будет происходить разными подпрограммами. Это позволит оптимизировать код по скорости, путём отказа от проверки ряда условий. Разберём сначала черно-белый вывод.
Сам вывод происходить внутри трёх вложенных циклов: по знакоместам, по пиксельным линиям в знакоместе, по горизонтали.
Вначале цикла «по знакоместам» в регистровую пару DE, из таблицы заносим адрес первой пиксельной линии в знакоместе. Переход по строкам внутри знакоместа будет происходить увеличением на единицу содержимого регистра D.
Ширина экрана 64 байта, значит, в одном секторе умещается 2 строки. В регистр C заносим число 2, в качестве счетчика строк в секторе.
В цикле «по горизонтали» в одной итерации обрабатываем два смежных байта на двух полуэкранах, соответственно число итераций цикла устанавливаем равной 32. Перебрасываем первый байт через регистр A «ld a, (hl); inc hl; ld (de), a». После переходим на второй полуэкран «res 5, d» и перебрасываем второй байт, возвращаемся на первый полуэкран и смещаемся на знакоместо «set 5, d; inc e».
По окончанию цикла «по горизонтали», уменьшаем счетчик числа линий в секторе на 1. Если достигли нуля, грузим новый сектор, заносим в счетчик числа линий 2, а в регистровую пару HL начало буфера ПДП. Здесь нужно контролировать возврат операции чтения, на окончание файла. Если файл внезапно кончился, выходим из подпрограммы.
Собственно всё! Как закончатся все циклы, картинка будет на экране. Нужно будет уйти на ожидание нажатий любой кнопки, после чего включить часы и выйти из программы.
Отличие загрузки цветной картинки не значительные. У нас теперь в два раза выросла пиксельная строка до 128 байт, то есть до целого сектора, а значит, счетчик линий нам не нужен. Информация в файле чередуется байт графики, байт цвета. После переброски байта графики переходим на цвет «ld a, 11000000B; xor d; ld d, a» и аналогичным образом перебрасываем байт цвета, после чего тем же способом возвращаемся в графику.
Перед выходом из программы, если выводили картинку с палитрой, то восстанавливаем стандартную палитру.
Если у кого остались вопросы он всегда может обратиться по одному из следующих адресов.
FidoNet: Tarasow Aleksey 2:5053/57
E-mail: tae1980(очень злая собака)yandex.ru
А на форуме, в группе ВК есть много интересного, а все новое будет выкладываться, в том числе и там.
По этой ссылке (https://yadi.sk/d/GCBtRwywJSSmpw или https://vk.com/doc359059980_525959959) можно скачать архив lzh, в котором находятся:
m80.com, l80.com - основные файлы пакета m80.
LOADGRF.ASM – исподники кода, описанного в этом статье.
BIOSK.INC – файл для подзагрузки используемых макросов.
LOADGRF.BAT – командные файлы для ассемблирования примера.
LOADGRF.COM - уже с ассемблированный код, готовый для запуска. Запускать из командной строки с указанием в качестве первого аргумента имени файла GRF для вывода на экран.
Набор черно-белых и цветных GRF файлов для теста.
По этой ссылке (https://yadi.sk/d/105vTeEIqrfbVA или https://vk.com/doc359059980_526413291) можно скачать образ диска в формате «pro» (работа с ним аналогична работе с образами дисков в формате «trd») с описанными файлами. Образ диска загрузочный, так что с него можно загрузиться. На IBM PC наиболее популярны два эмулятора, которые могут эмулировать Profi, это ZXMAK2 (https://archive.codeplex.com/?p=zxmak2) и Unreal Speccy (https://sourceforge.net/projects/unrealspeccy/). У Unreal Speccy больше возможностей, но сложнее настройка, тогда когда ZXMAK2 все настройки проведёт сам.
К сожалению, в настоящее время оба эмулятора отображают цвета расширенной палитры Profi с ошибкой, что сказываться на качестве картинки. Хотя ситуация не приятная, но не критичная. Пока могу посоветовать только, для получения наилучшего результата использовать реальное железо.
Ниже приведен основной текст программы находящейся в файле «LOADGRF.ASM».
.Z80
.RADIX 10
;-----------------------------------------------------------
INCLUDE BIOSK.INC ; Загрузка макросов.
;----------------------------------------------------------------
scr_buf EQU 005CH ;Блок управления файлом (БУФ).
BIOS EQU 0F800H
GETADR EQU BIOS+36H
SETCMRS EQU BIOS+2DH
; Таблицы
TIME EQU 7
;----------------------------------------------------------------
;Загружаем картинку.
.bdos bdPDPInst, PDP_SCR ; установили буфер ПДП на нужный адрес
.bdos bdOpen, scr_buf
inc a ; if a<>255
jp nz, grf.Title.ok ; then
; else Если на диске нет такого файла
say ' <!> Нет такого файла <!>'
.bdos bdExit ; <!> Выход <!>
grf.Title.ok:
;читаем заголовок
.bdos bdReadCon scr_buf
ld hl, (PDP_SCR+00) ; HSize горизонтальный размер
ld bc, 512 ;; if FHSize=512
and a ;;
sbc hl, bc ;;
jr z, grf.Title.1 ;; then
;else
say ' <!> Горизонтальный размер картинки не 512 пикселей <!>'
.bdos bdExit ; <!> Выход <!>
grf.Title.1:
ld a, (pdp_scr+09) ; признак стандартного файла
and a ; if a=0 проверка файла на стандартность
jr z, grf.Title.std ; then
;else файл не стандартный =19 - палитра 256; иное - ошибка.
ld a, 02
ld (FColor), a ; палитра 256
ld hl, pdp_scr+10 ; адрес палитры
call PRGPAL ; перенос палитры
jp grf.Title.2
grf.Title.std: ;файл стандартный
ld a, (pdp_scr+04) ; BPP бит на точку или точек в байте
cp 8 ; if BPP=8
jr z,grf.Title.2 ; then картинка чб
ld a, 01 ; else
ld (FColor), a ; BPP=04 стандартные цвета
grf.Title.2:
.timeOFF ; убрали часы
ld de, 0207h or 08D8h ;; включили экран. Графика с #8000, цвет с #4000
call 0f82dH ;;
.bdos bdReadCon, scr_buf ; первый сектор изображения
ld ix, AdrZNgr ; таблица адресов знакомест
ld hl, pdp_scr ; hl=адрес начала ПДП
ld b, 30 ; число выводимых знакомест
di
ld a, (FColor) ; a=наличие цвета (0 - чб, 1-стандартные цвета, 2-палитра)
and a ;; if a=0
jp z, grf.chb ;; then выводим чб картинку
jp grf.color ;; else выводим цветную картинку
grf.endscr:
ei
grf.pause:
ld d, 0
call 0F806H
cp 0
jp z, grf.pause
ld d, 0
call 0F809H
.timeON ; включили часы
ld a, (FColor) ; a=наличие цвета (0 - чб, 1-стандартные цвета, 2-палитра)
ld hl, 0 ; стандартная палитра
cp 2 ;; if a=2
call z, PRGPAL ;; then устанавливаем стандартную палитру
.bdos bdExit ; <!> Выход <!>
;----------------------------------------------------------------------
grf.chb:
;Выводим черно-белую картинку шириною 512 пикселей.
;Портит регистры: hl, bc, de, af, ix, c'
;На входе:
;b - число выводимых знакомест.
;ix - ссылка в таблице адресов знакомест со смещением на первое выводимое знакоместо.
;hl - адрес начала выводимого фрагмента в ПДП. Первый сектор должен быть уже загружен.
;c' - контроль выхода за переделы ПДП.
grf.chb.zn: ; цикл по знакоместам
push bc
ld e, (ix+0) ;; de=адрес начала знакоместа
ld d, (ix+1) ;;
inc ix ; ix=ix+2 смещаем на следующее знакоместо
inc ix ;
ld b, 8 ; число строк в знакоместе
ld c, 2
grf.chb.x: ; цикл по пиксельным линиям в знакоместе
.push <de, bc>
ld b, 32 ; размер полуэкрана
grf.chb.y: ; цикл по выводимой ширине экрана
;первый байт
ld a, (hl) ; графика
inc hl
ld (de), a
res 5, d ;; переходим на следующее знакоместо. выключили бит
;второй байт
ld a, (hl) ; графика
inc hl
ld (de), a
set 5, d ;; переходим на следующее знакоместо. включили бит
inc e ;;
djnz grf.chb.y ; цикл по горизонтали
pop bc
dec c ; c=c-1; if c=0
jr nz, grf.chb.1 ; then
push bc ; else
.bdos bdReadCon, scr_buf ; последовательное чтение
pop bc
ld hl, pdp_scr ; начало ПДП
ld c, 2
and a ; if a=0
jr z, grf.chb.1 ; then операция завершена без ошибки.
.pop <de, bc> ; else
jp grf.endscr ; <!> Выход по ошибки
grf.chb.1:
pop de
inc d ; опускаемся на следующую пиксельную линию в знакоместе
djnz grf.chb.x ; цикл по линиям
pop bc
djnz grf.chb.zn ; цикл по знакоместам
jp grf.endscr ; <!> Выход <!>
;----------------------------------------------------------------------
grf.color:
;Выводим цветную картинку шириною 512 пикселей.
;Портит регистры: hl, bc, de, af, ix, c'
;На входе:
;b - число выводимых знакомест.
;ix - ссылка в таблице адресов знакомест со смещением на первое выводимое знакоместо.
;hl - адрес начала выводимого фрагмента в ПДП. Первый сектор должен быть уже загружен.
;c' - контроль выхода за переделы ПДП.
grf.color.zn: ; цикл по знакоместам
push bc
ld e, (ix+0) ;; de=адрес начала знакоместа
ld d, (ix+1) ;;
inc ix ; ix=ix+2 смещаем на следующее знакоместо
inc ix ;
ld b, 8 ; число строк в знакоместе
grf.color.x: ; цикл по пиксельным линиям в знакоместе
.push <bc, de>
ld b, 32 ; размер полуэкрана
grf.color.y: ; цикл по выводимой ширине экрана
;первый байт
ld a, (hl) ; графика
inc hl
ld (de), a
ld a, 11000000B ; переходим на цвет
xor d ;
ld d, a ;
ld a, (hl) ;; цвет
inc hl
ld (de), a
ld a, 11000000B ; переходим на графику
xor d ;
ld d, a ;
res 5, d ;; переходим на следующее знакоместо. выключили бит
;второй байт
ld a, (hl) ; графика
inc hl
ld (de), a
ld a, 11000000B ; переходим на цвет
xor d ;
ld d, a ;
ld a, (hl) ;; цвет
inc hl
ld (de), a
ld a, 11000000B ; переходим на графику
xor d ;
ld d, a ;
set 5, d ;; переходим на следующее знакоместо. включили бит
inc e ;;
djnz grf.color.y ; цикл по горизонтали
.bdos bdReadCon, scr_buf ; последовательное чтение
ld hl, pdp_scr ; начало ПДП
and a ; if a=0
jr z, grf.color.end ; then операция завершена без ошибки.
.pop <de, bc, bc> ; else
jp grf.endscr ; <!> Выход по ошибки
grf.color.end:
.pop <de, bc>
inc d ; опускаемся на следующую пиксельную линию в знакоместе
djnz grf.color.x ; цикл по линиям
pop bc
djnz grf.color.zn ; цикл по знакоместам
jp grf.endscr ; <!> Выход <!>
;----------------------------------------------------------------------
;Программирование палитры.
;На входе: hl - адрес новой палитры или 0 для установки стандартной [0].
;
;Портит: всё.
PRGPAL:
di
push hl
ld c, TIME ;; C=номер таблицы. 7 - TIME вектор TIK,SEC,MIN,HOUR, DAY,MONTH,YEAR-1980. Кроме того , со смещением -1 лежит BORD
call getadr ;; Получаем адрес таблицы
dec hl ;; смещением -1 на BORD
ld a, (hl) ;;
ld (?BORD), a ;; Что бы не расчитывать в цикле
ld c, 0 ; C=номер таблицы. 0
call getadr ; Получаем адрес таблицы
ld a, (hl) ;
ld e, a ;
inc hl ;
ld a, (hl) ;
ld d, a ;
pop hl
push de
push hl
set 5, d
call SETCMRS
pop hl
ld a, h ; if hl<>0
or l ;
jr nz, PRGPAL.P1 ; then
ld hl, Palette.Std; else
PRGPAL.P1:
push hl
ld hl, (38h)
ex (sp), hl ; отправили на стек содержимое 38h в hl=hl с входа
ld de, 0c9fbh ; ei ret
ld (38h), de
ei
halt
di
;порт палитры 7E, до выдачи данных в рег бордера задаем инверсный код номера ячейки 00..0F
;значение цвета задается в битах A15-A8 при выполнении записи в порт 7E,
;командой out (c),e
;а выводимое значение (E) запишется в порт BORDER для следующего цикла
; hl содержит адрес данных палитры
ld a, (?BORD)
or 0Fh ; в А бит звука и код 0F для цвета (ячейка 0)
ld e, a
ld c, 7Eh ; порт записи для данных палитры
ld d, 16 ; всего 16 ячеек
out (c), e ; сейчас вывели в BORDER значение 0Fh
;конечно, произошло и программирование палитры той ячейки, какой BORDER был до этого, скорее всего
;BORD был равен 7, а это номер ячейки 8 (7 xor 0F) но так как потом мы зальём все 16 ячеек это не страшно
dec e
PRGPAL.prg2:
ld a, (hl) ; значение цвета 8 бит
cpl ; инверитруем (так надо по схеме компа)
ld b, a
out (c), e ; задали цвет и номер бордер для следующей ячейки
inc hl
dec e ; следующий цвет
dec d
jr nz, PRGPAL.prg2
ld a, (?BORD) ; восстановили BORDER
out (0feh), a
pop hl
ld (38h), hl
pop de
call SETCMRS
rst 38h
ei
ret
;----------------------------------------------------------------------
Palette.Std:
DB 0 ; BLACK
DB 00000010B
DB 00010000B
DB 00010010B
DB 10000000B
DB 10000010B
DB 10010000B
DB 10010010B ; WHITE
DB 0 ; gray
DB 00000011B
DB 00011000B
DB 00011011B
DB 11000000B
DB 11000011B
DB 11011000B
DB 11011011B ; WHITE (BRIGHT 1)
;----------------------------------------------------------------------
AdrZNgr: ; Таблица адресов начала знакомест
dw 0A000H, 0A020H, 0A040H, 0A060H, 0A080H, 0A0A0H, 0A0C0H, 0A0E0H
dw 0A800H, 0A820H, 0A840H, 0A860H, 0A880H, 0A8A0H, 0A8C0H, 0A8E0H
dw 0B000H, 0B020H, 0B040H, 0B060H, 0B080H, 0B0A0H, 0B0C0H, 0B0E0H
dw 0B800H, 0B820H, 0B840H, 0B860H, 0B880H, 0B8A0H
AdrZNgr_end:
;----------------------------------------------------------------
;Информация о загружаемой картинке
FColor: db 00 ;Наличие цвета. 0-нет;
;grf 1-стандартные цвета; 2-палитра
FPallet: ds 16 ;Палитра файла.
PDP_SCR: ds 128
E_PDP_SCR:
END
Приложение. Используемые макросы
.bdos macro com, par
;Вызов операции BDOS.
;com - код операции
;par - параметр для передачи
ifnb <par>
ld de, par
endif
ld c, com
call 0005h
endm
;-----------------------------------------------------------
say macro STR, REG
;Вывод строки на экран.
;Все регистры сохраняются.
;STR - строка для вывода.
;REG - имя регистра (a, sp, hl, de, bc, ix, iy) для вывода в десятичном виде. []
;Сохраняет: af, hl, bc, de
local BUFF, B_END
ifnb <str>
.push <af, hl, de, bc>
.bdos bdWrite, buff
jr B_END
BUFF: db STR, '$'
B_END:
.pop <bc, de, hl, af>
endif
ifnb <reg>
.push <af, hl, de, bc>
.writed REG
.pop <bc, de, hl, af>
endif
endm
;-----------------------------------------------------------
pause macro
;Макрос ожидания нажатия любой клавиши.
;Если нажатие ECS – сброс системы, выход из текущей программы.
;Сохраняет: af, hl, bc, de
local p0
.push <af, hl, bc, de>
p0: ld d, 0
call 0F806H
cp 0
jp z, p0
ld d, 0
call 0F809H
.pop <de, bc, hl>
cp 1bh ;ESC
jp z, 0
pop af
endm
;-------------------------------------------------------------
.writed macro REG, IND
;Операция 50/243.
;Вывод в десятичном формате.
;Если на входе:
;ING=0 печатаются регистры A,SP,HL,DE,BC,IX,IY. По умолчанию.
;ING=1 печатаются целые числа.
;Портит все регистры.
local BUFF1
if IND ;IND<>0
ld de, REG ;Передали цифру
ld (BUFF1+1), DE
else
ld (BUFF1+1), REG ;Передали регистр
endif
.bdos bdBSVV, buff1
jr BUFF1+3
BUFF1: db 0F3H
dw 0000
endm
;-----------------------------------------------------------
.timeOFF macro
;Отключения вывода таймера.
;Используется процедуры BIOS'а. Для драйвера TIME2.DRV
ld c,80H
ld d,1BH
call 0F80CH
or 080H
ld c,a
ld d,01BH
call 0F80CH
endm
;-----------------------------------------------------------
.timeON macro
;Включение вывода таймера.
;Используется процедуры BIOS'а. Для драйвера TIME2.DRV
ld c,80H
ld d,1BH
call 0F80CH
and 07FH
ld c,a
ld d,01BH
call
endm
;-----------------------------------------------------------
.Pop Macro Items
; (c) PSWsoft
Irp x,<Items>
POP x
Endm
EndM
;-----------------------------------------------------------
.Push Macro Items
; (c) PSWsoft
Irp x,<Items>
PUSH x
Endm
EndM