Программный знакогенератор
Подпрограмма знакогенератора
Текст подпрограммы знакогенератора приведен в примере 5.19. Перед обращением к ней код ASCII выводимого на экран символа помещается в регистр ai. В di записывается адрес видеопамяти для размещения кода точки левого верхнего угла изображения символа. Переменная cur_win содержит окно видеопамяти, которому принадлежит адрес, указанный в di. Предварительная установка окна не требуется.
Как обычно при работе с графикой, в регистре ез должен находиться сегмент видеобуфера (значение переменной vbuff). Остальные параметры задаются неявно, это переменные примера 5.18. После выполнения подпрограммы регистр di содержит адрес начала следующего символа, а переменная cur_win – окно, к которому относится этот адрес.
Пример 5.19. Подпрограмма рисования символов шириной в 8 точек.
outsgn: PushReg <ax,bx, ex, fs, si,di>; сохранение используемых регистров call Setwin установка исходного окна push Cur win сохранение номера исходного окна Ifs si, ftaddr; fs si = адрес таблицы символов mov ex, hsymb; ex = количество строк рисунка xor ah, ah очистка байта ah mul cl смещение рисунка в таблице (ax*cl) add si, ax полный адрес рисунка символа ; Построение изображения символа (внешний цикл) out ext mov bh, f s: [si]; bh = код текущей строки таблицы inc si адрес следующей строки таблицы mov Ы, 8 Oh константа выделения (и счетчик) ; Построение текущей строки рисунка (внутренний цикл) out int mov al, grndcol;! al = цвет точки окружающего фона test bh, Ы текущий бит установлен? jz @F › нет, на запись кода точки mov al, symbcol;! al = цвет точки контура символа @@: stosb!! запись кода в видеопамять or di, di достигнута граница сегмента? jne @F › нет call Nxtwin установка следующего окна @@: shr bl, 01 сдвиг константы выделения jne out int управление внутренним циклом add di, augment адрес следующей строки рисунка jne @F › адрес в пределах окна call Nxtwin установка следующего окна @@: loop out ext управление внешним циклом pop Cur win исходный номер окна pop di восстановление исходного адреса add di, 08!! адрес для следующего символа jne @F › адрес в пределах окна mov ax, GrUnit константа для коррекции окна add Cur win, ax вычисляем значение нового окна @@: PopReg <si, fs, cx,bx, ах>; восстанавливаем регистры ret конец подпрограммы
Выполнение примера 5.19 начинается с сохранения в стеке содержимого используемых регистров. После этого устанавливается окно видеопамяти, в которое будет выводиться символ, и значение cur_win сохраняется в стеке, т. к. оно может измениться в процессе рисования символа.
Команда ifs загружает младшее слово ftaddr в регистр si, а старшее – в сегментный регистр fs, в результате пара регистров fs:si содержит адрес таблицы символов в оперативной памяти. После этого в сх записывается количество строк в рисунке символа. Эта величина определяет количество повторов внешнего цикла, она же используется при вычислении адреса начала заготовки символа в таблице. При умножении кода символа ASCII на высоту рисунка (ci) в регистре ах получается смещение заготовки символа, оно прибавляется к адресу начала таблицы, в результате чего в регистре si получается адрес первого байта заготовки рисунка символа.
Основные действия выполняют два вложенных цикла. Внешний имеет имя out_ext и начинается с чтения в регистры кода очередного байта заготовки изображения символа. После чтения адрес, находящийся в регистре si, увеличивается на 1. В ьь записывается константа выделения разрядов кода (80h) и начинается выполнение внутреннего цикла.
Внутренний цикл имеет имя out_int. В нем, начиная со старшего, последовательно выделяются биты строки, хранящейся в регистре ы. В зависимости от состояния текущего бита в ai записывается значение переменной grndcol или symbcol, затем оно копируется в видеопамять командой stosb. Константа выделения смещается на разряд вправо и если она не равна нулю, то внутренний цикл повторяется.
После построения текущей строки продолжается выполнение внешнего цикла. При этом формируется адрес байта видеопамяти, соответствующий началу следующей строки, и команда loop повторяет выполнение внешнего цикла, пока не будут нарисованы все строки.
Как обычно, при любых изменениях адреса видеопамяти проверяется принадлежность нового значения текущему сегменту. Если оно выходит за пределы сегмента, то устанавливается следующее окно видеопамяти.
После выхода из внешнего цикла из стека восстанавливаются значение исходного окна и адрес видеопамяти, который увеличивается на ширину символа. Если при этом произойдет выход за границу сегмента, то увеличивается номер окна. Для этого к нему прибавляется значение переменной Grunit. Перед возвратом из подпрограммы восстанавливается сохраненное в стеке исходное содержимое регистров.
Отметим, что при однократном обращении к знакогенератору не обязательно вычислять позицию следующего символа. Однако при выводе связного текста такие вычисления необходимы, и удобнее их делать именно в знакогенераторе.
Описанная подпрограмма рассчитана на выполнение в режимах PPG. Для того чтобы при описании режимов direct color не возвращаться к программированию знакогенератора, покажем, что надо изменить для его использования в этих видеорежимах. В примерах 5.18 и 5.19 комментарий к изменяемым командам отмечен двумя восклицательными знаками.