Na FreeHostingu Endora běží desítky tisíc webů. Přidejte se ještě dnes!
Vytvořit web zdarmaNa FreeHostingu Endora běží desítky tisíc webů. Přidejte se ještě dnes!
Vytvořit web zdarmaPi programovn her i program se jist nevyhneme poadavkm na ten vstupnch zazen. Turbo Pascal sice dovoluje pouvn jednoduchch funkc READ a READLN, pop. ve spojen s jednotkou CRT tak ReadKey a KeyPressed, ale vystvaj zde otzky, pro linkovat celou jednotku kvli pr funkcm, i, pokud nemte patchovanou CRT pro vy potae, zda bude fungovat na strojch s frekvenc vy ne 200, resp. 233 MHz (znm Division By Zerro, Runtime Error 200). Proto bychom se mohli pokusit naprogramovat vlastn funkce.
Jist je Vm divn, pro zanat takovmi vcmi, kdy u je Pascal podporuje, pro teba nezat rovnou zvukovmi kartami, stmi nebo komunikac pes sriov kabel (hra ve dvou peci nen vbec k zahozen). No, dvody jsou minimln ti. Za prv, o zvukovch kartch budu jet pst. Za druh, o mezipotaov komunikaci si mete pest na mch strnkch (www.volny.cz/martinlux) v sekci Programovn a Perifrie (pop. i jinde, pokud se nkdo rozhodne moje lnky "importovat"). A za tet, jednotky dodvan k Pascalu (pokud u jsou) jsou pomal. A co nejvce natve je, kdy je program zdrovn eknm na stisk klvesy nebo zbyten te my, kdy se nic nedje!
Zaneme v Turbo Pascalu 7, nebo ten se u na kolch a je obecn sna pro nj nco napsat. Poslze pejdeme na Free Pascal, kter je efektivnj. Budu se (zatm) vnovat veobecn (i v dalch lncch) jen DOSov verzi, nebo pro Windows i Linux mete vyut speciln napsanch jednotek (MOUSE pro my, KEYBOARD pro klvesnici, WINGRAPH pro grafiku, i pmo DirectX jednotky pro vechny innosti narz). Navc jsem se jet tak daleko nedostal (dost mon, e a to pochopm, tak o tom nco napi, nebo nkdo jin). Navc pro Free Pascal existuje spousta pklad (nap. co se te jednotky MOUSE) pmo v jeho dokumentaci (anglitina je samozejm nezbytn). V pkladech pouvm diakritku Windows, avak pokud pouvte DOS verzi FP nebo TP7, muste mt v pamti ovlada pro Latin 2 i jinou etinu a diakritiku sprvn peformtovat. Pokud budete kompilovat pro Windows, bude diakritika sprvn (ale nen zarueno, e budou fungovat pklady, kde se pistupuje do DOSov (konvenn) pamti nebo k portm). Pklady pro DOS budou fungovat i pod Windows systmem, pokud dobe emuluje DOS. Tak moje assemblerovsk komente nejsou akceptovateln ani v TP7 ani ve FP, take je ped kompilac bu smate nebo nahrate typem {} i (* a *)...
Zaneme nm "jednodum" a to je ten joysticku. Vte nebo ne, i tak zastaral vc jako je BIOS poskytuje funkce pro jeho ten. Avak ty jsou obvykle docela pomal. Na druhou stranu celkem nevyaduj kalibraci a vrac celkem slunou pesnost. Nevhoda je jejich neustl voln, co se samozejm odraz na rychlosti. Peruen, kter poskytuje tyto sluby, m slo $15 (nebo-li 15h) a samotn sluba m slo $84. Je nutn si uvdomit dv vci: GAME port umouje pipojen a 2 joystick po 2 tlatkch nebo osch. V ppad, e je tam jen jeden, me vyuvat a 4 osy a 4 tlatka. Je nutn umt mezi tmto rozliovat podle toho, co Vm zad uivatel (detekce nen mon). Za druh, stav tlatek se vrac inverzn, tj. 1 znamen putn, 0 stisknut, a navc jako 4 nejvy bity (MSb). Stav jednotlivch os je slo WORD, piem pokud je joystick vycentrovn, mla by hodnota dosahovat piblin 32767. Problm je v tom, e odpory, kter se pouvaj v joysticku nejsou z nejkvalitnjch, nehled na mky v portu, take se pka jakoby klepe. Je proto vhodn provst kalibraci tm, e peteme nkolik hodnot po sob (pot, co vyzveme uivatele, aby na joystick nesahal) a tm zjistme, kam a se pka vychyluje. Pot sta brt, e je-li namen hodnota v danm intervalu, budeme brt joystick jakoby byl v klidov poloze (ve stedu). Jej pouit je triviln:
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 ; zskme stav tlatek inc dx int 15h ; zskme stav os mov x1,ax mov y1,bx mov x2,cx ; pokud mme jen jeden joystick mov y2,dx ; nemusme st vechny osy end;
Tento kd bude fungovat i ve Free Pascalu. My si vak zvolme mnohem rychlej zpsob, jak pouvat joystick, a to pomoc vlastnch sil. Vyuijeme znalosti toho, e stav MIDI/GAME portu se vrac na portu $201. Pokud se podvte do ATHelpu, zjiste e horn 4 bity jsou vyhrazeny pro tlatka a doln 4 pro osy. To vyplv z toho, e tlatka jsou digitln a sta jim tedy jen dva stavy. Ale jak st osy, kter jsou analogov, a pitom maj tak jen 1 bit? Zde se vyuv tzv. monostabilnho klopnho obvodu, kter vydr v nestabilnm stavu tak dlouho, jak velk odpor m k sob pipojen a toho se vyuv i u jostick. Nejprve zkusme zjistit, zda pota vbec njak joystick m. To se provede nsledovn:
function Detekce : boolean; assembler; asm
mov dx,$201 out dx,al ; zapeme cokoliv, tm zane men mov cx,$ffff mov bl,0 @cykl: nop loop @cykl in al,dx ; peteme stav portu cmp al,$ff je @neni ; joystick nenalezen mov bl,1 @neni: mov al,bl
end;
Test vlastn probh tak, e na port joysticku zapeme libovolnou hodnotu (proto ani AL neplnme) a pokme 65535 opakovn. Pokud se do t doby nezmn stav portu na hodnotu rozdlnou od 255, oekvme, e nen joystick pipojen. Samozejm to pot s tm, e je vhodn, aby na nj uivatel neahal, nebo alespo nemakal dn tlatka. Tuto legraci pouijeme pozdji ke ten stavu os. Nyn ale zkusme zjistit, jak jsou na tom tlatka.
function Tlacitka : byte; assembler; asm
mov dx,$201 out dx,al in al,dx shr al,4 not al
end;
Jist je Vm tento kd povdom. Tmto kdem peteme vechna 4 tlatka, kter sta testovat pomoc OR instrukce, take TLACITKA OR 1 = 1 je prvn tlatko, TLACITKA OR 2 = 2 je druh tlatko (stisknut), a tak dle. Se tenm pozice os je to velmi jednoduch. Zde si ale radji u zvolme, kter osa ns bude zajmat. Za typ dosadme kd osy, kterou budeme chtt zjistit, tj. 1 pro X na Joysticku A, 2 pro Y na joysticku A, 4 pro Bx a 8 pro By. Je vhodn si vytvoit pojmenovan konstanty, abychom se nespletli a nezadali nco jinho.
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 = stle se m jnz @cykl mov ax,cx ; pozice na dan ose end;
Protoe tato funkce (je nutn vzt v potaz, e tato funkce pedpokld, e nco nam, jinak skon v nekonen smyce) defakto m hodnotu vech os, ale pedv jen jednu vybranou, je zbyten provdt men 2x a 4x po sob pro vechny osy. Msto toho nm sta napsat si funkci, kter bude vracet tolik os, kolik budeme potebovat. Protoe budeme vracet 2-4 parametry, napeme danou funkci jako proceduru a budeme jej vracet pes VAR (u Free Pascalu bychom mohli pout i funkci, protoe FP umouje vracet i sloit typy, co TP7 neum). Bhem tto funkce zakeme peruen, protoe jeho ppadn vskyt by zmnil hodnotu namenho daje (je mon jej samozejm zakzat i v pedchoz funkci). Je tak nutn si ovdomit, e nyn nepedvme vsledek pes registr AL, ale pes ukazatele, kter jsou uloeny v zsobnku (VAR), proto musme tak zjistit adresu danch promnnch. Tato funkce u tak bere v potaz i to, e by nic nenamila.
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 mme. Zatm dn @1: in al,dx inc cx cmp cx,$ffff ; konec men jne @2 ; ne mov al,0 @2: or al,bl ; abychom netestovali ji zmen test al,1 jnz @3 or bl,1 ; osa Ax je ji zmena les di,_ax mov es:[di],cx ; pedme 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 vechny osy zmeny? jne @1 ; ne sti ; ano, konme end;
Pro rychlej kd je mon tak nahradit vechny MOV NECO,0 na XOR NECO,NECO (jen je nutn potat s tm, e se budou mnit pznaky v registru FLAGS); tak test CMP NECO,0 lze napsat jako OR NECO,NECO. Pokud budete cht pout dan kd pod Free Pascalem, muste konstrukce:
les di,NECO mov es:[di],cx
mov esi,NECO mov [esi],cx
Je to dno strukturou programu a chrnnm reimem (Flat model). Nezapomete vdy pout jednotku GO32 pomoc USES, jinak Vm to bude hlsit podobn vci:
Zkuenj programtoi si ale vimnou jin jedn vady. Tento program toti sice funguje na vech potach, ale na kadm procesoru dv jin vsledky, protoe stihne napotat v CX jinou hodnotu (na nkterch dokonce jet dve, ne stihne nco zmit). To by jist nemuselo vadit, stailo by jen pi kadm sputn zkalibrovat joystick, ale jak uznte, to je trochu otrava (hlavn pro uivatele). Je proto vhodnj pout harwarov ta. Meme se bu povsit na INT 1Ch, kter se standardn vol 18.2x za vteinu a zvyovat si njakou svou promnnou, a nebo st stav tae pes tzv. oblast pamti v BIOS data. Zde se nachz Dword (Longint) na adrese 0:$46c, kter obsahuje poet tik hodin od startu potae. A ten se aktualizuje tak 18.2x za vteinu. Zkusme takto nahradit funkci pro men jedn osy a navc ji oetme proti tomu, e by nic nenamila:
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 pehlednost psna radji v Pascalu, i kdy nee problm, pokud pekrote pi hran poet tik 1^32-1). Nejprve si zjistme potan stav a pak zaneme mit. Pro ppad, e bychom nic nezmili, nastavme si osu do stedov polohy. V cyklu vdy nejprve pokme 1 takt tae a kdy probhne, zjistme, zda u neskonilo men. Pokud ano, pedme hodnotu poitadla, kter namilo hodnotu od 0 do 17. Abychom se dreli pedchozch konvenc, normalizujeme hodnotu <0,17> na hodnotu <0,65535>. Pro urychlen by samozejm stailo pracovat jen s intervalem 0 a 17. Ve Free Pascalu bude kd malinko jin. Zvlt kvli portm a pstupu do pamti. Navc zde pouvme interval 0-65536 nebo pak nen nutn pi jeho peteen 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 dret intervalu 0-65536, mme do OSA uloit pouze POCITADLO bez njakch vpot, co nm ten nepomrn zrychl. Cel vc m ale malou nevhodu v tom, e men joysticku probh po celkem velkch skocch. Peci jenom meme namit teoreticky hodnotu 0, pak a 3600, 7201, atd. co nen pli pesn (pokud ovem nemte tzv. digitln joystick, co byly ty star, kter nemly plynul potenciometry, ale krajn spnae, take umly jen levo,sted,pravo a nic mezi tm). Je to dno tm, e asova se inkrementuje 18.2x za vteinu. Natst mme prostedky, jak jej urychlit. asova se nastavuje na portu $43 a jeho hodnota se d pot vyst na portu $40 (hod se nap. pro nhodn sla). asova provd "tiky" v rozmez 838 ns a 54.925493 ms (co je prv 18.2x za vteinu). To je provedeno nastavenm tae pomoc dlitele 1 a 65535 (lze pout i 65536, ale to je pro ns zbyten). asovae (tzv. porty PIT) jsou ti, vtinou se vak pouv ten prvn. Cel se to nastavuje a pli jednodue. Standardn se uvd vzorec "1193180 DIV Frekvence". Je ale mon k prvnmu slu jet 1 pidat.
procedure Casovac(PocetTiku : longint); var Delitel : word; begin Delitel := $1234DD div PocetTiku; Port[$43] := $34; Port[$40] := Lo(Delitel); Port[$40] := Hi(Delitel); end;
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 Vm to nebude fungovat (nap. budete mt stle jen rychlost 18.2 Hz), bude nutn obsadit peruen INT $1C (namsto testovn potu tik pomoc Mem) a pi kadm voln si pist 1 (pokud bude nastavena na TRUE njak promnn nap. MERENI), kter pob u sprvn (podle nastaven). Kdy budete tedy chtt mit joystick ve 128 Hz intervalech (pop. 64 Hz, pokud by se Vm to zdlo moc), zavolejte tuto proceduru s parametrem 128, resp. 64. Dvojkov slo jsem zvolil proto, jeliko pak je mon upravit pedchoz kd na men os tak, aby se namsto DIV MAX mohlo pout SHR 7, resp. SHR 6, co je vrazn rychlej. Joystick lze nyn mit po skocch 512, resp. 1024, co je u vrazn lep. Problm nastane, pokud si nyn budete chtt pest systmov hodiny. Zjistte, e se jaksi splaily. To je dno tm, e se nyn nezvtuj 18.2 za vteinu ale teba 128x za vteinu. Pro Vs z toho neplyne problm, hor to bude s aplikacemi, kter budou hodiny st, a Vy skonte. Natst tu je jednoduch een. Ped koncem programu nastavte frekvenci zpt na 18.2 Hz (ideln pouijte dlitel 0). Ale toto een nesta, protoe hodiny jdou naped. Natst ns zachrn CMOS, kter si udruje tzv. reln hodiny. Ty jdou vdy nezvisle na systmovch (dokonce se z nich systmov pi startu potae "vytvo"). Te budeme muset opt vyut porty, nebo jinak s k CMOSu nedostaneme. CMOS sdl od portu $70, navc zabr jen nkolik port a svou malou pam zpstupuje pes tzv. registry (obdoba mixer u zvukovch karet). Prce s tmito registry je natst lehk. Do portu $70 urte slo registru a z portu $71 nsledn petete jeho obsah. Protoe pam CMOS je trochu pomalej (navc organizovan po bytech), je vhodn dt potai chvli as na zpracovn Vaeho poadavku (v istm assembleru sta instrukce JMP $+2). Legrace je o to vt, e veker daje jsou uloeny jako BCD, a tedy je musme jet pevdt na standardn sla.
function b(v : byte) : byte; begin b := 10 * (v shr 4) + (v and 15) end;
Tato funkce pevede BCD daj na normln slo. BCD je slo, kter namsto 255 doke v jednom bytu uloit pouze 99, protoe pro kadou slici jsou vyhrazeny 4 bity a tam se v destkov soustav vce ne 0-9 dostat ned. Stednk za pkazem uvdt nemusme, nebo po nm hned nsleduje END. Tuto funkci vyuijeme pi ten pamti 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;
procedure Cas; var s,m,h,d,m,y,rok : word; begin s := r(0); ; vteiny m := r(2); ; minuty h := r(4); ; hodiny d := r(7); ; den m := r(8); ; msc y := r(9); ; rok SetTime(0,0,0,0); ; nastavme plnoc if (y >= 80) and (y <= 99) then rok := y+1900 else ; urme sprvn stolet rok := y+2000; SetDate(rok,m,d); ; nastavme sprvn as a datum SetTime(h,m,s,0); end;
Dvod, pro nejprve nastavujeme plnoc je ten, aby se nm nestalo, e nm sprvn as nebo datum zmn jej pekroen. Pokud bychom toti nejprve nastavili datum a pot as, mohlo by se datum zmnit na nov den v ppad, e by systmov datum bylo tsn ped plnoc (a samozejm reln jet daleko ped n). Opan by zase hrozilo, e bychom nastavili as, kter by te byl tsn ped plnoc a ne bychom nastavili datum (pi tch vpotech), tak bychom byli o den pozadu. Ale to je celkem nadnesen mra rizika, kter hroz i tehdy, je-li reln as, kter jsme naetli, tsn ped plnoc. Milisekundy nenastavujeme, nebo 1 vteina spodn ns zase a tak nepl vzhledem k tomu, e dky nepesnmu krystalu si PC udl do tdne mnohem vt chybu i bez ns. Rok je v BIOSu uloen pouze na 1 byte, tedy musme rozliit, do kterho stolet spad. Meme samozejm tak vyut sluby CMOSu, kter zn sprvn stolet:
procedure Cas; var s,m,h,d,m,y,t : word; begin s := r(0); ; vteiny m := r(2); ; minuty h := r(4); ; hodiny d := r(7); ; den m := r(8); ; msc y := r(9); ; rok t := r($32); ; stolet SetDate(t*1000+rok,m,d); SetTime(h,m,s,0); end;
Je tak doporueno, pokud se zjist, e je S po naten 0, nast vechno jet jednou, protoe mohlo dojt k pekroen plnoci. Kdy vyuijeme prvn variantu, dostaneme u finln een:
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;
Dobe, te u umme st joystick, ale jak zjistit, kde jsou meze tch souadnic, kter jsme namili? K tomuto slou tzv. auto-kalibrace. Tu je nutn na danm potai pout pouze 1x a pak nkam uloit. Bude platn, dokud se nevymn joystick. I pes svj vzneen nzev se jedn o velice jednoduchou vc. Sta jen, kdy uivatele podte o ti vci: nastaven joysticku nahoru doleva, nastaven na sted (putn pky) a pot nastaven do polohy dole vpravo (nejjednodu monost je nechat zjistit jen NL a DP a sted si vydlit, nebo zjistit jen sted a ve ostatn brt jako L a P, resp. D a N, ale to je velice nepesn). Mezi jednotlivmi kroky me uivatel bu stisknout tlatko joysticku, a nebo radji libovolnou klvesu, nebo to zaru, e zvlt ve stedov poloze nezpsob tas ruky namen njakch extrmnch hodnot, zvlt, budeme-li v pozdjch metodch st vcekrt. Vad to i pesto, e se vzva objev a po dokonen men, protoe vlastn ovlivn a to nsledujc. Jen je nutn mt na pamti, e uivatel me tlatko joysticku nebo klvesu dret o nco dle ne je zdrvo a tedy ji meme pest nkolikrt. Jak se tomu vyvarovat? Pot, co zjistme, e uivatel stiskl klvesu nebo tlatko, kter ns zjm, pokme jet na dobu, kdy nebude stisknuto nic. My si pi ten peteme souadnice, kter namme v danch 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 sted'); 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 stedu. Pro osu Y to plat odbobn, jen s parametry B,D a F. Asi jste si ale vimli, e spousta joystick nen zrovna super materil, a i kdy na n nesahte, tak se jakoby chvj a to i pesto, e jste je ve Windows nakalibrovali bez chvn. Pro een tohoto problmu se zavd tzv. Dead Zone, nebo-li mrtv zna. Zde se jedn o msto na ose, kde se pohyb joysticku prost ignoruje. Ve spousta her se d nastavit run, ale my si udlme tzv. autodetekci. Sta jen, kdy doplnme n stvajc kd, kdy v kadm stavu peteme souadnice ne jednou, ale teba 100x. Bhem ten porovnvme, zda je nov vchylka men i vt a podle toho si ji ulome jako mrtvou znu. Promnnch budeteme mt tedy o 1 navc pro kadou 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 sted'); 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 zdit promnnou, kter bude udvat posledn stav joysticku. Pokud se joystick dostane do zny <A,C1), bude vlevo, pokud
A to je pro dneek vechno. Doufm, e Vm to alespo k nemu bylo dobr. Pt budeme pokraovat klvesnic a my pro Turbo Pascal 7, resp. Free Pascal, ale vlastnmi silami, a pozdji po dokonen tohoto tdlnho lnku jet grafikou a zvuky, resp. multimedilnmi monostmi jazyka Pascal.
POKRAOVN P͊T S KLVESNIC A MY ...