Asi si říkáte, proč probírat takové věci. Všichni přeci víme, že na disk se přistupuje přes BlockRead a BlockWrite (je snad ještě někdo, kdo to čte po 1 bytu?!?), v paměti se pracuje s GetMem (popř. New) a FreeMem (atd.), a na CD-ROM se přeci přistupuje stejně jako na obyčejný disk (HDD). No, možná pro Vás budu objevovat Ameriku, ale možná, že se také dozvíte něco nového. Sice jsem psal, že zvukovými kartami končím, ale ještě mne napadlo něco, o čem bych mohl napsat. Tak do toho... Následující kódy by měly fungovat jak v TP7 (po zmenšení některých array či GetMem), tak i ve FP. Pokud zde najdete chybu, tak si ji prosím opravte. Mým cílem není napsat Vám kompletní jednotky, ale naučit Vás, jak si je udělat sami... opravou chyb se také hodně naučíte.
Nemá cenu se zabývat udržováním několika bloků najednou, např. několika částmi stejného souboru, protože to nám zaručuje správce diskové cache pod Windows nebo SmartDrive (či jiný) pod DOSem. Pokud vyžadujete mít v paměti několik bloků současně, tak použijte naši metodu a daná data si vždy někam uschovejte. Nám tedy stačí, když si uvědomíme, co často děláme a jak si to zjednodušit. Naším hlavním cílem je, abychom se nemuseli starat o správu souboru, a mohli z něj číst třeba jen po jednom bytu, aniž bychom tím zpomalovali aplikaci.
Napíšeme si tedy proceduru, která bude využívat svůj vlastní buffer o velikosti 32 kB, s tím, že pokud budeme vyžadovat nějaká data, procedura načte ze souboru celý blok (pokud navíc soubor ještě není otevřen, zajistí jeho otevření a zavření případného předchozího) o velikosti 30 kB od požadované pozice (a 2 kB před současnou pozicí, kdyby se bylo potřeba vracet) a z něj teprve poskytuje data. Můžeme tedy žádat i jen jeden byte, a přesto bude práce s ním rychlá. Navíc procedura zajišťuje i spožděný zápis, tedy můžeme data zapisovat i a číst několikrát po sobě, a přesto se zapíší až když se bude zavírat soubor nebo měnit blok dat (zápis by šel optimalizovat, kdybychom si podobně jako u banků ve VESA udržovali interval offsetů v bufferu, kam se zapisovalo a pak uložili jen tato data, ale nebudeme si to komplikovat - procedura by neměla být tak složitá, aby její zpracování nezdrželo počítač déle než zápis těch pár kB dat na disk navíc). Procedura pracuje jen s netypovými soubory (prakticky kterýkoliv), a když si daný kód napíšete jako jednotku, tak se sama nainstaluje při startu Vašeho programu a také sama skončí, když Vy ukončíte svůj program:
unit FastFile; {$I-} {...kdo to ještě neví} interfrace type TJmeno = string[8+1+3]; {vrací chybový kód: 0 OK 1 OK, dosažen konec souboru 2 soubor nelze otevřít 3 chyba při čtení dat z disku (nebo za koncem souboru) } function CtiSoubor(Jmeno : TJmeno; var Buffer : array of byte; Pozice,Pocet : longint) : byte; {0 = OK, 4 = nelze založit nový soubor, 5 = chyba při zápisu dat na disk} function ZapisData(Jmeno : TJmeno; var Buffer : array of byte; Pozice,Pocet : longint) : byte; {může přidávat data na konec souboru (po 32-2 kB blocích), ale ne za konec} {pokud potřebujete přidat méně než 30 kB, požadujte nejprve data na začátku souboru a pak nahrajte blok NOVAVEL-30 kB, počítač načte blok <=NOVAVEL-32 kB a při zápisu zapíše 32 kB tak, že velikost souboru bude ta Vámi požadovaná} implementation const VelBufferu = 32768; var DiskBuffer : record Prostor : pointer; {kde jsou uložena naše data} Soubor : TJmeno; {aktuální soubor, '' = žádný, zavřený} Handle : file; {ukazatel na soubor} Offset : longint; {akt.pozice bufefu v souboru} Vel : longint; {velikost celého souboru} PocetDat : longint; {počet skutečně přeč./zaps.dat} Zapis : boolean; {bylo zapisováno?} end; Cesta : string; {kde jsou uloženy soubory} {pokud změníte velikost souboru na menší pomocí Seek a Truncate, musíte provést: DiskBuffer.Vel := FileSize(Handle)} function CtiSoubor(Jmeno : TJmeno; var Buffer : array of byte; Pozice,Pocet : longint) : byte; var PuvRezim : byte; PoziceBuf : longint; AktVel : longint; begin with DiskBuffer do begin if Soubor <> Jmeno then {chceme pracovat s jiným souborem} if Soubor <> '' then begin Close(Handle); if IOResult <> 0 then begin end; Soubor := ''; end; if Soubor = '' then {ještě nebyl přiřazen} begin if Cesta[Length(Cesta)] = '' then Delete(Cesta,Length(Cesta),1); Assign(Handle,Cesta+''+Jmeno); PuvRezim := FileMode; FileMode := 0; {pouze pro čtení - kdyby měl bit ReadOnly} Reset(Handle,1) FileMode := 2; {i pro zápis; pokud je RO, vrátí BW chybu později} if IOResult <> 0 then begin CtiSoubor := 2; Exit; end; Vel := FileSize(Handle); {zjistíme velikost} Offset := FilePos(Handle); {aktuální pozice = 0} Zapis := False; Soubor := Jmeno; {neotevře znovu soubor v ABCx, pokud je již otevřen XYx} end; PoziceBuf := 0; {kam zapisujeme do externích dat} (* --- *) while Pocet > 0 do {dokud je co číst, čteme} begin {máme již požadovaná data načtená?} if not ((Pozice >= Offset) and (Pozice < Offset+VelBufferu)) then begin {ne, musíme je načíst} if Pozice >= Vel then begin CtiSoubor := 3; {překročili jsme konec souboru} Exit; end; if Pozice > 2048 then {nastavíme počátek bufferu v souboru} Offset := Pozice-2048 else Offset := 0; Seek(Handle,Offset); {skočíme na danou pozici a přečteme soubor} BlockRead(Handle,Prostor^,VelBufferu,PocetDat); if (IOResult <> 0) or (PocetDat = 0) then begin CtiSoubor := 3; Exit; end; Zapis := False; end; {poskytneme data} if Pozice+Pocet >= VelBufferu then AktVel := VelBuffer-Pozice else AktVel := Pocet; {kolik dat budeme nyní přenášet} Move(Ptr(Seg(Prostor^),Ofs(Prostor^)+Pozice-Offset)^,Buffer[PoziceBuf],AktVel); Inc(Pozice,AktVel); {posuneme pozici v souboru, kdyby bylo více dat} Inc(PoziceBuf,AktVel); end; {podáme hlášení o výsledku konce souboru} (* --- *) if Pozice = Vel then CtiSoubor := 1 else CtiSoubor := 0; end; end; function ZapisData(Jmeno : TJmeno; var Buffer : array of byte; Pozice,Pocet : longint) : byte; begin with DiskBuffer do begin if Soubor <> Jmeno then if Soubor <> '' then begin Close(Handle); if IOResult <> 0 then begin end; Soubor := ''; end; if Soubor = '' then begin if Cesta[Length(Cesta)] = '' then Delete(Cesta,Length(Cesta),1); Assign(Handle,Cesta+''+Jmeno); PuvRezim := FileMode; Reset(Handle,1) if IOResult <> 0 then begin Rewrite(Handle,1); {pokud nejde otevřít, zkusíme ho znovu založit} if IOResult <> 0 then begin ZapisData := 4; Exit; end; end; Vel := FileSize(Handle); Offset := FilePos(Handle); Zapis := False; Soubor := Jmeno; end; PoziceBuf := 0; {odkud čteme z externích dat} (* --- *) while Pocet > 0 do begin if not ((Pozice >= Offset) and (Pozice < Offset+VelBufferu)) then begin if Zapis then {aktuální blok byl změněn, musí se zapsat} begin Seek(Handle,Offset); BlockWrite(Handle,Prostor^,VelBufferu,PocetDat); if (IOResult <> 0) or (PocetDat = 0) then begin ZapisData := 5; Exit; end; end; if Pozice > Vel then {na konec souboru přidávat můžeme} begin ZapisData := 3; Exit; end; if Pozice > 2048 then Offset := Pozice-2048 else Offset := 0; Seek(Handle,Offset); {načteme data kvůli opětovnému zápisu} BlockRead(Handle,Prostor^,VelBufferu,PocetDat); if (PocetDat <> Vel-Offset) and (Pocet <> VelBufferu) then begin ZapisData := 3; Exit; end; end; if Pozice+Pocet >= VelBufferu then AktVel := VelBuffer-Pozice else AktVel := Pocet; Move(Buffer[PoziceBuf],Ptr(Seg(Prostor^),Ofs(Prostor^)+Pozice-Offset)^,AktVel); Inc(Pozice,AktVel); Inc(PoziceBuf,AktVel); Zapis := True; {tento blok dat byl změněn} end; (* --- *) ZapisData := 0; end; end; var KonecObsl : pointer; {ukončíme naši jednotku} procedure KonecFR; far; begin ExitProc := KonecObsl; if (DiskBuffer.Jmeno <> '') and DiskBuffer.Zapis then begin {zapíšeme případná zbývající data} Seek(Handle,Offset); BlockWrite(DiskBuffer.Handle,DiskBuffer.Prostor^, DiskBuffer.VelBufferu,DiskBuffer.PocetDat); Close(DiskBuffer.Handle); if IOResult <> 0 then; end; FreeMem(DiskBuffer.Prostor,VelBufferu); end; {automatická instalace při startu programu} begin KonecObsl := ExitProc; ExitProc := @KonecFR; GetMem(DiskBuffer.Prostor,VelBufferu); {to snad má dneska každý} FillChar(DiskBuffer,SizeOf(DiskBuffer),0) Cesta := ''; end.
Tento článek se nás netýká, pokud nám stačí paměť, kterou získáme přes VAR. Pokud ovšem potřebujete dynamickou správu paměti, jistě na ni používáte GetMem. Pokud alokujete jeden, dva bloky, nemá cenu, abyste používali následující příklad. Pokud však během programu často alokujete malé bloky paměti a zase je uvolňujete, je zdlouhavé používat neustále GetMem, neboť tato procedura je totiž velice pomalá! Vyřešíme to po svém. Necháme naši jednotku, aby alokovala velký blok paměti (v TP7 je to omezeno na 65520 bytů, takže to trochu postrádá smysl, ale pokud děláte např. spojový seznam s ukazateli, kde každá položka má pár (desítek) bytů, bude se to i tak hodit), a v něm bude provádět operace s pamětí. Ve Free Pascalu by se dalo zařídit, aby Váš buffer mohl dynamicky růst, ale jak říkám, nebudeme si komplikovat život.
Funkce podporují i fragmentaci, tj. budou obsazovány všechny volné bloky od začátku bufferu, i když se jedná jen o "díry". Můžete si doplnit defragmentaci, kdy budete volné bloky přesouvat na konec a ty plné na začátek, kdy Vám stačí pouze měnit ukazatel na blok (a data v nich, samozřejmě). Funkce Vám vrací ukazatel na data, kterým můžete číst data normálně přes Bloky[Handle].Data^, ovšem pokud jste alokovali více než je min.velikost bloku, a paměť je fragmentovaná, můžete bez obav přečíst jen velikost prvního bloku (což je v našem případě 16 kB). Aby toto fungovalo i mimo jednotku, musíte strukturu BLOKY z VAR přesunout do INTERFACE části (můžete si udělat i vlastní funkci, které když předáte aktuální blok (ne handle), tj. ten, na nějž ukazuje POINTER (pokud je první), tak Vám vrátí pointer na následující blok stejného vlastníka (Seg(Buffer^):Ofs(Buffer^)+DalsiBlok*VelBloku), tj. můžete číst dalších 16 kB; pokud už byl toto poslední blok (došlo se na konec seznamu), vrátí NIL (a namísto bloku, který jste jí předali, vrátí číslo bloku, kterému patří vrácený ukazatel - toto číslo pak použijete zase jako parametr pro další hledání). Takto můžete najít všechny bloky. Funkci zjednodušíte pátrání, když do RECORDu zavedete položku PrvniBlok, což bude číslo bloku (dosadí funkce ALOKUJ) ve VOLNEBLOKY, na který ukazuje pointer - od něj začnete hledat další bloky, které pak použijete zase jako výchozí pro další hledání). Můžete však využít vestavěné funkce, které načtou/uloží data z/do Vašeho bufferu do této paměti korektně, i pokud jsou data fragmentovaná. Můžete si dokonce určit i offset v daném handle (v bytech).
unit FastMem; interface {0 = OK, 1 = nedostatek volné paměti (málo bloků), 2 = už nejsou volné handle} function Alokuj(var Handle : word; Vel : longint) : byte; {0 = OK, 3 = dané handle neexistuje (nebylo alokováno)} function Uvolni(Handle : word) : byte; {0 = OK, 3 = handle není alokované, 4 = překročen limit velikosti handle} function Zapis(Handle : word; var Data : array of byte; Vel,Offset : longint) : byte; {0 = OK (buf.naplněn), 3 = handle není alokované, 4 = překročen limit velikosti} function Precti(Handle : word; var Data : array of byte; Vel,Offset : longint) : byte; implementation const VelBufferu = 8*1048576; {1024*1024 = 1 MB} Bloku = 64; {kolik maximálně "pamětí" můžete alokovat; max. 65534} {kvůli tomu, že $ffff označuje blok bez vlastníka} VBloku = 512; {na kolik bloků je paměť rozdělená} {pak bude možné min. alokovat VelBufferu div VBloku} {tj. zde 16 kB} var Buffer : pointer; Bloky : array[0..Bloku-1] of record {seznam bloků paměti} Bloku : word; {0 = není alokované} Vel : longint; Data : pointer; {pro 0 je zde NIL} end; VolneBloky : array[0..VBloku-1] of word; {seznam částí volné paměti} VolnychBloku : word; function Alokuj(var Handle : word; Vel : longint) : byte; var VelBloku : longint PocetBloku : longint; i,j : word; begin VelBloku := VelBufferu div VBloku; if Vel > VolnychBloku*VelBloku then {málo volné paměti} begin Alokuj := 1; Exit; end; PocetBloku := Vel div VelBloku; if Vel mod VelBloku <> 0 then Inc(PocetBloku); for i := 0 to Bloku-1 do {najdeme volné handle} if Bloky[i].Bloku = 0 then begin Bloky[i].Vel := Vel; {obsadíme volné bloky} for j := 0 to VBloky-1 do if VolneBloky[j] = $ffff then begin VolneBloky[j] := i; Dec(VolnychBloku); Inc(Bloky[i].Bloku); if Bloky[i].Bloku = PocetBloku then Break; end; Alokuj := 0; {OK} Exit; end; Alokuj := 2; {žádné volné handle} end; function Uvolni(Handle : word) : byte; var i : word; begin if Bloky[i].Bloku = 0 then {toto handle nebylo použito} begin Uvolni := 3; Exit; end; Uvolni := 0; Bloky[i].Data := NIL; {nastavíme handle na volné} for i := 0 to VBloku-1 do {uvolníme všechny bloky, které si nárokovalo} if VolneBloky[i] = Handle then begin VolneBloky[i] := $ffff; {uvolníme daný blok} Dec(Bloky[i].Bloku); Inc(VolnychBloku); if Bloky[i].Bloku = 0 then Break; {už jsou všechny} end; end; function Zapis(Handle : word; var Data : array of byte; Vel,Offset : longint) : byte; var AktVel : longint; VelBloku : longint; i : word; AktBlok : word; Zdroj : longint; begin if Bloky[i].Bloku = 0 then begin Zapis := 3; Exit; end; if Vel+Offset > Bloky[i].Vel then {některá data jsou mimo limit} begin {nebo Offset je větší než počet bytů v handle} Zapis := 4; Exit; end; VelBloku := VelBufferu div VBloku; AktBlok := 0; Zdroj := 0; for i := 0 to Bloky[i].Bloku-1 do {začneme přenášet data do bloků} begin if Vel > VelBloku then AktVel := VelBloku else AktVel := Vel; {kolik nám ještě zbývá} Dec(Vel,AktVel); while Offset > VelBloku do {najdeme první blok, od kterého vede offset} begin while VolneBloky[AktBlok] <> Handle do Inc(AktBlok); Dec(Offset,VelBloku); {zároveň hledáme jen v našich blocích} end; if Offset > 0 then begin {poprvé musíme zohlednit offset} Move(Data[Zdroj],Ptr(Seg(Buffer^),Ofs(Buffer^)+AktBlok*VelBloku+Offset)^,AktVel); Offset := 0; end else Move(Data[Zdroj],Ptr(Seg(Buffer^),Ofs(Buffer^)+AktBlok*VelBloku)^,AktVel); Inc(Zdroj,VelBloku); {přesuneme data a aktualizujeme ukazatel} if Vel = 0 then Break; {už není požadavek na přenos zbytku} end; Zapis := 0; end; function Precti(Handle : word; var Data : array of byte; Vel,Offset : longint) : byte; var AktVel : longint; VelBloku : longint; i : word; AktBlok : word; Cil : longint; begin if Bloky[i].Bloku = 0 then begin Precti := 3; Exit; end; if Vel+Offset > Bloky[i].Vel then begin Precti := 4; Exit; end; VelBloku := VelBufferu div VBloku; AktBlok := 0; Cil := 0; for i := 0 to Bloky[i].Bloku-1 do begin if Vel > VelBloku then AktVel := VelBloku else AktVel := Vel; Dec(Vel,AktVel); while Offset > VelBloku do begin while VolneBloky[AktBlok] <> Handle do Inc(AktBlok); Dec(Offset,VelBloku); end; if Offset > 0 then begin Move(Ptr(Seg(Buffer^),Ofs(Buffer^)+AktBlok*VelBloku+Offset)^,Data[Cil],AktVel); Offset := 0; end else Move(Ptr(Seg(Buffer^),Ofs(Buffer^)+AktBlok*VelBloku)^,Data[Cil],AktVel); Inc(Zdroj,VelBloku); if Vel = 0 then Break; end; Zapis := 0; end; var KonecObsl : pointer; {$F+} {far je v FP zbytečný, ale ani nepřekáží} procedure KonecFR; begin ExitProc := KonecObsl; FreeMem(Buffer,VelBufferu); end; {$F-} begin KonecObsl := ExitProc; ExitProc := @KonecFR; GetMem(Buffer,VelBufferu); FillChar(VolneBloky,SizeOf(VolneBloky),$ff); FillChar(Bloky,SizeOf(Bloky),0); VolnychBloku := VBloku; end;
Nám v této kapitole nepůjde ani tak o čtení souborů z CD-ROM, protože to je možné stejně jako u HDD, ale přímo ovládání samotné mechaniky. Toužíte se naučit mechaniku vysouvat a zasouvat, či si udělat vlastní přehrávač Audio CD (za předpokladu, že máte povolený vstup CD na zvukové kartě na její výstup, viz. minulý seriál)? Tak jste tu správně. Tady bude trochu více funkcí a navíc v assembleru.
První věc, kterou musíte udělat na začátku Vašeho programu, je tzv. inicializovat CD-ROM mechaniku. Prakticky si pouze zjistíte informace od ovladače. CD-ROM totiž pracuje nezávisle na počítači, je možné ji tedy spustit (PLAY) a pak ukončit program. Ona bude hrát dál. Pokud jí budete chtít zastavit, spustíte znovu program, Init a pak STOP (ten raději proveďte 2x (příkaz, ne program) za sebou, některé mechaniky jsou na to háklivé). Nejprve budeme potřebovat vytvořit nějaké ty proměnné:
var Pozadavek : record Velikost : byte; Podjednotka : byte; Prikaz : byte; StatusLo : byte; StatusHi : byte; Rezerva : array[0..7] of byte; Zbytek : array[0..242] of byte; end; Informace : record Verze : word; Pocet : word; Mechanika : word; MinStop : byte; MaxStop : byte; Prvni : longint; Posl : longint; end; Subkanal : array[0..255] of byte;
Pokud Vám není jasný význam z jejich jmen, poznáte ho během jejich využívání. První, trochu delší funkce je již zmíněná inicializace. Ta zjistí, zda se používá správná verze ovladače (MSCDEX) a zda je mechanika CD-ROM (a připravena):
function Init : boolean; assembler; asm mov ax,$1500 {viz. ATHelp, multiplex} xor bx,bx {počet CD-ROM mechanik} clc {or ax,ax} int $2f xor ax,ax {chyba} jc @chyba mov informace.pocet,bx {počet a číslo první mechaniky, 0=A, 1=B, atd.} mov informace.mechanika,cx mov ax,$150c xor bx,bx {verze ovladače} clc int $2f xor ax,ax jc @chyba mov informace.verze,bx cmp bx,512 {potřebujeme verzi 2.01} jbe @chyba mov ax,$150b {je to skutečně CD-ROM} xor bx,bx {můžete si vybrat i jinou, pokud jich je více} mov cx,informace.mechanika clc {tento bit se většinou nemusí mazat} int $2f mov cx,ax xor ax,ax jc @chyba cmp bx,$adad {značka pro MSCDEX} jne @chyba or cx,cx {mechanika není CD} je @chyba mov pozadavek.prikaz,$d call proved {otevřeme zařízení - ne dvířka} xor ax,ax jc @chyba {zjisti stav} mov subkanal+0.byte,6 call ioinput xor ax,ax jc @chyba {stav} mov bl,subkanal+1.byte test bl,$10 {podporuje Audio CD?} jz @chyba inc ax @chyba end;
Uf, je to za námi. Jistě jste si všimli, že tu voláme dvě jakési funkce. Ve skutečnosti jsou vlastně celkem tři, a umožňují komunikovat s danou jednotkou. První funkce vyžaduje od jednotky provedení určitého příkazu, další dvě do ní (nebo z ní) posílají určitá data (informace):
procedure Proved; assembler; asm mov ax,$1510 {požadavek na ovladač} mov cx,informace.mechanika mov bx,seg pozadavek mov es,bx mov bx,offset pozadavek clc int $2f jc @chyba mov al,pozadavek.statushi add al,al {nastavíme Carry při chybě} end; function IOinput : word; assembler; asm mov pozadavek.prikaz,3 {IO vstup} mov ax,offset subkanal {adresa pro přenos} mov pozadavek+$e.word,ax mov ax,seg subkanal mov pozadavek+$10.word,ax call proved end; function IOoutput : word; assembler; asm mov pozadavek.prikaz,$c mov ax,offset subkanal mov pozadavek+$e.word,ax mov ax,seg subkanal mov pozadavek+$10.word,ax call proved end;
Teď budeme potřebovat funkci pro ukončení obsluhy ovladače, kterou zavoláte na konci svého programu, pak funkci pro reset mechaniky (ta se moc nepoužívá, ale kdybyste chtěli), a pak několik podpůrných funkcí: jedna zastavuje přehrávání, druhá ho obnovuje po pauze, třetí přepočítává čas z červené knihy na sektor a čtvrtá zahajuje přehrávání. Upozorňuji, že to jsou (kromě Konec a Reset) pouze podpůrné funkce. Můžete je volat buď přímo a nebo pomocí pozdějších funkcí, které Vám ještě zjistí, zda proběhla chyba či nikoliv:
function Konec : boolean; assembler; asm mov pozadavek.prikaz,$e call proved xor ax,ax jc @chyba inc ax @chyba: end; function Reset : boolean; assembler asm mov subkanal+0.byte,2 call iooutput xor ax,ax jc @chyba inc ax @chyba: end; function StopAudio : byte; assembler; asm mov pozadavek.prikaz,$85 call proved end; function ResumeAudio : byte; assembler; asm mov pozadavek.prikaz,$88 call proved end; function Vypocti : longint; assembler; {data vložíme do DX:AX} asm mov bx,ax {BH=vteřiny, BL=snímky, DX=minuty} mov ax,60 mul dx mov dx,ax {minuty * 60} xor ah,ah mov al,bh add dx,ax {+vteřiny} mov ax,75 mul dx xor bh,bh add ax,bx (* 75 + snímky *) adc dx,0 sub ax,150 (* -150 *) sbb dx,0 (* DX:AX=logický sektor *) end; function PlayAudio : byte; assembler; asm mov pozadavek,prikaz,$84 {hraj} mov pozadavek+$d.byte,0 {HSB formát} mov ax,informace.prvni+0.word mov dx,informace.prvni+2.word call vypocti mov pozadavek+$e.word,ax mov pozadavek+$10.word,dx mov ax,informace.posl+0.word mov dx,informace.posl+2.word call vypocti mov ax,pozadavek+$e.word mov dx,pozadavek+$10.word mov pozadavek+$12.word,ax mov pozadavek+$14.word,dx call proved end;
Definitivní funkce najdete už zde: samozřejmě, pokud Vám nejde o chybu, můžete si předchozí funkce přepsat jako procedury, neboť z nich nezjisíte chybu. Tak to jde alespoň u STOP a RESUME. PLAY bude trochu horší. Takže nic nezkazíte, když budete používat jen tyto tři následující funkce (zvláště ta poslední je přímo lahoda):
function CDstop : boolean; assembler asm call stopaudio xor ax,ax jc @chyba inc ax @chyba: end; function CDresume : boolean; assembler; asm call resumeaudio xor ax,ax jc @chyba inc ax @chyba: end; function CDplay(Skladba : byte) : boolean; assembler; asm mov subkanal+0.byte,$a {audio informace} call ioinput jc @chyba mov al,subkanal+1.byte mov informace.minstop,al mov al,subkanal+2.byte mov informace.maxstop,al {poslední stopa} mov ax,subkanal+3.word mov informace.posl+0.word,ax mov ax,subkanal+5.word mov informace.posl+2.word,ax mov al,skladba mov subkanal+0.byte,$b {informace o stopě} mov subkanal+1.byte,al call ioinput jc @chyba mov al,subkanal+6.byte {řídící byte stopy} and al,$d0 {vymaže COPY bit} cmp al,$40 {audio nebo data?} je @chyba mov ax,subkanal+2.word mov informace.prvni+0.word,ax mov ax,subkanal+4.word mov informace.prvni+2.word,ax call stopaudio call playaudio jc @chyba mov ax,1 jmp @konec @chyba: xor ax,ax @konec: end;
Pokud Vám ovšem stačí jen to zmiňované (asi jste fakt skromní) zasouvání a vysouvání dvířek :-), tak i na to jsou zde funkce (pravda, pochopit celý systém asi budete muset sami, sám toho totiž o ovládání CD-ROM moc nevím):
function Zasunout : boolean; assembler asm mov subkanal+0.byte,5 call iooutput xor ax,ax jc @chyba inc ax @chyba: end; function Vysunout : boolean; assembler asm mov subkanal+0.byte,0 call iooutput xor ax,ax jc @chyba inc ax @chyba: end;
Kdyby Vám to nefungovalo a nevěděli jste, co s tím, nebo Vám to bylo dokonce málo, doporučuji se podívat na originál jednotku CDROM.ASM od Guenthera Klaminga. Kdybyste jí snad nenašli, tak zkuste hledat jednotku CDROM.PAS od Grega Estabrookse, která je psaná převážně v Pascalu. No, tak to bychom měli. Příště se podíváme, jak si udělat svůj vlastní bootovací "operační systém" (zatím z diskety) a pak už budu mít snad konečně volno :-)