Всё о печати символов...
(C)Cooper/RSM/P7S
──────────────────────────────────────────
Сегодня редкая программа, особенно сис-
темнная, обходится без этого - без печати
символов.
На Спектруме существует 4-ре стандарта
шрифтов. Они разнятся между собой шириной
одного символа. Форматы эти 8х8, 6х8, 5х8,
4х8 точек. Следовательно, первым форматом
мы можем поместить в одну строку на экране
32 символа, вторым - 42 символа, третьим -
51 символ, а четвёртым - 64 символа. Мат-
рица 5х8 является наименее распространён-
ной среди всех других.
Что же представляет из себя блок шрифта?
Самый обыкновенный шрифт состоит из 256
символов, а точнее - ввиде их образов.
Один символ, вне зависимости от размера
матрицы, задается 8-ю байтами. Например,
вот так хранится в символьном наборе (да-
лее - фонт) буква "А":
adr: bin:
#C000 00000000
#C001 00111100
#C002 01000010
#C003 01000010
#C004 01111110
#C005 01000010
#C006 01000010
#C007 00000000
Не трудно подсчитать, что весь фонт за-
нимает в памяти 2048 байт.
Наряду с обычными линейными шрифтами,
существуют т.н. экранные шрифты. Там тоже
каждый символ состоит из восьми байт, раз-
ница же заключается в порядке нахождения
данных восьми байт символа в блоке шриф-
тов (Ух, бля, и сказал ;). Например, всё
та же буква "А" будет храниться вот так:
adr: bin:
#C000 00000000
#C100 00111100
#C200 01000010
#C300 01000010
#C400 01111110
#C500 01000010
#C600 01000010
#C700 00000000
Обратите внимания на адреса залегания
байтов в памяти. Зачем это надо я расскажу
ниже, когда мы эти символы будем печатать
на экран.
Значит так, с форматом шрифтов разобра-
лись. Ещё раз повторяю: всё выше сказанное
относится к стандартным фонтам, т.к. суще-
ствуют нестандартные шрифты (6х7, пакован-
ные, шрифты для пропорциональной печати и
прочие). Теперь же, мы будем наши буковки
(или чё там у нас ;) выводить на экран.
Для этого нам понадобится процедура, ко-
торая б пересчитывала заданные нами коор-
динаты в соответствующий адрес в экранной
области. Тоесть, вот эта...
;=========координаты -> scr adr========
;in: D - Y координата, E - X координата
;out:DE - screen adress
LD A,D
AND 7
RRCA
RRCA
RRCA
OR E
LD E,A
LD A,D
AND 24
OR 64
LD D,A
Не будем вдаваться в подробности, мол,
что, где и как работает, т.к. это общепри-
нятая процедура и используется везде.
Теперь рассмотрим ньюансы при печати шри-
фтов с разными матрицами...
Печать символа 8х8
Символы с такой матрицей выводить проще
всего. Допустим, в регистре A у нас храни-
тся код символа, а в DE - его координаты
на экране. Тогда процедура, печатающая си-
мвол 8х8 точек, будет иметь вид:
;=====печать символа 8х8 с цветом=====
;in: DE - координаты, A - код символа
Pr_Sym LD L,A
LD H,0
ADD HL,HL ;умножаем код символа
ADD HL,HL ;на восемь...
ADD HL,HL
LD BC,font ;...и складываем с
ADD HL,BC ;адресом начала фонта
LD A,D ;знакомая нам уже
AND 7 ;процедурка ;)
RRCA
RRCA
RRCA
OR E
LD E,A
LD A,D
AND 24
OR 64
LD D,A
LD B,8
PRINT LD A,(HL) ;берем байт из фонта
LD (DE),A ;и кладем в экран
INC HL ;делаем приращение
INC D
DJNZ PRINT ;переходим по циклу
RET ;и возвращаемся...
Скорость работы данной процедуры доволь-
но-таки малая. Если тебя она, т. е. ско-
рость, не устраивает, то пора вернуться к
нашим экранным шрифтам.
Что же мы тогда имеем? Обратите внимание
на конструкцию:
LD L,A
LD H,0
ADD HL,HL
ADD HL,HL
ADD HL,HL
LD BC,FONT
ADD HL,BC
В данном фрагменте код символа умножает-
ся на восемь и складывается с адресом на-
шего шрифта. Это, вообще-то, занимет неко-
торую часть процессорного времени. Если уж
быть точным, то умножение тратит 33 такта
на символ. Вроде чепуха, но при печати,
например, целого экрана, этот мизер вылье-
тся в изрядное количество тактов.
Ввиду особенностей построения экранных
шрифтов, для вычисления адреса символа не
нужно делать умножение. Тоесть, теперь мы
экономим эти 33 такта, да и в цикле печати
на экран вместо INC HL,нужно делать INC H,
что экономит еще 16 тактов.
"А можно ли еще быстрее",- спросит на-
чинающий кодер. Конечно можно. Вот смотри-
те: первое, что можно сделать, особо не
напрягаясь, так это "развернуть цикл". То-
есть, теперь нам не приходится грузить в
регистр B "восемь" (экономим 7 тактов) и,
соответственно, не надо делать DJNZ, что
тоже экономит изрядное количество тактов.
Но учтите, что участок кода, перебрасываю-
ший байты шрифта в экран надо повторить 8
раз, а это приводит к увеличению размера
процедуры. За все надо платить :( Да, и не
забудьте, при переброске последнего байта
на экран, убрать лишние команды INC D и
INC H. Зачем тратить на них время?
Для особо ленивых привожу текст ускорен-
ной процедуры. А вот и он ;)
;=====печать символа 8х8 (fast) =====
;in: DE - координаты, A - код символа
Pr_Sym LD L,A ;в HL - код симво-
LD H,0 ;ла, который мы
LD BC,font ;складываем с
ADD HL,BC ;адресом начала фонта
LD A,D ;знакомая нам уже
AND 7 ;процедурка ;)
RRCA
RRCA
RRCA
OR E
LD E,A
LD A,D
AND 24
OR 64
LD D,A
DUP 7 ;директивы ALASM!!!
LD A,(HL) ;берем байт из фонта
LD (DE),A ;и кладем в экран
INC H ;приращение fnt adr
INC D ;приращение scr adr
EDUP ;и так 8 раз
LD A,(HL)
LD (DE),A
RET ;возвращаемся...
Печать символов 4х8
Для печати символов данного размера удо-
бнее всего использовать экранный шрифт,
где каждый символ продублирован в левой и
правой частях. Что-то типа этого:
adr: bin:
#C000 00000000
#C100 01000100
#C200 10101010
#C300 10101010
#C400 11101110
#C500 10101010
#C600 10101010
#C700 00000000
Зачем это надо - увидите далее, по ходу
статьи.
Чем же таким кардинальным отличается пе-
чать символов 4х8 от печати символов 8х8?
Одно из различий заключается в том, что
теперь по X-координате мы можем напечатать
целых 64 символа, а следовательно её, т.е.
координату, можно задавать от 0 до 63.
Далее, нам нужно перевести данные коор-
динаты в приемлемый для нас вид, ведь на
экране всего 32 знакоместа. Вобщем, не бу-
ду здесь страдать херней и этим же заби-
вать текст. Короче, Склифосовский!!!
Берем координату X, сдвигаем ее командой
RR E (обычно Х-овая координата "сидит"
именно здесь). Тогда мы получим привычные
для 32-символьной печати координаты, а
флаг переноса будет указывать какую из по-
ловинок знакоместа печатать.
Привожу для примера простенькую, но в то
же время, вполне работоспособную процеду-
ру, печатающую символами данного размера.
;===========================
;Драйвер печати символов 4х8
;print A at (D,E)
Print4 LD L,A
LD H,0
LD BC,Font4
ADD HL,BC
RR E
JP C,RIGHT
CALL Posit
LD B,8
LEFT LD A,(HL) ;печать символа в левой части
AND %11110000
LD C,A
LD A,(DE)
OR C
LD (DE),A
INC D
INC H
DJNZ LEFT
RET
RIGHT CALL Posit ;печать символа в правой части
LD B,8
RIGHT1 LD A,(HL)
AND %00001111
LD C,A
LD A,(DE)
OR C
LD (DE),A
INC H
INC D
DJNZ RIGHT1
RET
Как видите - ничего сложного!
Печать символов 6х8
А здесь прийдется повозиться, т.к. это
наиболее сложная для печати символьная ма-
трица.
Сразу предупреждаю, что здесь будет опи-
сан лишь принцип печати таким шрифтом, не
претендующий на что-то особенное, ввиду не
самой быстрой скорости печати. Для того,
чтобы научиться быстро печатать такими
шрифтами советую вам обратиться к другим
журналам и газетам, где подробно освещены
данные вопросы.
Приведу сразу исходник, а потом прокоме-
нтируем всё это.
;===========================
;Драйвер печати символов 6х8
;print A at (D,E)
Print68 EX DE,HL
PUSH HL
PUSH DE
PUSH BC
EX DE,HL ;ищем адрес символа
LD L,A
LD H,0
LD BC,Font6
ADD HL,BC
EX DE,HL ;DE-char image
GetMasks LD A,L
LD B,A
SRL A
SRL A
LD C,A
ADC A,C
ADD A,C
LD L,A
;процедура coords->scr adres=
LD A,H
AND #07
RRCA
RRCA
RRCA
ADD A,L
LD L,A
LD A,H
AND #18
OR #40
LD H,A ;HL-screen addr
PUSH HL ;в зависимости от X координаты
LD A,B ;берём нужную маску для печати
AND #03
ADD A,A
LD L,A
LD H,#00
LD BC,Masks
ADD HL,BC
LD C,(HL)
INC HL
LD B,(HL) ;BC-mask
POP HL
EXX
LD B,8 ;BC=mask
PrtA1 EXX
LD A,(DE)
BIT 7,B
JR NZ,PrtA2
RRCA
RRCA
BIT 0,C
JR NZ,PrtA2
RRCA
RRCA
BIT 3,B
JR NZ,PrtA2
RRCA
RRCA
PrtA2 EXX
LD C,A
EXX
BIT 0,C
JR NZ,PrtA3
LD A,B
CPL
AND (HL)
LD (HL),A
EXX
LD A,C
EXX
AND B
OR (HL)
LD (HL),A
BIT 7,B
JR NZ,PrtA4
PrtA3 INC L
LD A,C
CPL
AND (HL)
LD (HL),A
EXX
LD A,C
EXX
AND C
OR (HL)
LD (HL),A
DEC L
PrtA4 INC H
INC D
EXX
DJNZ PrtA1
POP BC
POP DE
POP HL
RET
Masks DEFW #FC00 ;маска печати для символов 6х8
DEFW #03F0
DEFW #0FC0
DEFW #003F
При печати такими шрифтами нам уже при-
ходится пользоваться такими вещами, как
маска (что это такое, я думаю, объяснять
не надо, т.к. обычно все программеры начи-
нают свой путь с печати спрайтов. А там и
до маски недалеко ;) Вообщем, данная про-
цедура печатает сразу по два знакоместа.
Это нужно по той причине, что ширина сим-
вола ну никак не кратна знакоместу и полу-
чается, что некоторые символы необходимо
печатать на стыке двух знакомест. Интерес
же заключается в том, что 4-ре символа по
ширине занимают ровно три знакоместа. Поэ-
тому, как не трудно догадаться, масок все-
го четыре. Ведь после каждого четвёртого
символа мы начинаем печать с начала знако-
места и т.д.
При старте, в зависимости от координаты
X, выбирается необходимая маска и ложится
на экран, только потом поверх накладывает-
ся символ. Думаю, что принцип понятен ;)
Можно печатать стринги (сообщения) пор-
циями, по 4-ре символа в каждой, т.е. кра-
тно знакоместам. Тем самым повышается ско-
рость печати. Но надо использовать уже
другую процедуру ;)
Печать символов в цвете
Для этого нам надо после вычисления ад-
реса в экране использовать данную процеду-
рку, которая пересчитывает адрес в экран-
ной области в соответствующий адрес в об-
ласти аттрибутов.
;=========scr adr -> attr adr========
;in: DE - screen adress
;out:DE - attr adress
LD A,D
RRCA
RRCA
RRCA
AND 3
OR #58
LD D,A
Для символов 8х8 и 4х8 я думаю всё и так
понятно, а вот для печати 6х8 надо помоз-
говать с проверкой на то, сколько знакоме-
ст надо окрашивать - одно или два. Но это
я оставлю вам на самостоятельную проработ-
ку. Не всё же жевать и в рот ложить ;) На-
до и самим немного думать. А то будем мы
здесь все с атрофированными как у ПЦ'шных
геймеров мозгами ;)
Печать стрингов
Даю напоследок простенькую процедурку
для печати сообщений. Да, токен установки
координат может быть любым - это уже ваше
дело. Можно добавить сюда и токен установ-
ки цвета: 16, код цвета. А можно как и в
ACEdit'е ;)
;==================================
; -=Процедура печати стрингов=-
;вх: DE - координаты
; HL - адрес строки текста
;поддерживаются токены:
;0 - конец текста
;17,X,Y - установка координат печати
;==================================
Pr_Str LD A,(HL) ;взять код символа
AND A ;проверить на ноль
RET Z ;совпало - выйти
CP 17 ;проверка на токен
JP NZ,Pr_Cont
INC HL ;установка новых координат
LD E,(HL)
INC HL
LD D,(HL)
INC HL
JP Pr_Str
Pr_Cont PUSH HL
PUSH DE
CALL Print8 ;нужная Вам процедура печати
POP DE
POP HL
INC E
INC HL
JP Pr_Str
──────────────────────────────────────────