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, Ы текущий бит установлен ?
Выполнение примера 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 комментарий к изменяемым командам отмечен двумя восклицательными знаками.