Главная страничкаИсходники программСтатьи и описанияПроекты и разработкаПолезные ссылкиЗадай вопрос, получи ответВыскажи свое мнениеПишите письма

Хитрости программирования

Введение

Данная статья посвящена маленьким, но полезным хитростям и тонкостям программирования, про которые, судя по всему, программисты должны знать по умолчанию, и видимо по этому ни кто не говорит.


Хитрость первая, расчет координат точки в SVGA режиме

Хороший стандарт VESA, цены ему нету, но есть одно маленькое неудобство, видео память делится на части (банки) по $FFFF (64Kb) каждая, и перед тем как поставить точку нам нужно рассчитать ее положение в видео памяти и указать какой байт в банке она занимает. Для этого нужно проделать некоторую операцию, а то есть координату Y * (Ширину экрана) и прибавить X, далее младшее слово результата будет смещение в банке, а старшее слово будет номер банка в котором должна находится наша точка. При этом результат должен быть 32 битным числом (в Pascal - LongInt)

На практике это смотрится примерно так:

Procedure Pix(X,Y:LongInt;Col:Byte);
Var Addr:LongInt;
Begin

If (Word(X)<ScreenXres) And (Word(Y)<ScreenYres) Then Begin

Addr:=Y*ScreenXres+X;
SetBank(Addr shr 16);
Mem[SegA000:Word(Addr)]:=Col;

End;

End;

Все выше сказанное конечно хорошо, но уверяю вас, работает медленно, попробуем оптимизировать все это, переписав на Assembler. Оптимизировать будем следующим образом, LongInt это хорошо, но медленнее чем Integer, если не писать под 386 ассемблер, так как Borland не порадовала нас встроенным 386 ассемблером, будем обходится тем что есть, в последствии это окажется немного быстрее и короче.

Оптимизированный на Assembler'е вариант:

Procedure Pix(X,Y:Integer;Col:Longint);Assembler;
Asm

mov ax,screenxres {заносим в AX ширину экрана}
mov cx,y {CX = Y координата}
mul cx {Умножаем на CX, при этом после такого умножение результат окажется в регистровой паре AX:DX, в AX будет младшее слово а в DX старшее, и это хорошо}

add ax,x {прибавляем к регистровой паре AX:DX X}
adc dx,0

{В AX:DX у нас полный адрес, при этом DX содержит номер банка а в AX смещение в банке}

push ax {Положим пока AX в стек на хранение и установим нужный банк вообще лучше сохранить номер установленного банка в какойто переменной и если он уже установлен, то зачем его устанавливать еще один раз}

cmp dx,curbank
jz @bankend

{а вот теперь преимущество над 32 битным режимом, в функции установки банка в VESA номер банка задается регистром DX а он у нас уже установлен (судя по всему на это и рассчитывали создатели VESA, только вот вопрос, че они об этом не кому не сказали?), а вот 32 битные команды в данном случае оказались бы не эффективными, так как после вычислений результат у нас был бы в регистре eax и его пришлось бы двигать и копировать в DX, а это лишние тики}

mov curbank,dx {Сохранили новый банк}
mov ax,$4F05
int $10
@bankend:

{Взяли из стека в регистр DI сохраненное выше смещение}
pop di

mov es,SegA000 {ES - адрес сегмента $A000, при этом использование SegA000 правильно работает
в защищенном режиме}

mov al,col {Заносим в AL цвет}
mov es:[di],al {Ну и ставим точку}

End;

P.S. и еще, в примере на Паскале я использовал проверку на выход за экран следующего вида:

If (Word(X)<ScreenXres) And (Word(Y)<ScreenYres) Then

а это тоже самое что и

If (X>=0) and (Y>=0) and (X<ScreenXres) and (Y<ScreenYres) Then

но вместо 4 проверок у нас две проверки. Так как в числе типа Integer 8 бит является указателем знака, а в числе типа Word это продолжение значения, то при отрицательных значениях X и Y указанных как Word мы получаем положительное значение большее ширины экрана, а координата точки больше экрана мы не ставим, опа как.

Для тех кто еще не понял, просто попробуйте следующею строчку Writeln(Word(-1));
и на экране вместо -1 появится 65535


Rambler's Top100