From: "Victor B. Wagner" <vitus@agropc.msk.su>
Subj: А знаете ли вы что... (ver 2).10H советов программисту на TP
Organization: Agroecological problems Center
Около месяца назад я уже посылал первую версию этого текста в
телеконференцию. Тогда советов было 8. Сейчас их число выросло
вдвое. Но поскольку первые советы прошли в сезон летних отпусков,
я на всякий случай повторяю и их.
Приглашаю всех кто совершает маленькие открытия в процессе работы
с Turbo Pascal следовать моему примеру и писать о них не дожидаясь,
пока кто-нибудь отчаянно завопит: "HE-E-ELP!!!" Вдруг кому приго-
дится.
А знаете ли вы?
Несколько (10h) советов программисту на Turbo Pascal
>$0001)
А знаете ли вы , что процедура Rename в Turbo Pascal поз-
воляет перемещать файлы из одного каталога в другой (правда в
пределах одного диска)?
>$0002)
А знаете ли вы, что вопреки тому, что утверждается в Help,
из программы на TP можно открыть более 16 файлов (в пределе -
столько, сколько написано в команде FILES CONFIG.SYS). Вот так:
Procedure SetMaxFiles(Count:Word);assembler;
asm
Mov AH,67H
MOV BX,Count
Int 21H
end;
После этого вызываете SetMaxFiles(40) и 40 файлов (включая,
правда 5 стандартных) в вашем распоряжении.
>$0003)
А знаете ли вы, что для того, чтобы вызвать из вашей прог-
раммы другую, совершенно не обязательно задавать директиву $M и
ограничивать память вашей программы еще в момент компиляции.
Имеющаяся в модуле Memory Turbo Vision процедура SetMemTop поз-
воляет отдать дочернему процессу всю свободную динамическую па-
мять. Вот так
SetMemTop(HeapPtr);{Отдаем память}
Exec(GetEnv('COMSPEC'),'');{В данном случае вызываем DOSShell}
SetMemTop(HeapEnd);{Забираем ее обратно}
>$0004)
А знаете ли вы (по-моему, этого не знает даже Питер Нор-
тон), что запуская из вашей программы DOSShell вы вполне можете
оставить в Environment достаточно свободного места для работы
сложных BAT-файлов. Решение лежит на поверхности: Пусть пере-
менная EnvSize:Integer содержит размер корневого Environment
(откуда вы его возьмете - ваше дело. Можно воспользоваться мо-
дулем TPEnv из архива BONUS в TurboProfessional, можно просто
задать константу побольше. Далее делаем вот так
Str(EnvSize,tmpString);
Exec(GetEnv('COMSPEC'),'/E:'+tmpString);
Почему этого не сделал если не Нортон, то хотя бы Волков,
я не знаю.
>$0005)
А знаете ли вы, что по адресу объекта (типизированной кон-
станты или процедуры) в программе на TP можно очень просто по-
лучить ее смещение в EXE-файле Вот так:
Var Self:File;
HdrSize:Word;
Const SomeThing:record
....
end = ( .... ) ;
begin
Assign(Self,PAramStr(0));{Откроем собственный файл}
Reset(Self,1);
Seek(Self,8);{считаем размер его заголовка
(слово по смещению 8)}
BlockRead(Self,HdrSize,2);
Offset:=Ofs(SomeThing)+
LongInt((Seg(SomeThing)-PrefixSeg-$10+HdrSize))*16;
{А вот формула для вычисления смещения. Теперь можно запи-
сывать измененное значение константы SomeThing, и при следующем
запуске программы у нее возникнет такое впечатление, что так и
было задумано при компиляции}
Seek(Self,Offset);
BlockWrite(Self,Settings,Size);
Close(Self);
>$0006)
А знаете ли вы почему в предыдущем примере было использо-
вано преобразование типов в формуле? Это уже не DOS, это чистый
Turbo Pascal. Вопреки утверждениям документации вычисления в
целых числах типа Integer и Word делаются без приведения к типу
Longint. Поэтому не сделав этого приведения сами, мы рисковали
бы как только размер нашего файла превысит 64 К, либо получить
Range Check Error, либо, при компиляции с R- получить не то
значение, какое надо, а остаток от его деления на 65536. Данная
вещь (это не баг, а средство повышения эффективности, к сожале-
нию плохо документированное) очень часто служит источником
трудно вылавливаемых ошибок.
>$0007)
А вы знаете, что адрес, который выдает Turbo Pascal при
ошибке времени выполнения, вычисляется по формуле
Ptr(Seg(ErrorPoint)-PrefixSeg-$10 , Ofs(ErrorPoint))
>$0008)
А вы знаете что в DOS есть функции преобразования строки в
верхний и нижний регистры, и если в CONFIG.SYS стоит
COUNTRY=007, они корректно работают с русскими буквами.
Например:
Function Upcase(C:Char):Char;assembler;
asm
mov AX,6520H
mov DL,C
int 21H
mov AL,DL
end;
А можно за один вызов перевести в верхний регистр целую строку
String или PChar. Это подфункции 21H и 22H той же функции 65H
Вариант для String приведу, а для PChar пишите сами - он проще.
Procedure UpString(var S:String);assembler;
asm
Push DS
LDS BX,S
XOR CX,CX
MOV CL,[BX];Длина строки в CX
INC BX
MOV DX,BX ; DS:DX- указатель на первый символ
MOV AX,6521H
INT 21H
POP DS
end;
Для подфункции 22H указатель на ASCIIZ строку передается в тех же DS:DX
>$0009)
Знаете ли вы как заставить вашу программу при возникновении
ошибки на диске или принтере выдавать стандартное досовское
"Abort,Retry,Ignore", а не вылетать по Runtime Error ...?
(Это не касается программ использующих Turbo Vision, Turbo
Professional или что-то подобное)
Тривиально:
SetIntVec($24,SaveInt24)
Заставляет вашу программу пользоваться стандартным DOS-овским
обработчиком ошибок вместо стандартного паскалевского.
>$000A)
А знаете ли вы как в графическом режиме вывести с помощью
Writeln текст на цветном фоне?
(цветным считается фон ненулевого цвета. Чему вы там назначили
нулевой цвет - дело ваше)
Во-первых - зачем?
а) потому что в режимах разрешением более 640х200 Writeln
использует 14 и 16 битные шрифты, которые более красивы чем
8-битный, используемый по умолчанию OutText'ом.
б) потому что выводить числа Writeln'ом удобнее
в) Потому, что сказанное про Writeln относится и к Readln'у,
а ему аналога в графике нет.
Во-вторых - как?
Предположим, что вы используете модуль CRT, ибо о каком же
цвете может идти речь без него. Естественно в начале программы
вы поставили DirectVideo:=False, иначе вы вообще никакого
вывода не увидите и вывод идет через 10-е прерывание.
В графическом режиме параметр атрибут, который в текстовом
режиме работает как цвет текста и фона трактуется как цвет
текста. Но если старший бит равен 1 (Color Or $80), то цвет
каждой точки XOR-ится с тем что было на этом месте. Поэтому
делаем так
{ Пусть переменные TxColor и BkColor содержат цвет текста и
цвет фона соответственно, а строка S - то, что надо вывести}
TextAttr:=BkColor;
X:=WhereX;
Y:=WhereY;
For i:=1 to Length (S) do
Write(#219);{Chr(219)='ђ'}
{Это можно сделать за один вызов функции BIOS, так как один из
ее параметров - число повторений, но не будем оптимизировать в
ущерб понятности}
GotoXY(X,Y);
TextAttr:=$80 or (txColor xor BkColor);
Writeln(S);
В случае Readln несколько сложнее, так как можно заранее
очистить поле ввода, а затем присвоит TextAttr что положено и
вызвать Readln, но поэкспериментируйте после этого с клавишей
BackSpace. Лучше уж влезть в исходный текст модуля Crt и
вписать соответствующие вещи прямо в драйвер текстового файла.
Автор это как-то сделал и модуль VCrt можно найти на московских
BBS или на mailserv@sl.semsk.su
>$000B)
А знаете ли вы как проверить, пишет ли ваша программа на
экран или ее вывод был куда-то переназначен?
Вот так:
Function Redirected(var F:Text):Boolean;inline
($5F { POP DI }
/$07 { POP ES }
/$26/$8B/$1D { MOV BX,ES:[DI]}
/$B8/$00/$44 { MOV AX,4400 }
/$CD/$21 { INT 21 }
/$F7/$C2/$80/$00 { TEST DX,0080 }
/$74/$0A { JZ @@1 }
/$F7/$C2/$03/$00 { TEST DX,0003 }
/$74/$04 { JZ @@1 }
/$B0/$00 { MOV AL,00 }
/$EB/$02 { JMP 011C }
/$B0/$01); {@@1:MOV AL,01 }
{@@2:}
Вызывается соответственно Redirected(Input) или
Redirected(Output) и выдает True, если ввод или вывод программы
идет не с устройства CON. Нельзя пользоваться этой функцей если
Для файла, с которым она работает, была выполнена процедура
AssignCrt или AssignDevice (внимание код инициализации модуля
CRT первую из двух вышеупомянутых процедуры вызывает и для
Input и для Output)
>$000C)
А знаете ли вы как совместить в одной программе
использование модуля CRT и стандартного вывода?
Только не так, как это делает Фигурнов в своей книге "IBM PC
для пользователя" (программа CRCList)
Все гораздо проще - заводите текстовый файл, например F и
делаете ему
Assign(F,'');
Rewrite(f);
После этого все что пишется в этот файл, идет на стандатный
вывод DOS. Желательно правда воспользоваться функцией
Redirected из предыдущего совета и добавить следующие пять
строки:
if not Redirected(F) then
begin
AssignCRT(F);
Rewrite(F);
end;
Тогда вывод на экран будет идти через CRT, тем не менее его
можно будет переназначить. Внимание - функция Redirected
вызывается для ОТКРЫТОГО файла стандартного ввода-вывода.
>$000D)
А знаете ли вы самый простой способ заставить программу на
TP издать писк?
Write(^G);
>$000E) А вы знаете, что имея открытую файловую переменную можно
легко получить имя файла? (Подозреваю, что знаете - ведь это
делает встроенный отладчик Turbo Pascal, но как?)
FileName:=StrPas(PChar(@TextRec(F).Name))
(используется функция StrPAs из модуля Strings в TP 7.0. кто
работает в 6.0 и меньше, может заменить ее на Asciiz2Str из
TurboProfessional. Если же файл не текстовый, то во избежание
ошибки Invalid type cast используйте FileRec вместо TextRec (и
то и другое описано в модуле DOS))
>$000F)
А знаете ли вы почему Turbo Professiona версии 5.11 или
около того и Object Professional ранних версий не компилируется
под седьмым паскалем?
Дело в том что в момент их выпуска паскаля 7 еще не было и
поэтому Turbo Professional думает, что у всех версий кроме 6
структура динамической памяти такая же, как и в 5 (для 4 и 5.5
это действительно так)
Добавьте в файл TPDEFINE.INC следующие три строки
{$IFDEF Ver70}
{$DEFINE Heap6}
{$ENDIF}
Если же вы имеете версию 5.05 или более раннюю, которая и 6-го
то паскаля не знает, то поищите себе что-нибудь поновее.
Vitus Wagner
Origin:SoftWeyr
>$0010)
А знаете ли вы как спрятать текстовый курсор, если вы пользуетесь
модулем CRT для создания меню и т.д. Если у вас нет возможности
напрямую обратиться к функции изменения размеров курсора BIOS
(вы не знаете ее интерфейса, вы не хотите пользоваться встроенным
ассемблером и т.д.), то можно загнать курсор в 26 строку экрана,
которой, как известно на экране не видно. Это стандартный прием при
работе на ассемблере. Но модуль CRT обладает вредной привычкой не
позволять позиционировать курсор за пределы окна. (по умолчанию окно -
Window(1,1,80,25);
Нужно его обмануть - в начале программы увеличьте значение переменной
WindMax на 256 ( номер строки хранится в старшем байте). После этого
GotoXY(1,26) уберет курсор с экрана.
|