Быстрая процедура печати — Абзац


Бумажные газеты и журналыАбзац2001 №4 → Быстрая процедура печати

Быстрая процедура печати

Здравствуйте, уважаемые читатели. Вот и я написал СВОЙ абзац. Не знаю как вы, а я вот соскучился по статьям посвящённым не глубоким абстрактным размышлениям (хотя они нужны и полезны), а самым простым вещам - coding’у, графике, музыке, железу, demo и gamemaking’у и всему прочему, что для меня, например, и составляет предмет творчества на Speccy. Посему статья эта будет посвящена одной из таких вещей -программированию.

Хочу предложить вам несколько идей по поводу печати строк шестипиксельным font’ом. Я знаю, что вам такая печать жизненно необходима, хотя бы потому что сейчас каждый уважающий себя синклерист выпускает свой компьютерный журнал, газету, боевой листок и так далее (я и сам выпускаю, чего уж там... :-)). Кроме того, я надеюсь, что данная публикация подтолкнет других к написанию статей в «Абзац» на тему coding’а и т.п. (смотрим выше (даешь, понимаешь, гасилку как...).

Итак, быстрая печать. Сразу скажу, что речь далее будет вестись не об универсальных процедурах, а о печати именно строк (не отдельных символов), оптимизированной по скорости (в ущерб, кстати, памяти).

Для ускорения вывода создадим к основному шрифту ещё 3, символы в которых будут располагаться, соответственно, со смещением в 2, 4 и 6 пикселей от левого края. Чтобы при печати строки не тратить время на вычисление адреса нужного символа, лучше создавать font’ы таким образом, чтобы изображение первого символа лежало в байтах с адресами: font, font+#100, font+#200, ... , font+#700, изображение 2-го в байтах с адресами: font+#001, font+#101, font+#201, ... , font+#701, где font - адрес шрифта в памяти, кратный 256. Таким образом для вычисления адреса символа нужно будет занести в старший регистр старший байт адреса фонта, а в младший поместить код символа. Для пояснения всего вышесказанного приведу пример подпрограммы, формирующей соответствующие шрифты:


INS_FON LD DE,SH60         ; формируем шрифт
        CALL INSF_1        ; со смещением 0
        LD A,#0F,(IFF_1),A
        LD (IFF_1+1),A
        LD DE,SH63         ; -- / --
        CALL INSF_1        ; со смещением 2
        LD DE,SH61         ; -- / --
        CALL INSF_2        ; со смещением 6
        LD A,0,(IFF_2),A
        LD DE,SH62         ; -- / --
        CALL INSF_2        ; со смещением 4
        RET

INSF_2  LD IX,font
        LD E,#20           ; код 1го символа (пробел)
        LD C,224           ; считаем, что всего в
                           ; шрифте 224 символа
SF2_1   PUSH DE
        LD B,4
SF2_3   CALL SF2_2
        LD A,L,(DE),A:INC D; зачем сделано
        LD A,H,(DE),A:INC D; именно так
        CALL SF2_2         ; можно прочи-
        LD A,H,(DE),A:INC D; тать ниже
        LD A,L,(DE),A:INC D;
        DJNZ SF2_3
SF2_3B  POP DE
        INC E,IX
        DEC C:JR NZ,SF2_1
        RET
SF2_2   LD H,(IX+0),L,0:INC IX
.2      AND A:RL H,L
IFF_2   RET
.2      AND A:RL H,L
        RET

INSF_1  LD HL,font
        LD E,#20,C,224
SF1_1   PUSH DE
        LD B,8
SF1_2   LD A,(HL):INC HL
IFF_1   NOP:NOP
        LD (DE),A:INC D
        DJNZ SF1_2
        POP DE
        INC E,HL
        DEC C:JR NZ,SF1_1
        RET

Здесь font - адрес исходного обычного шрифта; SH60, SH61, SH62, SH63 - адреса формируемых шрифтов, должны быть кратны 256. В INSF_2 формируются шрифты по размеру в два раза больше, чем в INSF_1 из-за того, что при смещении изображения символа на 4 и 6 пикселей вправо оно выходит за границы одного байта. Причём, в INSF_2 шрифт формируется таким образом, что 2-я, 4-я, 6-я и 8-я линии изображения каждого символа хранятся в обратном порядке, т.е. сначала идёт правый байт, потом левый (чтобы понять, что я тут хотел сказать лучше посмотреть на саму эту процедуру и всё сразу станет ясно). В таком виде будет удобней выводить строку символов.

Теперь непосредственно о самой печати. Основная идея: при печати шестипиксельным шрифтом рассматриваются четвёрки символов, которые очень хорошо вписываются по ширине в три байта. Поэтому нужно выводить символы не отдельно друг от друга, а одновременно печатать на экран сразу всю четвёрку. В этом случае исчезает много ненужных команд, типа LD A,(DE), LD (DE),A, которые нужны при последовательном выводе (так, например, для вывода 2-го символа четвёрки нужно взять с экрана изображение уже напечатанного 1-го, совместить его со 2-м, положить обратно на экран. То же самое с 3-м и 4-м). Для вывода одновременно всей четвёрки нам нужно иметь следующие адреса:


адрес текста - допустим он в DE';
адрес на экране, куда выводится
строка - DE;
адрес 1-го символа - BC;
адрес 2-го символа - HL;
адрес 3-го символа - HL';
адрес 4-го символа - BC';


Сделаем  набросок процедуры вывода всей строки:


LIN_1   LD B,SH60[        ; заносим старшие
        LD H,SH61[        ; байты адресов в
        EXX
        LD H,SH62[        ; соответствующие
        LD B,SH63[        ; регистры
        LD A,(DE):INC DE  ; код 1го символа
                          ; четвёрки
        ...

; здесь следовало бы поставить проверку
; на различные управляющие коды,
; как то: конец строки, цвет и т.д.
; Думаю это каждый сможет сделать сам

        EXX:LD C,A:EXX    ; в BC уже есть
                          ; адрес
        LD A,(DE):INC DE  ; 2-ой символ
        ...
        EXX:LD L,A:EXX    ; 2-ой адрес
        LD A,(DE):INC DE  ; 3-й
        ...
        LD L,A            ; и так далее
        LD A,(DE):INC DE
        ...
        LD C,A

        EXX

; теперь непосредственно сам вывод

        LD A,(BC):INC B   ; выводим 1-ю
        OR (HL):INC H     ; (3-ю, 5-ю, 7-ю)
        LD (DE),A:INC E   ; строчку слева
        LD A,(HL):INC H   ; направо, т.е.
        EXX               ; как обычно
        OR (HL):INC H     ;
        EXX               ;
        LD (DE),A:INC E   ;
        EXX               ;
        LD A,(BC):INC B   ;
        OR (HL):INC H     ;
        EXX:LD (DE),A:
        INC D;

        EXX               ;
        LD A,(BC):INC B   ; выводим 2ую
        OR (HL):INC H     ; (4-ю, 6-ю, 8-ю)
        EXX               ; строку справа
        LD (DE),A:DEC E   ; налево - для
        EXX               ; этого в процедуре
        LD A,(HL):INC H   ; INSF_2 мы и сде-
        EXX               ; лали небольшой
        OR (HL):INC H     ; изврат в формате
        LD (DE),A:DEC E   ; шрифтов
        LD A,(BC):INC B   ;
        OR (HL):INC H     ;
        LD (DE),A:INC D   ;
        ...
; повторяем эти два блока ещё 3 раза
; для вывода всех восьми строк
        LD A,D:SUB 8:LD D,A
.3      INC E;
; переходим на три байта вправо и
; восстанавливаем значение регистра D
        ...
; здесь можно было бы поставить проверку
; на то, выходит ли строчка за экран и
; т.д.
        JP LIN_1

Про вывод цветов говорить, наверное, не стоит - ничего сложного в нём нет, к тому же особого влияния на скорость работы всей программы этот вывод не оказывает. В программе не показано ещё много мелочей - цель статьи изложить лишь идею, которую каждый может воплотить по-своему. Средняя скорость работы viewer’а, написанного с использованием данного алгоритма - 5-6 фреймов на обновление экрана размером 22*40 символов.


От редакции: В листинге присутствуют особенности ассемблера Storm. Например: .3 INC E, что при компиляции дает 3 команды INC E.