int21h

Dosud jsem sem psal zásadně svoje vlastní poznatky a všechno jsem měl osobně odzkoušeno. Tentokrát ale předkládám cizí text, který jsem pouze přeložil (z ruštiny).
Tento článek je na ruských webech celkem často zmiňován a zatím jsem neviděl jeho anglickou, natož pak českou verzi. Takže přece jen tu jakýsi můj přínos je...
Původní autor: "Victor B. Wagner"


Víte, že...?
Několik (10h) rad programátorům v Turbo pascalu


01)Víte, že procedura Rename v Turbo pascalu dovoluje přesouvat soubory mezi adresáři? Pravda - pouze v rámci jednoho disku.

02)Víte, že navzdory tomu, co se píše v nápovědě, se dá v TP programu současně otevřít více než 16 souborů?
Ve skutečnosti je to omezeno hodnotou FILES v souboru CONFIG.SYS
Musíte předtím ale zavolat tuto proceduru:
 Procedure SetMaxFiles(Count:Word);assembler;
 asm
   Mov AH,67H
   MOV BX,Count
   Int 21H
 end;
Poté zavolejte třeba SetMaxFiles(40) a můžete naráz pracovat se 40 soubory. Tedy včetně pěti standardních systémových.

03)Víte, že kvůli tomu, abyste mohli zavolat z vašeho programu jiný program, není vůbec třeba nastavovat direktivu $M a omezovat paměť vaší aplikace už v momentu překladu. V jednotce Memory z balíku Turbo vision je procedura SetMemTop, která dovoluje dát dceřinému procesu veškerou volnou dynamickou paměť.
 SetMemTop(HeapPtr);{dáme paměť}
 Exec(GetEnv('COMSPEC'),'');{v tomto případě volám příkazový interpret DOSu}
 SetMemTop(HeapEnd);{vezmeme si paměť zpět}

04)Víte, že (i když podle mě to neví ani Peter Norton) když z vašeho programu spouštíte příkazový interpret, tak klidně můžete nechat v systémových proměnných (v environmentu) dostatek volného místa na práci složitých BAT souborů. Řešení leží hned na povrchu. Ať proměnná EnvSize:Integer obsahuje velikost hlavního environmentu (jak se k němu dostanete, je zase jiná kapitola. Můžete využít jednotku TPEnv z archívu BONUS v balíku TurboProfessional), anebo můžete zadat trochu větší hodnotu.
Dále napíšeme tyto řádky:
Str(EnvSize,tmpString);
Exec(GetEnv('COMSPEC'),'/E:'+tmpString);
Nechápu, proč tohle nedělá Norton nebo alespoň Volkov.

05)Víte, že podle adresy objektu (typové konstanty nebo procedury) můžete velice jednoduše získat jeho offset v EXE souboru:
 Var Self:File;
     HdrSize:Word;
 Const SomeThing:record
                  ....
                 end = ( .... ) ;
 begin
  Assign(Self,ParamStr(0));{otevřeme sama sebe}
  Reset(Self,1);
  Seek(Self,8);{posuneme se na položku hlavičky EXE souboru, která ukazuje velikost hlavičky}
              
  BlockRead(Self,HdrSize,2);  {přečteme ji}
  Offset:=Ofs(SomeThing)+
             LongInt((Seg(SomeThing)-PrefixSeg-$10+HdrSize))*16;
{A tohle je vzorec pro výpočet offsetu. Teď je možné původně pevně danou hodnotu konstanty změnit a uložit a při dalším spuštění
programu to bude z jeho hlediska stejné, jako kdyby tuto hodnotu konstanta získala už při kompilaci.} 
  Seek(Self,Offset);
  BlockWrite(Self,Settings,Size);
  Close(Self);

06)Víte, proč v předchozím příkladu bylo ve vzorci použito přetypování? to už není záležitost DOSu, to je čistě věc turbo pascalu. Navzdory tvrzení manuálu se celočíselné výpočty typů Integer a Word provádí bez vnitřního převedení na typ Longint.
Proto, pokud bychom toto přetypování neudělali sami, tak bychom riskovali, že jakmile by velikost našeho souboru přesáhla 64KB, tak bychom buďto dostali chybovou hlášku Range check error nebo při kompilaci s R- bychom dostali nesprávný výsledek. Byl by to zbatek po dělení dělitelem 65536.
Tato skutečnost (není to bug, ale prostředek pro zvýšení efektivity - bohužel špatně dokumentovaný) je velmi často zdrojem těžko odhalitelných chyb.

07)Víte, že adresa runtimové chyby se vypočítá ze vzorce:
  Ptr(Seg(ErrorPoint)-PrefixSeg-$10 , Ofs(ErrorPoint))

08)Víte, že v DOSu jsou funkce převedení řetězce mezi velkými a malými písmeny?
Jestli je navíc v souboru CONFIG.SYS řádka COUNTRY=007, tak správně pracují s ruskými znaky.
(a pokud je COUNTRY=042, tak s českými v kódování Latin 2 - pozn. Laaca)

Například:
Function Upcase(C:Char):Char;assembler;
asm
mov AX,6520H
mov DL,C
int 21H
mov AL,DL
end;

Navíc existuje funkce na převedení celého řetězce (String nebo PChar) na velká písmena:
Dělají to podfunkce? 21h a 22h té samé funcke 65h.
Ukážu vám variantu pro String. Pro PChar ji napište sami, je jednodušší.
Procedure UpString(var S:String);assembler;
asm
 Push DS
 LDS BX,S
 XOR CX,CX
 MOV CL,[BX];Délka řetězce do CX
 INC BX
 MOV DX,BX ; DS:DX- ukazatel na první znak
 MOV AX,6521H
 INT 21H
 POP DS
end;
Pro podfunkci 22h se ukazatel na ASCIIZ řetězec také předává v DS:DX

09)Víte, jak donutit váš program při vzniku chyby na disku nebo v tiskárně vydávat standardní DOSovské "Abort, Retry, Ignore" místo pascalovského hlášení Runtime Error ...?
(Toto se natýká programů využívajících Turbo Vision, Turbo Professional nebo něco podobného)
Je to triviální:
SetIntVec($24,SaveInt24);
To donutí váš program používat standardní DOSovskou správu chyb místo pascalovské.

0A)Víte, jak v grafickém režimu napsat pomocí Writeln text na barevném pozadí?

Za prvé - proč?
a) protože s rozlišením více než 640x200 Writeln používá 14ti a 16ti bitové fonty, které jsou hezčí než osmibitový, který používá OutText.
b) protože je pohodlnější psát takto čísla
?) protože to, co platí pro Writeln, platí i pro Readln, jenž nemá grafický analog

Za druhé - jak?
Předpokládejme, že používáte jednotku Crt, jinak o barvách nemůže být řeči.
Na začátek programu určitě dejte "DirectVideo:=False", jinak vůbec žádný výstup neuvidíte. Takto výstupní rutiny poběží přes přerušení INT10h.
V grafickém režimu parametr atribut, který v textovém režimu funguje jako barva textu a pozadí, se v grafickém režimu chápe jako barva textu. Ale pokud je nejvyšší bit roven 1 (Color or $80), tak se barva každého bodu "vyXORuje" s tím, co tam bylo předtím.
Takže to uděláme takhle:

{ TxColor a BkColor obsahují příslušnou barvu textu a barvu barvu pozadí a řetězec S to, co chceme napsat.}
TextAttr:=BkColor;
X:=WhereX;
Y:=WhereY;
For i:=1 to Length (S) do
Write(#219);{Chr(219)='?'}
{To se dá udělat jedním voláním funkce BIOSu, protože jeden z jejích parametrů je počet opakování. Ale nebudeme to optimalizovat na úkor srozumitelnosti}
GotoXY(X,Y);
TextAttr:=$80 or (txColor xor BkColor);
Writeln(S);
V případě Readln je to trochu složitější, protože, sice můžete předtím vyčistit zadávací pole a potom nastavit TextAttr jak potřebujete a zavolar Readln, jenže když si to zkusíte, tak zjistíte, že vzniká problém s klávesou BackSpace. Nejlepší je pohrabat se ve zdrojovém textu unity Crt a dopsat příslušné věci přímo do ovladače textového souboru.
Autorovi se to podařilo a jeho jednotku VCrt můžete najít na moskevských BBS nebo na mailserv@sl.semsk.su

0B)Víte, jak zjistit, jestli váš program píše na obrazovku nebo jestli je někam přesměrovaný?
Takto:
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:}
Funkci volejte buďto Redirected(Input) nebo Redirected(Output) a dostanete True, když je vstup (nebo výstup) není ze zařízení CON. Tuto funkci nelze použít, jestliže pro soubor, s kterým pracuje, byla volána procedura AssignCrt nebo AssignDevice (pozor - inicializační kód jednotky Crt první z těchto procedur volá, a to i pro Input, tak i pro Output)

0C)Víte, jak v jednom programu sloučit použití jednotky Crt a standardního výstupu?
Hlavně ne tak, jak to dělá Figurnov ve své knize "IBM PC pro uživatele" (program CRCList)
Je to daleko jednodušší - deklarujte textový soubor, třeba F, a napište:
Assign(F,'');
Rewrite(f);
Potom všechno, co se píše do tohoto souboru, jde na standardní výstup DOSu.
Je žádoucí použít funkci Redirected z předchozí rady a přidat těchto pět řádek:
  if not Redirected(F) then
   begin
     AssignCRT(F);
     Rewrite(F);
   end;
Potom výstup na obrazovku půjde přes Crt, ale přesto ho bude možno přesměrovat. Pozor, funkci Redirected volejte pro OTEVŘENÝ soubor standardního vstupu/výstupu.

0D)Víte, jak nejsnáze v pascalu jak pípnout?
Write(^G);

0E)Víte, že z otevřené souborové proměnné můžete snadno dostat jméno souboru? Mám pocit, že víte, vždyť tohle dělá zabudovaný debugger Turbo pascalu. Nuže?
FileName:=StrPas(PChar(@TextRec(F).Name))
(využivá se funkce StrPas z jenotky Strings v TP 7.0. Kdo pracuje v 6.0 nebo starším, může ji zaměnit za Asciiz2Str z TurboProfessional. Když soubor není textový, tak místo TextRec napište FileRec (oboje je definováno v jednotce DOS), abyste se vyhnuli chybě Invalid type cast.

0F)Víte proč se Turbo Professional ve veri 5.11 nebo také starší verze Object Professionalu nepřeloží pod TP/BP 7.0?
Kvůli tomu, že v době jejich vydání ještě TP 7.0 neexistoval a proto si Turbo Professional myslí, že struktura dynamické paměti je ve všech verzích pascalu kromě 6.0 stejná jako v 5.0. (Pro 4 a 5.5 to doopravdy je)
Přidejte do souboru TPDEFINE.INC následující tři řádky:
{$IFDEF Ver70}
  {$DEFINE Heap6}
{$ENDIF}

10)Víte, jak schovat textový kurzor, jestli používáte jednotku Crt?
Je to užitečné např. pro tvorbu různých menu a podobně. Pokud nemáte možnost obrátit se přímo na BIOS k funkcím změny rozměrů kurzoru (neznáte potřebné funkce nebo nechcete používat vestavěný assembler, atd.), tak můžete zahnat kurzor na 26. řádku obrazovky, kterám jak známo, není vidět. To je obvyklý postup při programování v assembleru. Jenže jednotka Crt má hloupý zvyk nedovolit změnit pozici kurzoru mimo hranice okna. Defaultně je okno
Window(1,1,80,25);
Je potřeba to oklamat. Na začátku programu zvětšete proměnnou WindMax na 256 (číslo řádky se uchovává ve vyšším bajtu). Potom už po volání
GotoXY(1,26) zmizí kurzor z obrazovky.
2006-11-30 | Laaca
Datum: 9.3.2011 19:06
Od: Jzak
Titulek: Rename
U toho přesouvání , jak to mám zapsat.
Datum: 5.1.2015 21:52
Od: Mircosoft
Titulek: Rename
Assign(soubor,'c:\puvodni\cesta\soubor.bla');
Rename(soubor,'c:\nova\cesta\soubor.bla');
Reklamy: