2.9. МЕТОДИКА РАСЧЕТА АДРЕСОВ В ЭКРАННОЙ
ОБЛАСТИ ПО ЗАДАННЫМ КООРДИНАТАМ
Расчет адресов в экранной области па- г=================¬
мяти - это основная задача, встающая пе- ¦ ------ --- ¦
раед каждым, кто хочет заняться компью- ¦ - - - ¦
терной графикой, работая в машинном коде. ¦ ------ - ¦
Не приобретя практики в этом вопросе труд- ¦ - - - --- - ¦
но двигаться вперед. L=================-
2.9.1. Расчет адреса в дисплейном файле
(координаты заданы в знакоместах).
Предположим, что координаты X и Y заданы в занкоместах и
находятся в регистровой паре DE. В регистре D - координата X, а
в регистре E - координата Y. Генеральной задачей является полу-
чить в регистровой паре HL адрес в дисплейном файле, соответ-
ствующий левому верхнему углу заданного знакоместа.
Приступая к этой задаче, напомним читателю как адрес в
регистровой паре HL соответствует координатам экрана:
H L
-------------+-------------¬ --------------+-------------¬
¦ ¦ ¦ ¦
г==T===T===T===T===T===T===T===T===T===T===T===T===T===T===T==¬
¦0 ¦ 1 ¦ 0 ¦ X ¦ X ¦ X ¦ X ¦ X ¦ X ¦ X ¦ X ¦ X ¦ X ¦ X ¦ X ¦ X¦
L==¦=T=¦===¦===¦===¦===¦===¦===¦===¦===¦===¦===¦===¦===¦===¦==-
¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦
¦ L----- L-------- L-------- L----------------
Адрес 16384 Номер Номер Номер Номер столбца
сегмента линии ряда
0...2 0...7 0...7 0 ... 31
Рис. 16.
На первом этапе по координате Y определяется номер сегмен-
та экрана. Т.к. координата Y не может быть больше 23-х, то для
ее выражения достаточно 5-ти битов.
LD A,E
г===T===T===T===T===T===T===T===¬
¦ 0 ¦ 0 ¦ 0 ¦ ? ¦ ? ¦ ? ¦ ? ¦ ? ¦
L===¦===¦===¦===¦===¦===¦===¦===-
Поскольку в каждом сегменте по 8 рядов, то на номер
сегмента указывают биты 3 и 4, поэтому замаскировав биты 0, 1,
2, получим указание на номер экранного сегмента:
AND 18
г===T===T===T===T===T===T===T===¬
¦ 0 ¦ 0 ¦ 0 ¦Сег¦Сег¦ 0 ¦ 0 ¦ 0 ¦
L===¦===¦===¦===¦===¦===¦===¦===-
Командой OR 40 включим 6-ой бит, что послужит указанием
на начало дисплейного файла:
OR 40
г===T===T===T===T===T===T===T===¬
¦ 0 ¦ 1 ¦ 0 ¦Сег¦Сег¦ 0 ¦ 0 ¦ 0 ¦
L===¦===¦===¦===¦===¦===¦===¦===-
А теперь сравните полученную конструкцию с тем, что должно
быть в регистре H (см. Рис.16). Таким образом, старший байт
адреса мы уже сформировали.
LD H,A
Выставили старший байт адреса в дисплейном файле. У нас,
правда, занулены биты 0,1,2, указывающие на линию в ряду, но
нас они и не интересуют, поскольку координаты заданы в знако-
местах, а не в пикселах и верхнему левому углу знакоместа
соответствует его нулевая (верхняя линия).
Теперь займемся вычислением младшего байта адреса для по-
мещения его в регистр L.
LD A,E
Вновь рассматриваем координату Y.
г===T===T===T===T===T===T===T===¬
¦ 0 ¦ 0 ¦ 0 ¦ ? ¦ ? ¦ ? ¦ ? ¦ ? ¦
L===¦===¦===¦===¦===¦===¦===¦===-
AND 07
г===T===T===T===T===T===T===T===¬
¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦Ряд¦Ряд¦Ряд¦
L===¦===¦===¦===¦===¦===¦===¦===-
Оставив три младших бита, мы выделили тем самым номер
ряда в экранном сегменте.
OR A
Обнулили флаг C в регистре F
г===T===T===T===T===T===T===T===¬ г===¬
¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦Ряд¦Ряд¦Ряд¦ ¦ 0 ¦
L===¦===¦===¦===¦===¦===¦===¦===- L===-
RRA
г===T===T===T===T===T===T===T===¬ г===¬
¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦Ряд¦Ряд¦ ¦Ряд¦
L===¦===¦===¦===¦===¦===¦===¦===- L===-
RRA
г===T===T===T===T===T===T===T===¬ г===¬
¦Ряд¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦Ряд¦ ¦Ряд¦
L===¦===¦===¦===¦===¦===¦===¦===- L===-
RRA
г===T===T===T===T===T===T===T===¬ г===¬
¦Ряд¦Ряд¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ ¦Ряд¦
L===¦===¦===¦===¦===¦===¦===¦===- L===-
RRA
г===T===T===T===T===T===T===T===¬ г===¬
¦Ряд¦Ряд¦Ряд¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ ¦ 0 ¦
L===¦===¦===¦===¦===¦===¦===¦===- L===-
В результате четырех шагов вращения через флаг C три
младших бита, указывавшие на номер ряда, стали старшими.
ADD A,D
г===T===T===T===T===T===T===T===¬
¦Ряд¦Ряд¦Ряд¦ Ст¦ Ст¦ Ст¦ Ст¦ Ст¦
L===¦===¦===¦===¦===¦===¦===¦===-
Прибавив значение столбца, а оно меньше 32 и потому зани-
мает только пять младших битов, мы получили конструкцию,
полностью соответствующую младшему байту адреса, изображенному
на рис. 16 .
LD L,A
Выставили младший байт адреса и задача выполнена.
2.9.2. Расчет адреса в файле атрибутов.
Атрибуты "привязаны" только к знакоместам и потому коорди-
наты могут быть заданы только в знакоместах. Предположим, что
координата Y содержится в регистре E, а координата X - в ре-
гистре D. Генеральная задача - сформировать в регистровой паре
HL двухбайтный адрес, указывающий на ячейку памяти, в которой
содерится байт атрибутов для нашего знакоместа.
Напомним, как адрес в регистровой паре HL соответствует
атрибутам экрана.
H L
-------------+-------------¬ --------------+-------------¬
¦ ¦ ¦ ¦
г==T===T===T===T===T===T===T===T===T===T===T===T===T===T===T==¬
¦0 ¦ 1 ¦ 0 ¦ 1 ¦ 1 ¦ 0 ¦ X ¦ X ¦ X ¦ X ¦ X ¦ X ¦ X ¦ X ¦ X ¦ X¦
LT=¦===¦===¦===¦=T=¦===¦=T=¦===¦===¦===¦===¦===¦===¦===¦===¦==-
L------T--------- ¦ ¦ ¦ ¦
¦ L---------------- L----------------
адрес 22528 Номер Номер столбца
ряда
0...23 0 ... 31
Рис. 17
Принимаем в аккумулятор параметр Y.
LD A,E
г===T===T===T===T===T===T===T===¬
¦ 0 ¦ 0 ¦ 0 ¦Ряд¦Ряд¦Ряд¦Ряд¦Ряд¦
L===¦===¦===¦===¦===¦===¦===¦===-
Оставляем от номера ряда два старших бита.
AND 18
г===T===T===T===T===T===T===T===¬
¦ 0 ¦ 0 ¦ 0 ¦Ряд¦Ряд¦ 0 ¦ 0 ¦ 0 ¦
L===¦===¦===¦===¦===¦===¦===¦===-
Тремя шагами логического вращения влево делаем эти два
бита младшими.
SRL A
г===T===T===T===T===T===T===T===¬
¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦Ряд¦Ряд¦ 0 ¦ 0 ¦
L===¦===¦===¦===¦===¦===¦===¦===-
SRL A
г===T===T===T===T===T===T===T===¬
¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦Ряд¦Ряд¦ 0 ¦
L===¦===¦===¦===¦===¦===¦===¦===-
SRL A
г===T===T===T===T===T===T===T===¬
¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦Ряд¦Ряд¦
L===¦===¦===¦===¦===¦===¦===¦===-
Включаем биты 3,4,6 - они дают указание на начало файла
атрибутов.
OR 58
г===T===T===T===T===T===T===T===¬
¦ 0 ¦ 1 ¦ 0 ¦ 1 ¦ 1 ¦ 0 ¦Ряд¦Ряд¦
L===¦===¦===¦===¦===¦===¦===¦===-
Если сравнить полученную конструкцию с рис. 16, то видно,
что она полностью соответствет старшему байту адреса, который
должен быть в регистре H. Туда мы его и помещаем.
LD H,A
Старший байт сформирован. Приступаем к формированию млад-
шего байта. Вновь принимаем в аккумулятор координату Y.
LD A,E
г===T===T===T===T===T===T===T===¬
¦ 0 ¦ 0 ¦ 0 ¦Ряд¦Ряд¦Ряд¦Ряд¦Ряд¦
L===¦===¦===¦===¦===¦===¦===¦===-
Оставляем три младших бита.
AND 07
г===T===T===T===T===T===T===T===¬
¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦Ряд¦Ряд¦Ряд¦
L===¦===¦===¦===¦===¦===¦===¦===-
Обнуляем флаг C в регистре F. Флаг С.
/
OR A /
г===T===T===T===T===T===T===T===¬ г===¬
¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦Ряд¦Ряд¦Ряд¦ ¦ 0 ¦
L===¦===¦===¦===¦===¦===¦===¦===- L===-
RRA
г===T===T===T===T===T===T===T===¬ г===¬
¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦Ряд¦Ряд¦ ¦Ряд¦
L===¦===¦===¦===¦===¦===¦===¦===- L===-
RRA
г===T===T===T===T===T===T===T===¬ г===¬
¦Ряд¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦Ряд¦ ¦Ряд¦
L===¦===¦===¦===¦===¦===¦===¦===- L===-
RRA
г===T===T===T===T===T===T===T===¬ г===¬
¦Ряд¦Ряд¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ ¦Ряд¦
L===¦===¦===¦===¦===¦===¦===¦===- L===-
RRA
г===T===T===T===T===T===T===T===¬ г===¬
¦Ряд¦Ряд¦Ряд¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ ¦ 0 ¦
L===¦===¦===¦===¦===¦===¦===¦===- L===-
В результате четырех шагов вращения через флаг C три
младших бита номера ряда стали старшими.
ADD A,D
г===T===T===T===T===T===T===T===¬
¦Ряд¦Ряд¦Ряд¦ Ст¦ Ст¦ Ст¦ Ст¦ Ст¦
L===¦===¦===¦===¦===¦===¦===¦===-
Прибавив значение столбца (оно меньше 32 и потому зани-
мает только пять младших битов), мы получили конструкцию,
полностью соответствующую младшему байту адреса, изображенному
на рис. 16 .
LD L,A
Выставили младший байт адреса - задача выполнена.
2.9.3. Расчет адреса в дисплейном файле
(координаты заданы в пикселах).
Это наиболее трудоемкая задача из трех. Предположим, что
координаты точки экрана содержатся в регистрах D (x) и E (y).
Наша задача определить адрес в дисплейном файле, соответствую-
щий данной точке. Надо сказать, что такая постановка не вполне
корректна, т.к. минимальной единицей хранения данных является
байт, а он определяет линию в знакоместе, т.е. 8 пикселов, а не
один. Поэтому задача становится чуть глубже. Надо будет еще
найти бит в байте, соответствующий заданной точке.
Попутно надо еще решить одну мелкую проблему. Дело в том,
что координата y (когда она задана в пикселах) отсчитывается по
экрану снизу вверх. Мы об этом уже говорили. А общая тенденция
нарастания адресов в дисплейном файле - сверху вниз. Поэтому
надо координату y преобразовать. Это делают путем подмены.
Вместо y в проработку берут дополнение y до 175. Получается
так, что y как бы инвертируется (назовем его y'). Тогда верхней
линии экрана соответствует нулевое значение y', а нижней -
максимальное значение.
Загружаем в аккумулятор число 175:
LD A,0AFH
и вычитаем из него y:
SUB E
LD E,A
Теперь в регистре E у нас находится y'. Раскладка битов в
регистре E и в аккумуляторе после этого может быть, например
такой:
г===T===T===T===T===T===T===T===¬
¦e7 ¦ e6¦ e5¦ e4¦ e3¦ e2¦ e1¦ e0¦
L===¦===¦===¦===¦===¦===¦===¦===-
Выключим флаг C в регистре F: Флаг C
/
AND A /
г===T===T===T===T===T===T===T===¬ г===¬
¦e7 ¦ e6¦ e5¦ e4¦ e3¦ e2¦ e1¦ e0¦ ¦ 0 ¦
L===¦===¦===¦===¦===¦===¦===¦===- L===-
RRA
г===T===T===T===T===T===T===T===¬ г===¬
¦ 0 ¦ e7¦ e6¦ e5¦ e4¦ e3¦ e2¦ e1¦ ¦ e0¦
L===¦===¦===¦===¦===¦===¦===¦===- L===-
Включим флаг C:
SCF
г===T===T===T===T===T===T===T===¬ г===¬
¦ 0 ¦ e7¦ e6¦ e5¦ e4¦ e3¦ e2¦ e1¦ ¦ 1 ¦
L===¦===¦===¦===¦===¦===¦===¦===- L===-
RRA
г===T===T===T===T===T===T===T===¬ г===¬
є 1 є 0 є e7є e6є e5є e4є e3є e2є є e1є
L===¦===¦===¦===¦===¦===¦===¦===- L===-
Выключим флаг C:
AND A
г===T===T===T===T===T===T===T===¬ г===¬
¦ 1 ¦ 0 ¦ e7¦ e6¦ e5¦ e4¦ e3¦ e2¦ ¦ 0 ¦
L===¦===¦===¦===¦===¦===¦===¦===- L===-
RRA
г===T===T===T===T===T===T===T===¬ г===¬
¦ 0 ¦ 1 ¦ 0 ¦ e7¦ e6¦ e5¦ e4¦ e3¦ ¦ e2¦
L===¦===¦===¦===¦===¦===¦===¦===- L===-
А теперь подменим биты 0, 1, 2 в аккумуляторе соответству-
ющими им битами из регистра E:
XOR E
AND 0F8H
XOR E
Ъ---------------------------------------------------------------ї
¦ ПРИМЕЧАНИЕ Й-----------------"¦
¦ Три последние операции XOR,AND,XOR об- ¦ ЫЯЯЯЯЬ ЯЫЯ ¦¦
¦разуют одну интересную комплексную опера- ¦ Ы Ы Ы ¦¦
¦цию замещения. Если у Вас, например, есть ¦ ЫЯЯЯЯЬ Ы ¦¦
¦два байта - A и B и Вы хотите несколько ¦ Ы Ы Ь ЬЫЬ Ь ¦¦
¦битов в байте A заменить на соответствую- И-----------------ј¦
¦щие биты из байта B, то эти операции позволят это сделать. За-¦
¦мещены будут те биты, которые замаскированы в операции AND¦
¦нулями. Например пусть A имеет раскладку a7a6a5a4a3a2a1a0, а B¦
¦имеет раскладку b7b6b5b4b3b2b1b0 и Вам надо заменить a5 и a3 на¦
¦b5 и b3. Тогда в операции AND эти биты должны быть замаскирова-¦
¦ны. ¦
¦ Исходно: a7 a6 a5 a4 a3 a2 a1 a0 ¦
¦ ¦
¦ XOR B: b7 b6 b5 b4 b3 b2 b1 b0 ¦
¦ AND 214: 1 1 0 1 0 1 1 1 ¦
¦ XOR B: b7 b6 b5 b4 b3 b2 b1 b0 ¦
¦ _________________________________ ¦
¦ Результат: a7 a6 b5 a4 b3 a2 a1 a0 ¦
А---------------------------------------------------------------Щ
В результате трех последних операций образуется следующая
раскладка битов:
+-------------------------------+
¦ 0 є 1 є 0 є e7є e6є e2є e1є e0¦
+-------------------------------+
Эта картина уже соответствует тому, что мы должны получить
в регистре H (см. рис.15).
LD H,A
Таким образом, по дополнению координаты y до 175 можно
определить номер сегмента экрана (e7,e6) и номер линии
(e2,e1,e0) в неизвестном (пока) ряду этого сегмента. Номер
ряда мы установим, когда рассмотрим координату x и сможем
заполнить регистр L.
Рассмотрим теперь координату x.
LD A,D
Раскладка битов в общем случае может быть например такой:
+-------------------------------+
¦d7 є d6є d5є d4є d3є d2є d1є d0¦
+-------------------------------+
Делаем три шага вращения влево:
RLCA
+-------------------------------+
¦d6 є d5є d4є d3є d2є d1є d0є d7¦
+-------------------------------+
RLCA
+-------------------------------+
¦d5 є d4є d3є d2є d1є d0є d7є d6¦
+-------------------------------+
RLCA
+-------------------------------+
¦d4 є d3є d2є d1є d0є d7є d6є d5¦
+-------------------------------+
И второй раз выполняем рассмотренную выше операцию
логического замещения.
XOR E
AND C7
XOR E
В итоге получаем:
+-------------------------------+
¦d4 є d3є e5є e4є e3є d7є d6є d5¦
+-------------------------------+
Еще два шага вращения влево:
RLCA
+-------------------------------+
¦d3 є e5є e4є e3є d7є d6є d5є d4¦
+-------------------------------+
RLCA
+-------------------------------+
¦e5 є e4є e3є d7є d6є d5є d4є d3¦
+-------------------------------+
Теперь конструкция в аккумуляторе полностью соответствует
младшему байту адреса дисплейного файла (см. рис. 15).
LD L,A
Младший байт адреса в дисплейном файле сформирован. В нем
старшие три байта указывают на номер ряда внутри установленного
сегмента экрана, а младшие пять байтов - на номер столбца.
Теперь мы знаем номер сегмента, ряда, столбца и линии. Все
эти данные хранятся в регистровой паре HL. Последнее, что оста-
лось сделать - это вспомнить, что данная линия в своем знако-
месте имеет 8 пикселов, а нам нужен только один из них - необ-
ходимый. Фактически его номер определяется остатком от деления
координаты x на 8, а вычисляется этот остаток - путем маскиро-
вания пяти старших битов, но есть еще один нюанс. Дело в том,
что координата x изменяется слева направо 0,1,2... 255, а
номера битов в байте, идут наоборот 7,6,5.....0. Поэтому нужно
сделать преобразование.
Возьмем координату x:
LD A,D
+-------------------------------+
¦d7 є d6є d5є d4є d3є d2є d1є d0¦
+-------------------------------+
и замаскируем пять старших битов:
AND 07
+-------------------------------+
¦ 0 є 0 є 0 є 0 є 0 є d2є d1є d0¦
+-------------------------------+
Фактически мы имеем в аккумуляторе остаток от деления на 8.
Поместим его в регистр B
LD B,A
и выполним преобразование:
LD A,08
SUB B
LD B,A
Итак, наша задача выполнена. В регистровой паре HL содер-
жится адрес, по которому находится байт, задающий на экране
линию, которой принадлежит наша исходная координата. А регистр
B содержит номер бита, соответствующий нужной нам точке в этой
линии.
2.10 УНИВЕРСАЛЬНАЯ ПРОГРАММА СКАНИРОВАНИЯ ЭКРАНА
Эта программа может выполнить сканирование экрана в
заданном знакоместе и определить что за символ там содержится.
В отличие от функции БЕЙСИКа SCREEN$ (X,Y) и от ее
машиннокодовых аналогов из системного ПЗУ компьютера, данная
программа способна распознать и символы графики пользователя и
символы блочной графики.
Программа имеет три различных точки входа. Какой из них
пользоваться зависит от того, в каком виде заданы начальные
условия. Если координаты сканируемого знакоместа заданы на вер-
шине стека калькулятора в порядке Y,X, то процедура вызывается
по метке SCR_FP. При этом результат подается на вершину стека
калькулятора.
Если входные координаты задаются в качестве параметров
функции пользователя FN S (), то точкой входа является метка
SCR_FN. В этом случае сама функция пользователя должна быть
задана как DEF FN S(X,Y) = USR addr, где addr - адрес метки
SCR_FN. Вызов же процедуры тогда выполняется командой RANDOMIZE
FN S(X,Y), где вместо X и Y Вы подставите конкретные параметры.
Если координаты задаются через регистры B (координата x) и
C (координата y), то процедура вызывается по метке SCR_1. Это
основная точка входа. Все остальные только выполняют преобра-
зование входящих данных и обращаются сюда же.
Результат работы процедуры определяется состоянием флага
Z (ZERO) регистра F. Если флаг включен, то сканирование прошло
успешно и символ идентифицирован. Если флаг выключен - такого
символа не существует. В случае успешного поиска код символа
можно узнать в аккумуляторе процессора.
Программа SCANER.
SCR_FP CALL 2307H ;Вызов процедуры ПЗУ STK_TO_BC.
;Процедура служит для размеще-
;ния содержимого вершины стека
;калькулятора в регистровой
;паре BC.
CALL SCR_1 ;Вызов другой точки входа. В
;результате ее работы получим
;в регистре A код символа, со-
;держащегося в тестируемом зна-
;коместе.
LD BC,0000 ;Инициализация BC.
JR NZ,SCR_STR ;Если символ не идентифициро-
;ван, то переход.
INC BC ;В паре BC теперь 1. Это длина
;символьной строки, в которой
;только один символ.
RST 30 ;Вызов этой системной процедуры
;ПЗУ резервирует пространство,
;размер которого стоит в BC. На
;адрес указывает DE.
LD (DE),A ;Заслали найденный символ.
SCR_STR JP 2AB2 ;Это точка входа системной
;процедуры ПЗУ STACK_AEDCB,
;которая помещает на вершину
;стека калькулятора данные из
;регистров A,E,D,C,B.
;Эта процедура сама совершит и
;возврат в вызывающую программу.
SCR_FN LD HL,(DEFADD) ;Указание на аргументы пользо-
;вательской функции. Подроб-
;ности см. в разделе 3.1.
LD DE,0004
ADD HL,DE ;Сдвиг на 4 байта.
LD C,(HL) ;Координата Y.
ADD HL,DE ;
ADD HL,DE ;Сдвиг на 8 байтов.
LD B,(HL) ;Координата X.
CALL SCR_1 ;Вызов другой точки входа. В
;результате ее работы получим
;в регистре A код символа, со-
;держащегося в тестируемом зна-
;коместе.
JR Z,SCR_SINGL ;Если символ найден - переход.
XOR A ;В противном случае в аккуму-
;ляторе выставляем ноль.
SCR_SINGL LD C,A ;Подготовка к
LD B,00 ;выходу.
RET ;Выход.
Основной является точка входа SCR_1, т.к. остальные
сводятся к ней же. Итак, BC содержит координаты X и Y.
SCR_1 LD A,C ; є
RRCA ; є
RRCA ; є Это поиск адреса зна-
RRCA ; є коместа в дисплейной
AND E0 ; є памяти по заданным
XOR B ; є координатам.
LD E,A ; є
LD A,C ; є См. стр. 86...89
AND 18 ; є
XOR 40 ; є
LD D,A ; є
PUSH DE ; є
Итак, адрес знакоместа установлен в регистровой паре DE и
сохранен на стеке. Теперь решается вопрос о том шаблон какого
же символа изображен в этом знакоместе.
LD HL, (CHARS) ;Системная переменная CHARS
;(23606 = 5C36) указывает на
;256 байтов ниже, чем начало
;таблицы шаблонов символов.
INC H ;Теперь HL указывает точно
;на начало таблицы шаблонов.
CALL 254DH ;Адрес 254DH - одна из точек
;входа в процедуру ПЗУ
;S_SCRN_$, которая выполняет
;проверку на то является ли
;тестируемый шаблон символом
;ASCII. Результат остается на
;вершине стека калькулятора.
CALL 2BF1H ;Адрес 2BF1 - точка входа про-
;цедуры ПЗУ STK_FETCH, которая
;снимает верхние пять значений
;со стека калькулятора и раз-
;носит их по регистрам
;A,E,D,C,B.
;Теперь в аккумуляторе имеется
;код символа, если шаблон в тес-
;тируемом знакоместе является
;таковым.
POP DE ;Восстановили адрес знакоместа.
DEC C
RET Z ;Выход, если поиск был успешным.
Если имеющийся в данном знакоместе символ не является сим-
волом ASCII, начинаем проверять, а не является ли он символом
графики пользователя UDG.
PUSH DE ;Запомнили адрес знакоместа.
LD HL,(UDG) ;Системная переменная UDG
;(23675 = 5C7BH) указывает на
;адрес, начиная с которого хра-
;нятся шаблоны символов графики
;пользователя.
LD B,15H ;15H = 21 DEC - количество сим-
;волов графики пользователя.
CALL 254F ;Это точка входа в системную
;процедуру S_SCRN_LP. Она про-
;верит есть ли символ UDG, со-
;ответствующий нашему знакомес-
;ту и поместит результат на
;вершину стека калькулятора.
CALL 2BF1 ;Вход в STK_FETCH и разнесение
;вершины стека калькулятора по
;регистрам A,E,D,C,B.
Здесь надо сделать одно замечание. Дело в том, что на вер-
шину стека, а затем и в акуумулятор пойдет не код символа UDG,
а некоторое другое число, которое надо еще скорректировать.
Причина в том, что процедура S_SCRN_LP в ПЗУ не предназначалась
для работы с символами UDG, а только с символами ASCII, в
результате чего возникает следующий эффект. Код найденного
символа, помещаемый в аккумулятор, определяется как разность
между числом 128 (предельный код символа ASCII) и тем, что
осталось в счетчике проверяемых символов (регистр B). Так как
символы UDG имеют конечный код равный 165, а не 128, то к
полученному в аккумуляторе результату необходимо добавить
недостающую разницу - число 37 (25H).
ADD A,25 ;Выполнили коррекцию.
POP HL ;Теперь адрес знакоместа - в
;паре HL.
DEC C
RET Z ;Возврат, если поиск был ус-
;пешным.
Если же поиск и на сей раз прошел безуспешно, нам остается
только проверить: "А не является ли тестируемый символ символом
блочной графики?" Проверка выполняется в два этапа. Сначала
проверяется верхняя половина символа, а потом нижняя. Если
первая проверка не прошла, то вторую выполнять нет смысла. Об-
ратите также внимание на то, что у любого символа блочной гра-
фики 4 линии в каждой половине равны между собой и потому
проверять можно не все четыре линии, а любую из них.
CALL TEST_HALF ;Проверка верхней половины
;символа.
RET NZ ;Возврат, если проверка не
;прошла.
LD C,A ;Если проверка прошла, то в
;регистре C запоминается би-
;товая рскладка линии верхней
;половины символа.
CALL TEST_HALF ;Проверка нижней половины
;символа.
RET NZ ;Возврат, если проверка не
;прошла.
ADD A,A ;Здесь по конструкции символа
ADD A,A ;блочной графики определяется
ADD A,C ;его номер.
ADD A,80 ;
CP A ;Включение флага Z как сигнал
;о том, что поиск был резуль-
;тативным.
RET ;Окончательный выход в вызы-
;вающую программу.
Подпрограмма TEST_HALF выполняет тестирование верхней или
нижней половины знакоместа в попытке определить не является ли
помещенный там символ символом блочной графики.
TEST_HALF CALL TEST_LINE ;Вызов подпрограммы, которая
;выполняет проверку первой
;линии данной половины.
RET NZ ;Возврат, если она не совпала.
LD D,A ;Если совпала, то ее код надо
;запомнить, ибо последующие
;три должны быть такими же.
LD B,03 ;Счетчик цикла для трех линий.
LOOP CALL TEST_LINE ;Вершина цикла для трех прове-
;рок линий.
RET NZ ;Возврат, если хотя бы одна из
;линий не соответствует блочной
;графике.
CP D ;Если она соответствует, но от-
RET NZ ;личается от первой, то тоже
;возврат.
DJNZ LOOP ;Конец цикла по линиям.
RET ;Выход из подпрограммы.
Подпрограмма TEST_LINE проверяет соответствуют ли линии
проверяемого знакоместа символам блочной графики. Конструкция
линии задается одним байтом. Поскольку символы блочной графики
имеют определенную симметрию, то многое можно о них сказать,
проверив старший полубайт (4 бита) и младший полубайт (4 бита).
Если один из них не соответствует конструкции символа блочной
графики, то дальнейшее исследование символа бесполезно.
TEST_LINE LD A,(HL) ;HL указывает на адрес линии
;в дисплейном файле. Теперь в
;регистре A фактически содер-
;жится конструкция линии.
INC H ;Переход к следующей линии.
LD E,00 ;Инициализация регистра E.
CALL TEST_NIBBLE ;Проверка старшего полубайта.
RET NZ ;Возврат, если она не прошла.
RLC E ;Раскладка битов в регистре
;E принимает вид:
; 0 0 0 0 0 0 0 ah
;где ah - состояние старшего
;полубайта в тестируемой
;линии.
CALL TEST_NIBBLE ;Проверка младшего полубайта.
RET NZ ;Возврат, если она не прошла.
LD A,E ;Аккумулятор имеет вид:
; al 0 0 0 0 0 0 ah, где
;ah и al - состояние старшего
;и младшего полубайтов в тес-
;тируемой линии.
RLCA ;Аккумулятор имеет вид:
; 0 0 0 0 0 0 ah al
;и эта информация уже может
;быть использована для
;вычисления кода символа.
CP A ;Включение флага Z как сигнал
;о том, что поиск был резуль-
;тативным.
RET ;Возврат.
Подпрограмма TEST_NIBBLE проверяет полубайты каждой линии
на предмет соответствия их конструкции символам блочной графи-
ки. При этом используется тот очевидный факт, что в любом
символе блочной графики все четыре старших (или младших) бита
должны быть одинаково включены или выключены.
TEST_NIBBLE PUSH BC ;Сохранили на стеке счетчик
;линий в B, поскольку ниже
;этот регистр будет использован
;как счетчик битов в полубайте.
RL E ;В результате этой трехходовой
RLA ;манипуляции стрший бит E стал
RR A ;равен старшему биту A (копи-
;рование произошло через флаг
;переноса С.
LD B,03 ;Организуем счетчик для провер-
;ки трех последующих битов.
LOOP_1 XOR E ;Эта операция сбросит старший
;бит аккумулятора, если он
;равен (а сначала так оно и
;есть) старшему биту в E.
RLA ;Очередной байт в аккумуляторе
;становится старшим, а бывший
;отправляется в флаг C
JR C, EXIT ;Флаг C мог включиться только
;если один из трех битов, сле-
;дующих за старшим, не равен
;ему. Значит, это не символ
;блочной графики и следует
;переход на EXIT.
DJNZ LOOP_1 ;Повторение для трех битов.
CP A ;Включение флага Z как сигнал
;о том, что поиск был резуль-
;тативным.
JR EXIT_1 ;Подготовка к возврату.
EXIT OR FF ;Выключение флага Z как сигнал
;о том, что поиск был ;тативным.
;безрезультатным.
EXIT_1 POP BC ;Восстановление счетчика линий
;в регистре B.
RET ;Возврат.