Координаты и адреса точек
Подпрограмма Caladdr
В примере 7.3 приведен текст подпрограммы, выполняющей вычисления по этой формуле. Перед ее вызовом в регистре сх указывается номер столбца (координата х), а в регистре dx – номер строки (координата у). Вычисленный адрес помещается в регистры dx:ax, т. е. в ах находится значение окна, а в dx – адрес (смещение) в этом окне.
Пример 7.3. Универсальная подпрограмма вычисления видеоадреса.
Daladdr: mov ax, bperline ax = размер строки в байтах mul dx dx:ax = Y*bperline push dx сохраняем старшую часть результата xchg ax, ex обмен содержимого регистров mul bytppnt ах = X*bytppnt, dx = 0 add ax, ex вычисляем младшую часть адреса mov dx, ax и сохраняем ее в регистре dx pop ax ах = старшая часть Y*bperline adc ax, 00 учитываем возможность переноса mul byte ptr GrUnit ах = al * GrUnit add ax, Base win!! если используется базовое окно ret выход из подпрограммы
Текст примера 7.3 не нуждается в подробных пояснениях, обращаем ваше внимание только на следующие особенности. Содержимое регистра dx (старшую часть произведения y*bperiine) надо сохранить в стеке потому, что оно будет испорчено при втором умножении. После второго умножения и вычисления младшей части адреса старшая часть выталкивается из стека в регистр а х. К ней прибавляется единица переноса, которая могла возникнуть, если при выполнении команды add ax, сх произошло переполнение и был установлен С-разряд регистра флагов (признак Carry). Команды пересылки и выталкивания из стека не изменяют состояние С-разряда. Поэтому если он был установлен, то adc ax, 00 прибавит единицу к содержимому регистра ах.
Можно изменить текст примера 7.3 так, чтобы вычисленный адрес возвращался в регистре di, значение окна присваивалось переменной cur_win и выполнялась установка окна (call setwin). В результате получится вариант подпрограммы Caiiwin, описанной в примере 3.4, применимый в любых видеорежимах VESA.
Другой вариант Caladdr
В примере 7.4 показан вариант подпрограммы caiaddr, в котором вместо умножения x*bytppnt содержимое регистра сх сдвигается на к разрядов влево. В зависимости от видеорежима, в команде сдвига букву к надо заменить цифрами 1 или 2, т. е. эта подпрограмма не универсальна.
Пример 7.4. Пересчет координат в адрес с использованием сдвига.
Caladdr: mov ax, bperline ax = размер строки в байтах raul dx dx:ax = Y*bperline shl ex, k k=l для Hi-Color; k=2 для True Color add ax, ex вычисляем младшую часть адреса adc dx, 00 учитываем возможность переноса xchg ax, dx обмен содержимого регистров mul byte ptr GrUnit ax = al * GrUnit add ax, Base_win!! если используется базовое окно ret выход из подпрограммы
В примере 7.4 сдвигается не результат умножения, а только значение координаты х (содержимое регистра сх). Это возможно потому, что значение координаты Y (содержимое регистра dx) умножается не на Horsize, а на bperline = horsize*bytppnt.
Особенность операций сдвигов заключается в том, что величина сдвига может либо находиться в регистре el (младший байт регистра сх), либо указываться непосредственно в команде, других вариантов нет. Поэтому для автоматического выбора величины сдвига в примере 7.4 вместо двух подряд расположенных команд shl ex, k и add ax, сх надо записать следующие:
mov bx, wrdppnt; bx = величина сдвига xchg bx, ex; обмен содержимого регистров shl bx, cl; сдвиг значения координаты X add ax, bx; вычисляем младшую часть адреса
Напомним, что если видеокарта в режиме True color поддерживает трехбайтовый код точки, то заменять умножение сдвигами не целесообразно.
Подведем итог.
Первая из двух описанных подпрограмм универсальная, а вторая специализированная. Вопрос о том, какая из них лучше, вообще говоря, не корректен. Корректен другой вопрос – в каких случаях нужны универсальные подпрограммы, а в каких специализированные. Первые целесообразно составлять при разработке библиотечных модулей, особенно для языков высокого уровня. А если вы разрабатываете задачу, в которой большинство подпрограмм специализировано, то целесообразность включения в ее текст одной или нескольких универсальных подпрограмм весьма проблематична.