╔══════════════════════════════════════════════════════════════╗
║ Кодинг. ║
╙──────────────────────────────────────────────────────────────╜
(C) EVP-SOFT
Всем привет. После долгого перерыва снова вышла рубрика "Ко-
динг.". На этот раз я расскажу о выводе на экран 64 символов в
строке.
Все ассемблерные тексты в формате ALASM'а. И если какой-ни-
будь там GENS не переваривает команду вроде LD A,LX, DUP или
INCBIN, то не надо звонить мне с вопросами типа 'чем эту команду
заменить?', а просто надо ставить 512 кб и переходить на ALASM.
Для начала подготовим фонт. Для повышения скорости вывода
фонт надо из обычного формата (когда 8 байт каждого символа ле-
жат друг за другом) конвертировать в "треть экрана", т.е. чтобы
между байтами одного знака было смещение #100. Тогда адрес знака
будет вычислятся так:
LD H,0
LD L,код символа
LD BC,FONT
ADD HL,BC
в HL теперь первый байт, для следующего просто делаем INC H
А если не сконвертировать фонт, то тогда код знака сначала
надо умножить на восемь - а это лишние 33 такта:
LD H,0
LD L,код символа
LD BC,FONT
ADD HL,HL
ADD HL,HL
ADD HL,HL
ADD HL,BC
в HL теперь первый байт, для следующего делаем INC HL, но если
фонт расположить по адресу, кратному 8, то можно INC L
Ну а конвертируется фонт такой вот процедурой:
ORG #6400,0
LD HL,#8000
LD DE,#4000
LD BC,256
M1 PUSH HL
PUSH DE
PUSH BC
LD B,8
M2 LD A,(HL)
LD (DE),A
INC L
INC D
DJNZ M2
POP BC
POP DE
POP HL
DEC BC
INC E
DUP 8
INC HL
EDUP
LD A,C
OR B
JR NZ,M1
RET
ORG #8000,0
INCBIN "FontбЧ.C" ;подгрузка обычного фонта
Ассемблируем, потом вызываем STS. Там делаем CALL #6400, за-
тем сохраняем полученный фонт командой SAVE "64$0.C" #4000,#800.
Есть несколько вариантов печати. Можно хранить каждый символ
в старшем полубайте, а потом, если вывод нечётный, то сдвигать
его в помощью RRCA. Или можно в младшем полубайте хранить копию
старшего, а перед выводом отбрасывать ненужную половину по мас-
ке. Но самый быстрый способ вывода - это иметь в памяти два фон-
та, один в старших полубайтах, а младшие пустые, второй соот-
ветственно наоборот.
Однако в теле программы, для лучшей упаковки, надо хранить
только один фонт, а второй создавать перед запуском:
FNT_EXT LD HL,FONTO ;адрес фонта
LD DE,FONT1 ;адрес копии
LD BC,#800 ;счётчик
FNT1 LD A,(HL) ;берём байт
RLCA ;сдвигаем старший полубайт в младший
RLCA ;
RLCA ;
RLCA ;
LD (DE),A ;кладём результат
INC HL ;адреса следуюших
INC DE ;байт
DEC BC ;уменшаем счётчик
LD A,C ;если в BC ещё
OR B ;не ноль то
JR NZ,FNT1 ;повтор цикла
RET ;иначе возврат
FONTO INCBIN "64$0.C" ;фонт имеющийся
FONT1 DEFS #800 ;а тут будет второй фонт.
Самый быстрый способ печати - это печатать сразу по два симво-
ла. У этого способа есть, конечно, недостаток - иногда надо на-
печатать всего один символ. Но в большинстве случаев надо напе-
чать сразу целую строку, и тогда это получается намного быстрее.
Эта процедура получает в HL начало строки, и печатает её, пока
не встретит код CR (#0D), если строка короткая, то добивает её
пробелами. При выходе в HL следующий после CR адрес. Итак:
PRINT EX DE,HL ; сохраним адрес текста
LD A,32 ;установим счётчик пустых знакомест
LD (PRT_X),A ;
Теперь вычислим координаты на экране. В COORD_Y заносим верти-
кальную позицию, а в COORD_X - горизонтальную. Но на практике
обычно горизонтальная позиция равна 0, и тогда блок обработки
COORD_X можно исключить. Младший бит COORD_X должен быть равен
0, т.е. X-адрес только нечётный:
LD A,0
COORD_Y EQU $-1
LD B,A
AND #07
RRCA
RRCA
RRCA
; LD C,0
;COORD_X EQU $-1
; SRL C
; OR C
LD L,A
LD A,B
AND #18
OR #40
LD H,A
LD (PRT_WR),HL ;сохраним адрес знакоместа
Берём два знака для печати:
EX DE,HL ; возвращаем в HL адрес текста
PRTS LD A,(HL) ; берём знак
INC HL ; это на будущее
CP #A
JP Z,PRTS ; код LF игнорируем
PRT1 CP #D
JP Z,PRT18 ; нашли код CR - конец строки
LD E,A ; сохраним первый символ в E
PRT7 LD A,(HL) ; берём второй знак
INC HL
CP #A
JP Z,PRT7 ; игнорируем LF
CP #D
JP NZ,PRTЧ ; если это не CR, то начинаем печать
PRT13 DEC HL ; а если CR - то оставим его на первый цикл
LD A,#20 ; ну а пока выведем пробел
PRTЧ PUSH HL ; сохраним адрес текста
LD L,A ;и начнём вычислять адреса символов в фонте
LD H,0
LD BC,FONT1
ADD HL,BC ; вычислим второй(т.е. правый) символ
EX DE,HL ; в DE будет адрес второго символа
LD H,0 ; а в HL скоро появится первый
LD BC,FONTO
ADD HL,BC ; вычислим первый(т.е. левый) символ
LD BC,0 ; в BC заранее подставили адрес печати
PRT_WR EQU $-2 ; посредством метки PRT_WR
LD A,C ; это на будущее, следующий адрес
INC A ; печати, т.к. автоперевода строк тут
LD (PRT_WR),A ; нет, то нам нужен только один байт
DUP 8 ; сделаем раскладку
LD A,(DE) ;берём второй символ
OR (HL) ;на него накладываем первый
LD (BC),A ;и всё это кладём уже на экран
INC H ;находим адреса следующих
INC D ;байт этих символов
INC B ;переходим на следующую линию экрана
EDUP ; конец раскладки
ORG $-3 ; затрём 3 посл. команды посл. раскладки
; т.к. они в ней ненужны.
PRT15 LD A,0 ; счётчик пустых знакомест,
PRT_X EQU $-1 ; вначале тут будет подставлено 32
DEC A ;
LD (PRT_X),A ;
POP HL ;восстановим адрес текста
JP PRTS ;и начнём вывод следующих двух символов
А это процедура добивает пробелы в конце строки, очень полез-
но, если снизу уже что-то было. Если это не надо, то просто уби-
раем всё что связано со счётчиком пустых знакомест и меткой
PRT_X, и где около метки PRT1 стоит JP Z,PRT18 вместо этого JP
ставим RET Z. А иначе набиваем дальше:
PRT18 LD A,(PRT_X) ;проверим наличие
AND A ;пустых знакомест
RET Z ;если нет, то возврат
PUSH HL ;иначе сохраним адрес текста
LD E,A ;в E будет кол-во знакомест для чистки
XOR A
LD HL,(PRT_WR) ;адрес на экране
LD B,8 ;высота знакоместа конечно 8 линий
PRT10 PUSH HL
PUSH BC
LD B,E
PRT9 LD (HL),A
INC L
DJNZ PRT9 ;цикл очистки одного знакоместа 8x8(!)
POP BC
POP HL
INC H
DJNZ PRT10 ;цикл очистки оставшихся знакомест
POP HL ;восстановим адрес текста
RET ;и возврат
Вот в качестве примера процедура 'LINE', которая выводит на
экране сверху и снизу две полоски из символов "*":
LINE XOR A
LD (Y),A
; LD (X),A ;;;; смотри COODR_X
LD HL,LIN1 ;
CALL PRINT ;
LD A,23 ;
LD (Y),A ;
; XOR A ;;;;
; LD (X),A ;;;;
LD HL,LIN1
CALL PRINT
RET
LIN1 DEFS 64,"*"
DEFB #D
Ну и раз уж начали тему вывода 64 символов, то сейчас я приве-
ду пример и под IS-DOS. А именно новый драйвер 64 символов, в
отличии от стандартного "tyбЧ.typ" этот будет печатать всю таб-
лицу из 256 символов.
Начнём с теории. Драйвер, как и резидент, должен иметь в конце
слово #FFFF, после которого лежит таблица адресов, которые надо
настроить при инсталяции (это делает set.com). Иначе, если в
драйвере есть команды JP nn, LD a,(nn), LD HL,nn то его нельзя
будет перемещать в памяти и система просто зависнет. Причём ад-
реса в таблице на единицу меньше реальных, это сделано для упро-
шения настройки команд тима JP nn, LD A,(nn) и аналогичных.
В начале драйвера должна быть следующая таблица:
DEFW адрес инсталляции, если 0 то не вызывается
DEFW адрес ПП вывода символа на экран, входные данные: A - код
символа, DE' - адрес на экране, а левый или правый надо
считать самим. Выход: если был правый символ, то E'+1
DEFW адрес ПП управления инверсией: A:Z - нету, A:NZ - есть
DEFW адрес ПП установки координат, вход: BC - лог.позиция,
выход: DE' - адрес на экране, левый или правый символ -
запомнить в драйвере.
DEFB длина курсора в пикселях, это в данном случае 4.
DEFW физ. координаты
DEFW лог. координаты
DEFB тип: 0-экран/1-принтер
DEFW адрес ПП обработки ошибок.
Итак, начнём создавать драйвер.
;
; Драйвер 64 символов в строке
; Под IS-DOS
; (C) EVP-SOFT at 1998.04.12
;
ORG 50000
DEFW 0 ; эта стандартная таблица,
M5 EQU $-1 ; описание смотри выше.
DEFW TTYOYT ;
M6 EQU $-1 ;
DEFW PRCPL ;
M7 EQU $-1 ;
DEFW PRADD ;
DEFB 4,0,0,0,0,0 ;
M13 EQU $-1 ;
DEFW M12 ;
Для ускорения печати фонт будет в "экранном формате" (см. в
предыдущей статье). Но только надо будет старший полубайт про-
дублировать в младший, т.к. в IS-DOS'е памяти нехватит, чтобы
иметь два раздельных фонта.
FONT INCBIN "64$0&1.C"
Управление инверсией:
PRCPL OR A
JR Z,M4
LD A,#2F ;#2F - это код команды CPL
M4 LD (M3),A
M12 XOR A ;а сюда при случае будем передавать обработку
RET ;ошибок. Т.е. просто их игнорировать
А тут вычисляем координаты печати.
PRADD SRL C
SBC A,A ; определяем
XOR #F0 ; левый или правый символ
M9 LD (M1),A ; в знакоместе 8x8
LD A,B
AND #07
RRCA
RRCA
RRCA
OR C
LD E,A
LD A,B
AND #18
OR #40
LD D,A
EXX ; теперь в DE' будет адрес на экране
RET ; и можно возвращаться
Теперь дошла очередь и до ПП вывода на экран:
TTYOYT EXX
LD H,0
LD L,A
M10 LD BC,FONT
ADD HL,BC
EX DE,HL
LD BC,#0800 ; в B счётчик цикла
M1 EQU $-2 ; в C маска выбора "левый или правый"
M15 LD A,(DE) ;;
M3 NOP ;в случае инверсии сюда заносим #2F, т.е. CPL
AND C ;;
XOR (HL) ;;
AND C ;; ну тут всё как обычно.
XOR (HL) ;;
LD (HL),A ;;
INC D ;;
INC H ;;
DJNZ M15
LD A,H ; восстановим начальный адрес
SUB 8 ;
LD D,A ; и вернём его в D, потом это будет D'
LD A,C ; сменим маску с левого на правый или
CPL ; наоборот
M8 LD (M1),A ;
RLA ; если с правого на левый, то
LD A,0 ; надо перейти к следующему
ADC A,L ; знакоместу
LD E,A ; кладём результат в E
EXX ;теперь DE станет DE', а остальное неважно
RET
Ну и теперь таблица адресов, где была прямая адресация:
DEFW #FFFF ; признак таблицы
DEFW M4,M5,M6,M7,M8,M9,M10,M13 ;адреса-1, а проще
;говоря, адреса команд
Длина файла имеет большое значение, если в конце таблицы адре-
сов появятся лишние байты, даже нули, то при установке такого
драйвера что-нибудь заглючит.
... этот драйвер можно скачать на HARD BBS ...
Other articles: