int21h

Ovládání joysticku v Pascalu

Při programování her či programů se jistě nevyhneme požadavkům na čtení vstupních zařízení. Turbo Pascal sice dovoluje používání jednoduchých funkcí READ a READLN, popř. ve spojení s jednotkou CRT také ReadKey a KeyPressed, ale vystávají zde otázky, proč linkovat celou jednotku kvůli pár funkcím, či, pokud nemáte patchovanou CRT pro vyšší počítače, zda bude fungovat na strojích s frekvencí vyšší než 200, resp. 233 MHz (známá Division By Zerro, Runtime Error 200). Proto bychom se mohli pokusit naprogramovat vlastní funkce.

Jistě je Vám divné, proč začínat takovými věcmi, když už je Pascal podporuje, proč třeba nezačít rovnou zvukovými kartami, sítěmi nebo komunikací přes sériový kabel (hra ve dvou přeci není vůbec k zahození). No, důvody jsou minimálně tři. Za prvé, o zvukových kartách budu ještě psát. Za druhé, o mezipočítačové komunikaci si můžete přečíst na mých stránkách (www.volny.cz/martinlux) v sekci Programování a Periférie (popř. i jinde, pokud se někdo rozhodne moje články "importovat"). A za třetí, jednotky dodávané k Pascalu (pokud už jsou) jsou pomalé. A co nejvíce naštve je, když je program zdržován čekáním na stisk klávesy nebo zbytečně čte myš, když se nic neděje!

Začneme v Turbo Pascalu 7, neboť ten se učí na školách a je obecně snažší pro něj něco napsat. Posléze přejdeme na Free Pascal, který je efektivnější. Budu se (zatím) věnovat všeobecně (i v dalších článcích) jen DOSové verzi, neboť pro Windows či Linux můžete využít speciálně napsaných jednotek (MOUSE pro myš, KEYBOARD pro klávesnici, WINGRAPH pro grafiku, či přímo DirectX jednotky pro všechny činnosti naráz). Navíc jsem se ještě tak daleko nedostal (dost možná, že až to pochopím, tak o tom něco napíši, nebo někdo jiný). Navíc pro Free Pascal existuje spousta příkladů (např. co se týče jednotky MOUSE) přímo v jeho dokumentaci (angličtina je samozřejmě nezbytná). V příkladech používám diakritku Windows, avšak pokud používáte DOS verzi FP nebo TP7, musíte mít v paměti ovladač pro Latin 2 či jinou češtinu a diakritiku správně přeformátovat. Pokud budete kompilovat pro Windows, bude diakritika správně (ale není zaručeno, že budou fungovat příklady, kde se přistupuje do DOSové (konvenční) paměti nebo k portům). Příklady pro DOS budou fungovat i pod Windows systémem, pokud dobře emuluje DOS. Také moje assemblerovské komentáře nejsou akceptovatelné ani v TP7 ani ve FP, takže je před kompilací buď smažte nebo nahraďte typem {} či (* a *)...


JOYSTICK:
---------

Začneme něčím "jednodušším" a to je čtení joysticku. Věřte nebo ne, i tak zastaralá věc jako je BIOS poskytuje funkce pro jeho čtení. Avšak ty jsou obvykle docela pomalé. Na druhou stranu celkem nevyžadují kalibraci a vrací celkem slušnou přesnost. Nevýhoda je jejich neustálé volání, což se samozřejmě odrazí na rychlosti. Přerušení, které poskytuje tyto služby, má číslo $15 (nebo-li 15h) a samotná služba má číslo $84. Je nutné si uvědomit dvě věci: GAME port umožňuje připojení až 2 joysticků po 2 tlačítkách nebo osách. V případě, že je tam jen jeden, může využívat až 4 osy a 4 tlačítka. Je nutné umět mezi tímto rozlišovat podle toho, co Vám zadá uživatel (detekce není možná). Za druhé, stav tlačítek se vrací inverzně, tj. 1 znamená puštěné, 0 stisknuté, a navíc jako 4 nejvyšší bity (MSb). Stav jednotlivých os je číslo WORD, přičemž pokud je joystick vycentrován, měla by hodnota dosahovat přibližně 32767. Problém je v tom, že odpory, které se používají v joysticku nejsou z nejkvalitnějších, nehledě na měřáky v portu, takže se páka jakoby klepe. Je proto vhodné provést kalibraci tím, že přečteme několik hodnot po sobě (poté, co vyzveme uživatele, aby na joystick nesahal) a tím zjistíme, kam až se páka vychyluje. Poté stačí brát, že je-li naměřená hodnota v daném intervalu, budeme brát joystick jakoby byl v klidové poloze (ve středu). Její použití je triviální:


var	Tlac : byte;
	X1,Y1,X2,Y2 : word;
asm  
	mov	ah,$84
	mov	dx,0
	int	15h
	not	al
	shr	al,4
	mov	tlac,al		; získáme stav tlačítek
	inc	dx
	int	15h		; získáme stav os
	mov	x1,ax
	mov	y1,bx
	mov	x2,cx		; pokud máme jen jeden joystick
	mov	y2,dx		; nemusíme číst všechny osy

end;  

Tento kód bude fungovat i ve Free Pascalu. My si však zvolíme mnohem rychlejší způsob, jak používat joystick, a to pomocí vlastních sil. Využijeme znalosti toho, že stav MIDI/GAME portu se vrací na portu $201. Pokud se podíváte do ATHelpu, zjisíte že horní 4 bity jsou vyhrazeny pro tlačítka a dolní 4 pro osy. To vyplývá z toho, že tlačítka jsou digitální a stačí jim tedy jen dva stavy. Ale jak číst osy, které jsou analogové, a přitom mají také jen 1 bit? Zde se využívá tzv. monostabilního klopného obvodu, který vydrží v nestabilním stavu tak dlouho, jak velký odpor má k sobě připojen a toho se využívá i u josticků. Nejprve zkusíme zjistit, zda počítač vůbec nějaký joystick má. To se provede následovně:


function Detekce : boolean; assembler;
asm  
	mov	dx,$201
	out	dx,al		; zapíšeme cokoliv, tím začne měření
	mov	cx,$ffff
	mov	bl,0
 @cykl:	nop
	loop	@cykl
	in	al,dx		; přečteme stav portu
	cmp	al,$ff
	je	@neni		; joystick nenalezen
	mov	bl,1
 @neni:	mov	al,bl  
end;  

Test vlastně probíhá tak, že na port joysticku zapíšeme libovolnou hodnotu (proto ani AL neplníme) a počkáme 65535 opakování. Pokud se do té doby nezmění stav portu na hodnotu rozdílnou od 255, očekáváme, že není joystick připojen. Samozřejmě to počítá s tím, že je vhodné, aby na něj uživatel nešahal, nebo alespoň nemačkal žádná tlačítka. Tuto legraci použijeme později ke čtení stavu os. Nyní ale zkusíme zjistit, jak jsou na tom tlačítka.


function Tlacitka : byte; assembler;
asm  
	mov	dx,$201
	out	dx,al
	in	al,dx
	shr	al,4
	not	al  
end;  

Jistě je Vám tento kód povědomý. Tímto kódem přečteme všechna 4 tlačítka, která stačí testovat pomocí OR instrukce, takže TLACITKA OR 1 = 1 je první tlačítko, TLACITKA OR 2 = 2 je druhé tlačítko (stisknuté), a tak dále. Se čtením pozice os je to velmi jednoduché. Zde si ale raději už zvolíme, která osa nás bude zajímat. Za typ dosadíme kód osy, kterou budeme chtít zjistit, tj. 1 pro X na Joysticku A, 2 pro Y na joysticku A, 4 pro Bx a 8 pro By. Je vhodné si vytvořit pojmenované konstanty, abychom se nespletli a nezadali něco jiného.


const	oAx = 1;
	oAy = 2;
	oBx = 4;
	oBy = 8;


function Osa(Typ : byte) : byte; assembler;
asm  
	mov	dx,$201
	out	dx,al
	mov	cx,$ffff	; čítač
 @cykl:	in	al,dx
	inc	cx
	test	al,typ		; 1 = stále se měří	
	jnz	@cykl
	mov	ax,cx		; pozice na dané ose

end;  

Protože tato funkce (je nutné vzít v potaz, že tato funkce předpokládá, že něco naměří, jinak skončí v nekonečné smyčce) defakto měří hodnotu všech os, ale předává jen jednu vybranou, je zbytečné provádět měření 2x až 4x po sobě pro všechny osy. Místo toho nám stačí napsat si funkci, která bude vracet tolik os, kolik budeme potřebovat. Protože budeme vracet 2-4 parametry, napíšeme danou funkci jako proceduru a budeme jej vracet přes VAR (u Free Pascalu bychom mohli použít i funkci, protože FP umožňuje vracet i složité typy, což TP7 neumí). Během této funkce zakážeme přerušení, protože jeho případný výskyt by změnil hodnotu naměřeného údaje (je možné jej samozřejmě zakázat i v předchozí funkci). Je také nutné si ovědomit, že nyní nepředáváme výsledek přes registr AL, ale přes ukazatele, které jsou uloženy v zásobníku (VAR), proto musíme také zjistit adresu daných proměnných. Tato funkce už také bere v potaz i to, že by nic nenaměřila.


procedure Osy(var _Ax,_Ay,_Bx,_By : word); assembler;
asm  
	cli
	mov	dx,$201
	out	dx,al
	mov	cx,$ffff	; -1
	mov	bl,0		; které osy už máme. Zatím žádné
 @1:	in	al,dx
	inc	cx
	cmp	cx,$ffff	; konec měření
	jne	@2		; ne
	mov	al,0
 @2:	or	al,bl		; abychom netestovali již změřené
	test	al,1
	jnz	@3
	or	bl,1		; osa Ax je již změřena
	les	di,_ax
	mov	es:[di],cx	; předáme její hodnotu
 @3:	test	al,2
	jnz	@4
	or	bl,2		; osa Ay
	les	di,_ay
	mov	es:[di],cx
 @4:	test	al,4
	jnz	@5
	or	bl,4		; osa Bx
	les	di,_bx
	mov	es:[di],cx
 @5:	test	al,8
	jnz	@6
	or	bl,8		; osa By
	les	di,_by
	mov	es:[di],cx
 @6:	cmp	bl,15		; jsou už všechny osy změřeny?
	jne	@1		; ne
	sti			; ano, končíme  

end;  

Pro rychlejší kód je možné také nahradit všechny MOV NECO,0 na XOR NECO,NECO (jen je nutné počítat s tím, že se budou měnit příznaky v registru FLAGS); také test CMP NECO,0 lze napsat jako OR NECO,NECO. Pokud budete chtí použít daný kód pod Free Pascalem, musíte konstrukce:


	les	di,NECO
	mov	es:[di],cx  

Nahradit tímto:

	mov     esi,NECO
        mov     [esi],cx  

Je to dáno strukturou programu a chráněným režimem (Flat model). Nezapomeňte vždy použít jednotku GO32 pomocí USES, jinak Vám to bude hlásit podobné věci:


Recompiling because of G:fpascal ingo32v2pasfilespokus.pas
pokus.pas(2,14) Error: Error while assembling exitcode 1
pokus.pas(2,14) Fatal: There were 1 errors compiling module, stopping
pokus.pas(2,14) Fatal: Compilation aborted

Zkušenější programátoři si ale všimnou jiné jedné vady. Tento program totiž sice funguje na všech počítačích, ale na každém procesoru dává jiné výsledky, protože stihne napočítat v CX jinou hodnotu (na některých dokonce ještě dříve, než stihne něco změřit). To by jistě nemuselo vadit, stačilo by jen při každém spuštění zkalibrovat joystick, ale jak uznáte, to je trochu otrava (hlavně pro uživatele). Je proto vhodnější použít harwarový čítač. Můžeme se buď pověsit na INT 1Ch, který se standardně volá 18.2x za vteřinu a zvyšovat si nějakou svou proměnnou, a nebo číst stav čítače přes tzv. oblast paměti v BIOS data. Zde se nachází Dword (Longint) na adrese 0:$46c, který obsahuje počet tiků hodin od startu počítače. A ten se aktualizuje také 18.2x za vteřinu. Zkusíme takto nahradit funkci pro měření jedné osy a navíc ji ošetříme proti tomu, že by nic nenaměřila:


const	Max = 18;
var	Pocitadlo : longint;
	PocStav : longint;
	Osa : longint;
	Citac : longint absolute 0:$46c;
begin
	PocStav := Citac;
	Port[$201] := 0;
	Osa := 32767;
	for Pocitadlo := 0 to Max-1 do
	begin
	 repeat until Citac <> PocStav;
	 PocStav := Citac;
	 if Port[$201] and 1 = 1 then
	 begin
	  Osa := (Pocitadlo shl 16) div (Max-1);
	  if Osa > 65535 then Osa := 65535;
	 {Osa := Osa and $ffff} 
	  Break;
	 end;
	end;
end.  

Tato funkce je už pro přehlednost psána raději v Pascalu, i když neřeší problém, pokud překročíte při hraní počet tiků 1^32-1). Nejprve si zjistíme počítační stav a pak začneme měřit. Pro případ, že bychom nic nezměřili, nastavíme si osu do středové polohy. V cyklu vždy nejprve počkáme 1 takt čítače a když proběhne, zjistíme, zda už neskončilo měření. Pokud ano, předáme hodnotu počitadla, které naměřilo hodnotu od 0 do 17. Abychom se drželi předchozích konvencí, normalizujeme hodnotu <0,17> na hodnotu <0,65535>. Pro urychlení by samozřejmě stačilo pracovat jen s intervalem 0 až 17. Ve Free Pascalu bude kód malinko jiný. Zvláště kvůli portům a přístupu do paměti. Navíc zde používáme interval 0-65536 neboť pak není nutné při jeho přetečení údaj normalizovat na WORD.


uses Go32;
const	Max = 18;
var	Pocitadlo : longint;
	PocStav : longint;
	Osa : dword;
begin
	PocStav := Mem[0:$46c];
        OutPortB($201,0);
	Osa := 32767;
	for Pocitadlo := 0 to Max-1 do
	begin
	 repeat until Mem[0:$46c] <> PocStav;
	 PocStav := Mem[0:$46c];
	 if InPortB($201) and 1 = 1 then
	 begin
	  Osa := (Pocitadlo shl 16) div (Max-1);
	  Break;
	 end;
	end;
end.  

Pokud bychom se nutně nemuseli držet intervalu 0-65536, můžme do OSA uložit pouze POCITADLO bez nějakých výpočtů, což nám čtení nepoměrně zrychlí. Celá věc má ale malou nevýhodu v tom, že měření joysticku probíhá po celkem velkých skocích. Přeci jenom můžeme naměřit teoreticky hodnotu 0, pak až 3600, 7201, atd. což není příliš přesné (pokud ovšem nemáte tzv. digitální joystick, což byly ty staré, které neměly plynulé potenciometry, ale krajní spínače, takže uměly jen levo,střed,pravo a nic mezi tím). Je to dáno tím, že časovač se inkrementuje 18.2x za vteřinu. Naštěstí máme prostředky, jak jej urychlit. Časovač se nastavuje na portu $43 a jeho hodnota se dá poté vyčíst na portu $40 (hodí se např. pro náhodná čísla). Časovač provádí "tiky" v rozmezí 838 ns až 54.925493 ms (což je právě 18.2x za vteřinu). To je provedeno nastavením čítače pomocí dělitele 1 až 65535 (lze použít i 65536, ale to je pro nás zbytečné). Časovače (tzv. porty PIT) jsou tři, většinou se však používá ten první. Celé se to nastavuje až příliš jednoduše. Standardně se uvádí vzorec "1193180 DIV Frekvence". Je ale možné k prvnímu číslu ještě 1 přidat.


procedure Casovac(PocetTiku : longint);
var Delitel : word;
begin
 Delitel := $1234DD div PocetTiku;
 Port[$43] := $34;
 Port[$40] := Lo(Delitel);
 Port[$40] := Hi(Delitel);
end;  

Pro Free Pascal by pak vypadala následovně:

procedure Casovac(PocetTiku : longint);
var Delitel : word;
begin
 Delitel := $1234DD div PocetTiku;
 OutPortB($43,$34);
 OutPortB($40,Lo(Delitel));
 OutPortB($40,Hi(Delitel));
end;  

Pokud Vám to nebude fungovat (např. budete mít stále jen rychlost 18.2 Hz), bude nutné obsadit přerušení INT $1C (namísto testování počtu tiků pomocí Mem) a při každém volání si přičíst 1 (pokud bude nastavena na TRUE nějaká proměnná např. MERENI), které poběží už správně (podle nastavení). Když budete tedy chtít měřit joystick ve 128 Hz intervalech (popř. 64 Hz, pokud by se Vám to zdálo moc), zavolejte tuto proceduru s parametrem 128, resp. 64. Dvojkové číslo jsem zvolil proto, jelikož pak je možné upravit předchozí kód na měření os tak, aby se namísto DIV MAX mohlo použít SHR 7, resp. SHR 6, což je výrazně rychlejší. Joystick lze nyní měřit po skocích 512, resp. 1024, což je už výrazně lepší. Problém nastane, pokud si nyní budete chtít přečíst systémové hodiny. Zjistíte, že se jaksi splašily. To je dáno tím, že se nyní nezvětšují 18.2 za vteřinu ale třeba 128x za vteřinu. Pro Vás z toho neplyne problém, horší to bude s aplikacemi, které budou hodiny číst, až Vy skončíte. Naštěstí tu je jednoduché řešení. Před koncem programu nastavte frekvenci zpět na 18.2 Hz (ideálně použijte dělitel 0). Ale toto řešení nestačí, protože hodiny jdou napřed. Naštěstí nás zachrání CMOS, který si udržuje tzv. reálné hodiny. Ty jdou vždy nezávisle na systémových (dokonce se z nich systémové při startu počítače "vytvoří"). Teď budeme muset opět využít porty, neboť jinak s k CMOSu nedostaneme. CMOS sídlí od portu $70, navíc zabírá jen několik portů a svou malou paměť zpřístupňuje přes tzv. registry (obdoba mixerů u zvukových karet). Práce s těmito registry je naštěstí lehká. Do portu $70 určíte číslo registru a z portu $71 následně přečtete jeho obsah. Protože paměť CMOS je trochu pomalejší (navíc organizovaná po bytech), je vhodné dát počítači chvíli čas na zpracování Vašeho požadavku (v čistém assembleru stačí instrukce JMP $+2). Legrace je o to větší, že veškeré údaje jsou uloženy jako BCD, a tedy je musíme ještě převádět na standardní čísla.


function b(v : byte) : byte;
begin
 b := 10 * (v shr 4) + (v and 15)
end;  

Tato funkce převede BCD údaj na normální číslo. BCD je číslo, které namísto 255 dokáže v jednom bytu uložit pouze 99, protože pro každou číslici jsou vyhrazeny 4 bity a tam se v desítkové soustavě více než 0-9 dostat nedá. Středník za příkazem uvádět nemusíme, neboť po něm hned následuje END. Tuto funkci využijeme při čtení paměti CMOS:


function r(ad : byte) : byte;
begin
 port[$70] := ad;
 r := b(port[$71])
end;


function r(ad : byte) : byte;
begin
 OutPortB($70,ad);
 r := b(InPortB($71))
end;  

A teď už nám jen zbývá napravit to, co jsme svým zásahem spáchali:

procedure Cas;
var s,m,h,d,m,y,rok : word;
begin
 s := r(0);		; vteřiny
 m := r(2);		; minuty
 h := r(4);		; hodiny
 d := r(7);		; den
 m := r(8);		; měsíc
 y := r(9);		; rok
 SetTime(0,0,0,0);	; nastavíme půlnoc
 if (y >= 80) and (y <= 99) then
  rok := y+1900 else	; určíme správné století
   rok := y+2000;
 SetDate(rok,m,d);	; nastavíme správný čas a datum
 SetTime(h,m,s,0);
end;  

Důvod, proč nejprve nastavujeme půlnoc je ten, aby se nám nestalo, že nám správný čas nebo datum změní její překročení. Pokud bychom totiž nejprve nastavili datum a poté čas, mohlo by se datum změnit na nový den v případě, že by systémové datum bylo těsně před půlnocí (a samozřejmě reálné ještě daleko před ní). Opačně by zase hrozilo, že bychom nastavili čas, který by teď byl těsně před půlnocí a než bychom nastavili datum (při těch výpočtech), tak bychom byli o den pozadu. Ale to je celkem nadnesená míra rizika, která hrozí i tehdy, je-li reálný čas, který jsme načetli, těsně před půlnocí. Milisekundy nenastavujeme, neboť 1 vteřina spoždění nás zase až tak nepálí vzhledem k tomu, že díky nepřesnému krystalu si PC udělá do týdne mnohem větší chybu i bez nás. Rok je v BIOSu uložen pouze na 1 byte, tedy musíme rozlišit, do kterého století spadá. Můžeme samozřejmě také využít služby CMOSu, který zná správné století:


procedure Cas;
var s,m,h,d,m,y,t : word;
begin
 s := r(0);		; vteřiny
 m := r(2);		; minuty
 h := r(4);		; hodiny
 d := r(7);		; den
 m := r(8);		; měsíc
 y := r(9);		; rok
 t := r($32);		; století
 SetDate(t*1000+rok,m,d);
 SetTime(h,m,s,0);
end;  

Je také doporučeno, pokud se zjistí, že je S po načtení 0, načíst všechno ještě jednou, protože mohlo dojít k překročení půlnoci. Když využijeme první variantu, dostaneme už finální řešení:


procedure Cas;
var s,m,h,d,m,y,rok : word;
begin
 repeat
  s := r(0);
  m := r(2);
  h := r(4);
  d := r(7);
  m := r(8);
  y := r(9);
 until s > 0;
 SetTime(0,0,0,0);
 if (y >= 80) and (y <= 99) then
  rok := y+1900 else
   rok := y+2000;
 SetDate(rok,m,d);
 SetTime(h,m,s,0);
end;  

Dobře, teď už umíme číst joystick, ale jak zjistit, kde jsou meze těch souřadnic, které jsme naměřili? K tomuto slouží tzv. auto-kalibrace. Tu je nutné na daném počítači použít pouze 1x a pak někam uložit. Bude platná, dokud se nevymění joystick. I přes svůj vznešený název se jedná o velice jednoduchou věc. Stačí jen, když uživatele požádáte o tři věci: nastavení joysticku nahoru doleva, nastavení na střed (puštění páky) a poté nastavení do polohy dole vpravo (nejjednoduší možnost je nechat zjistit jen NL a DP a střed si vydělit, nebo zjistit jen střed a vše ostatní brát jako L a P, resp. D a N, ale to je velice nepřesné). Mezi jednotlivými kroky může uživatel buď stisknout tlačítko joysticku, a nebo raději libovolnou klávesu, neboť to zaručí, že zvláště ve středové poloze nezpůsobí třas ruky naměření nějakých extrémních hodnot, zvláště, budeme-li v pozdějších metodách číst vícekrát. Vadí to i přesto, že se výzva objeví až po dokončení měření, protože vlastně ovlivní až to následující. Jen je nutné mít na paměti, že uživatel může tlačítko joysticku nebo klávesu držet o něco déle než je zdrávo a tedy ji můžeme přečíst několikrát. Jak se tomu vyvarovat? Poté, co zjistíme, že uživatel stiskl klávesu nebo tlačítko, které nás zájímá, počkáme ještě na dobu, kdy nebude stisknuto nic. My si při čtení přečteme souřadnice, které naměříme v daných stavech:


var A,B,C,D,E,F : longint;
begin
	while Keypressed then Readkey;
	Writeln('Nastavit nahoru doleva');
	Readkey;
	A := Osa(oAx);
	B := Osa(oAy);
	while Keypressed then Readkey;
	Writeln('Nastavit na střed');
	Readkey;
	C := Osa(oAx);
	D := Osa(oAy);
	while Keypressed then Readkey;
	Writeln('Nastavit dolů doprava');
	Readkey;
	E := Osa(oAx);
	F := Osa(oAy);
end.  

Nyní platí, že pokud je joystick v intervalu <A,C), je vlevo, pokud je (C,E>, je vpravo, jinak je na středu. Pro osu Y to platí odbobně, jen s parametry B,D a F. Asi jste si ale všimli, že spousta joysticků není zrovna super materiál, a i když na ně nesaháte, tak se jakoby chvějí a to i přesto, že jste je ve Windows nakalibrovali bez chvění. Pro řešení tohoto problému se zavádí tzv. Dead Zone, nebo-li mrtvá zóna. Zde se jedná o místo na ose, kde se pohyb joysticku prostě ignoruje. Ve spousta her se dá nastavit ručně, ale my si uděláme tzv. autodetekci. Stačí jen, když doplníme náš stávající kód, kdy v každém stavu přečteme souřadnice ne jednou, ale třeba 100x. Během čtení porovnáváme, zda je nová výchylka menší či větší a podle toho si ji uložíme jako mrtvou zónu. Proměnných budeteme mít tedy o 1 navíc pro každou osu.


const	Pocet = 100;
var	A,B,C1,C2,D1,D2,E,F : longint;
	Poc : word;
	X : longint;
begin
	while Keypressed then Readkey;
	Writeln('Nastavit nahoru doleva');
	Readkey;
	A := 0;
	B := 0;
	for Poc := 1 to Pocet do
	begin
	 X := Osa(oAx);
	 if X > A then A := X;
	 X := Osa(oAy);
	 if X > B then B := X;
	end;
	while Keypressed then Readkey;
	Writeln('Nastavit na střed');
	Readkey;
	C1 := Osa(oAx);
	C2 := C1;
	D1 := Osa(oAy);
	D2 := D1;
	for Poc := 1 to Pocet do
	begin
	 X := Osa(oAx);
	 if X > C2 then C2 := X;
	 if X < C1 then C1 := X;
	 X := Osa(oAy);
	 if X > D2 then D2 := X;
	 if X < D1 then D1 := X;
	end;
	while Keypressed then Readkey;
	Writeln('Nastavit dolů doprava');
	Readkey;
	E := $80000000;
	F := $80000000;
	for Poc := 1 to Pocet do
	begin
	 X := Osa(oAx);
	 if X < E then E := X;
	 X := Osa(oAy);
	 if X < F then F := X;
	end;
end.  

Teď už jen stačí si zřídit proměnnou, která bude udávat poslední stav joysticku. Pokud se joystick dostane do zóny <A,C1), bude vlevo, pokud , bude uprostřed, a pokud v (C2,E>, bude vpravo. Rozdíl nastane v tom, když se joysticky dostane do polohy menší než A nebo do polohy větší než E. V tomto případě musíte ponechat předchozí stav, který jste si uschovali (většinou to je levo nebo pravo). Totéž pro osu Y. U některých joysticků se navíc stává, že i když mají dobře nastavené mrtvé zóny, občas jim to přestřelí třeba přes půlku celé polosy, a v některých hrách Vám to může dost znepříjemnit život (např. jízda v autě). Můžete proto zavést podmínku, že pokud bude stav osy X uložen v Xnew, tak aby byla změna platná, musí platit že "(C1-Xnew) <= (C1-A) shr 1", resp. "(Xnew-C2) <= (E-C2) shr 1". Pak se tohoto vyvarujete. Problém nastane, pokud se takovýto výstřel objeví během automatické kalibrace. Buď musíte tyto výstřely elimovat i tam, a nebo požádat uživatele, aby nastavení mrtvých zón potvrdil, popř. upravil.



A to je pro dnešek všechno. Doufám, že Vám to alespoň k něčemu bylo dobré. Příště budeme pokračovat klávesnicí a myší pro Turbo Pascal 7, resp. Free Pascal, ale vlastními silami, a později po dokončení tohoto třídílného článku ještě grafikou a zvuky, resp. multimediálními možnostmi jazyka Pascal.


POKRAČOVÁNÍ PŘÍŠTĚ S KLÁVESNICÍ A MYŠÍ ...

2006-11-30 | Martin Lux
Reklamy:
Naše kulturistika Svaz kulturistiky a fitness ČR