Trochu zkušenější provedou nějaký test rychlosti procesoru a výsledné číslo použijí stejně jako v předcházejícím případě. Tento způsob snad má své místo jako doplňující postup v nějakých speciálnostech, ale jako hlavní řešení se také nehodí.
V PC je proto pro tyto účely tzv. časovač. Časovač generuje v pravidelných intervalech pulzy, kterých se může programátor "chytit". Standardně se pulz generuje jednou za 55ms, což je přibližně 18,2x za vteřinu. Tato frekvence se dá ale změnit a my si za moment ukážeme jak.
Nejjednodušší způsob, jak sledovat časovač, je využití proměnné BIOSu na adrese 0040:006C.
V pascalu(TP i FP) tedy takto:
CONST rychlost = 5; var d:longint; begin d:=MemL[Seg0040:$06c]; repeat if MemL[Seg0040:$06c]>d+RYCHLOST then begin writeln('vypršel'); timer_passed:=0; d:=MemL[Seg0040:$06c]; end; until keypressed; while keypressed do readkey; end.Konstantou rychlost můžu určovat po kolika pulzech budu reagovat. Pokud bude 0, tak budu reagovat na každý pulz.
Tento postup má ale dost velkou nevýhodu. Sami si totiž musíme hlídat, kolik pulzů už proběhlo a kolik zbývá a zkrátka si to musíme dost hlídat. Elegantní je tedy odchycení přerušení časovače. Od teďka budu psát k programátorům ve FP (princip je ale naprosto stejný). Pro Turbo pascal je totiž návodů spousta, pro FP ale moc ne...
Hardwarové zpracování přerušení časovače funguje tak, že po každém tiku se přes kanál IRQ 0 zavolá přerušení BIOSu INT08h, které ho zpracuje. Tím se myslí, že se postará o proměnnou na 0040:006C, hlídá motory disketových mechanik a snad ještě něco. Potom ale ještě zavolá přerušení INT1C, které je určeno pro uživatelské doplňující rutiny. Doporučuje se tedy, obsazovat právě tento vektor, a ne přímo INT08h.
Ve Freepascalu to uděláme takto:
{$ASMMODE INTEL} {$MODE FPC} {$Q-}{$R-}{$S-}{$D-} unit Timer; interface Procedure Zapni_Casovac; Procedure Vypni_Casovac; var pocet_tiku:byte; Nainstalovano:boolean; implementation uses go32; const int1c = $1c; var timerproc:pointer; oldint1c : tseginfo; newint1c : tseginfo; BackupDS : Word; external name '___v2prt0_ds_alias'; procedure int1c_handler; assembler;interrupt; asm cli push fs push es push ds push ax mov ax,cs:[BackupDS] mov ds,ax mov es,ax mov ax,dosmemselector mov fs,ax call TimerProc pop ax pop ds pop es pop fs sti end; Procedure HandlerDummy;begin end; Procedure MujCasovac; begin inc(pocet_tiku); OutPortB($20,$20); end; Procedure MujCasovacDummy;begin end; Procedure Zapni_Casovac; var i : Longint; counter:longint; begin timerproc:=@MujCasovac; newint1c.offset := @int1c_handler; newint1c.segment := get_cs; lock_data(timerproc, sizeof(timerproc)); lock_data(dosmemselector, sizeof(dosmemselector)); lock_data(pocet_tiku, sizeof(pocet_tiku)); lock_code(@MujCasovac,longint(@MujCasovacDummy) - longint(@MujCasovac)); lock_code(@int1c_handler,longint(@HandlerDummy)-longint(@int1c_handler)); get_pm_interrupt(int1c, oldint1c); set_pm_interrupt(int1c, newint1c); pocet_tiku:=0; Nainstalovano:=true; end; Procedure Vypni_Casovac; begin unlock_data(timerproc, sizeof(timerproc)); unlock_data(dosmemselector, sizeof(dosmemselector)); unlock_data(pocet_tiku, sizeof(pocet_tiku)); unlock_code(@MujCasovac,longint(@MujCasovacDummy) - longint(@MujCasovac)); unlock_code(@int1c_handler,longint(@HandlerDummy)-longint(@int1c_handler)); set_pm_interrupt(int1c, oldint1c); Nainstalovano:=false; end; begin Nainstalovano:=false; end.Nic hrozného ne? Jestli jste v minulém čísle četli mimořádně odborný, skvěle napsaný a hlavně MŮJ :-)) článek o chráněném režimu, tak vidíte, že je to druhý typ obsluhy přerušení - totální náhrada. Zajímavé je, že v tomhle případě (ale možná jenom na mém PC), se nic nestane, jestli vypustíte řádek
OutPortB($20,$20);Procedura nám samostatně zvyšuje čítač a hlavní program má o něco jednodušší úlohu. Ještě zdůrazněme, že ačkoliv tu totálně nahrazujeme přerušení INT1C, tak BIOS nadále přičítá do čítače na 0040:006C, protože INT1Ch je jen jakousi "odbočkou" INT08h.
Na začátku jsem se zmínil o tom, že je možné měnit frekvenci časovače. To je velice dobře, protože frekvence 18,2Hz je dost nízká. Například animace přehrávaná touto rychlostí není zcela plynulá. Časovač obsazuje porty 40h až 43h, které obsluhují 3 nezávislé kanály. Všechen dosavadní text je věnován kanálu 0, který je napojen právě na IRQ 0 - tedy na INT08h. Zbylé dva jsou občerstvovač paměti (aby se navymazala) a ovládání repráčku.
Změníme tedy nastavení kanálu nula: Do portu se to zadává v nějakých pochybných jednotkách, a proto musíme udělat přepočet:
Procedure NastavFrekvenci(f:longint); {F=pocet pulzu za sekundu} var i:longint; begin i:=$1234DD div f; OutPortB($43,$34); OutPortB($40,counter mod 256); OutPortB($40,counter div 256); end;
Na konci programu všechno vraťte do původního stavu:
Procedure VratFrekvenci; begin OutPortB($43,$34); OutPortB($40,0); OutPortB($40,0); end;Tyto rutiny je vhodné vložit do procedur
Zapni_Casovac a Vypni_Casovac z uvedeného příkladu. Abych řekl pravdu, tak ani nevím, do jakých hodnot je možné frekvenci časovače hnát. Pro tvorbu her ale extrémní hodnoty nejsou třeba :-)Ve hrách se také často kvůli plynulosti animace doporučuje čekat při vykreslování na vertikální návrat paprsku. Můžete se dokonce rozhodnout se na časovač vybodnout a synchronizovat se jenom takhle. Pokud si ale frekvenci nenastavíte sami, což často windows ani nedovolí, tak ale nevíte, jakou máte. V módu 13h to snad bývá 70Hz, ale ten asi používat nebudete. V lepších režimech je to jak u koho a jak kdy...
Já osobně jsem ale nikdy žádné trhání při ignorování obnovovací frekvence nepozoroval a ve svých programech dávám synchronizaci s paprskem volitelně - pokud si to uživatel vypne, tak se může běh programu zrychlit. Já ji mám vypnutou vždy.
by Laaca
2006-11-30 | Laaca
Diskuse
Mam takový blbý dotaz ... může se něco stát s PC nebo systémem, když ,,vynuluju" ten časovač?
MemL[Seg0040:$06c]:=0;
Já předpokládám že ne, ale chci mít jistotu :)
MemL[Seg0040:$06c]:=0;
Já předpokládám že ne, ale chci mít jistotu :)
Teoreticky ne.. Je možné, že to shodí Windows 98. Ale přímý dopad na data na disku by to mít nemělo..
> Nový ohlas