DevicesJan Hlavatý
Obsluha softwarových zařízení Amigy Vítejte u dalšího pokračování série o systému a assembleru. Vinou zákeřné
nemoci jsem byl nucen jeden díl vynechat. Nepanikařte, nic se neděje, cenné
informace si s sebou do hrobu nevezmu... Takže: Z minula už něco víte o
multitaskingu a komunikaci mezi jednotlivými triky pomocí portů a messages. V
tomto povídání se podíváme na praktické využití těchto prostředků v obsluze
softwarových zařízení Amigy - devices. Co je to device?
Device je ve své podstatě program, ovládající většinou nějaký hardware.
Device je schopno vykonávat určité příkazy specifické pro daný typ ovládaného
hardware (nebo software) způsobem jednotným pro všechna devices v systému. Běží
obvykle jako samostatný task (např. "trackdisk.device" ovládající disketové
mechaniky), v některých případech rutiny device běží jako ovladače přerušení
(např. "serial.device" ovládající přenos dat po sériovém portu). Některá
základní systémová devices jsou umístěna přímo v ROM systému, jiná jsou uložena
na systémovém disku v adresáři "DEVS:". Pokud se podíváte do tohoto adresáře,
najdete tam například u OS 3.1:
- clipboard.device: Umožňuje výměnu dat mezi programy. Například když v DPaintu
nakreslenou ikonu pomocí funkce "copy" přenesete do clipboardu a následně ji
vložíte do programu "iconedit" funkcí "paste", děje se to prostřednictvím tohoto
device.
- mfm.device: Nadstavba trackdisk.device,umožňuje čtení a zápis disket
naformátovaných pro PC (je využíváno pro PC0:)
- parallel.device: Zařízení obsluhující paralelní port. Přes něj probíhá např.
tisk na tiskárnách s rozhraním Centronics (většina). Pozor! Obsluhu tiskáren má
na starosti jiné zařízení ("printer.device"), parallel.device se pouze stará o
přenos dat mezi počítačem a hardwarem připojeným na paralelní port.
- printer.device: Toto zařízení obsluhuje tiskárny, a to standardním způsobem
pro všechny podporované tiskárny. Je schopno vytisknout kus textu a dokonce i
kus grafiky! Tomuto zařízení stačí sdělit "vytiskni mi tohle", a ono už samo
podle nastavených preferencí a ovladače tiskárny provede akci způsobem
specifickým pro konkrétní nastavenou tiskárnu. Při tisku textu lze v textu
používat standardní ANSI řídící kódy tiskáren, které jsou automaticky ovladačem
překládány na konkrétní řídící kódy tiskárny.
- serial.device: Toto zařízení obsluhuje sériový port. Nejobvyklejším použitím
tohoto zařízení je komunikace s modemem. Toto device umí nejen posílat data
nastavenou přenosovou rychlostí, ale postará se i o handshaking, ať už
softwarový (xON/xOFF) nebo hardwarový (RTS/CTS).
V ROM jsou umístěna další devices:
- gameport.device: Toto device obsluhuje joystickové porty - zprostředkovává
čtení myši a joysticku z obou portů.
- timer.device: Toto důležité device slouží k měření časových intervalů několika
různými způsoby.
- keyboard.device: Obsluhuje klávesnici - umožňuje načítat stisknuté klávesy tak
jak jsou postupně mačkány a uvolňovány, nebo načíst celou mapu stisknutých
kláves. Navíc umožňuje instalovat handler, který je schopen po stisku
ctrl-amiga-amiga (RESET) urychleně provést nějakou akci. Toho využívá například
trackdisk.device - tam reset handler zakáže jakýkoliv zápis na disk, aby nemohlo
dojít k poškození formátu právě zapisované stopy.
- input.device: Toto je bod, kde se sbíhají všechny vstupní události jako jsou
pohyby myši, stisky kláves, hlášení o vložení diskety a pod., které pak odtud
putují do systému - tedy především k intuitionu. Toto zařízení umožňuje
instalovat handler, který bude prohlížet všechny přicházející události (input
events), provádět různé akce a případně některé ze vstupních událostí
odstraňovat.
- ramdrive.device: Toto zařízení umožňuje používání RAM-disku, který je schopen
přežít reset (označuje se "RAD:").
- trackdisk.device: Obsluhuje všechny čtyři možné disketové mechaniky, a to
nejen normální 3.5" DD, ale i 5.25" a 3.5" HD. Umožňuje zapínat a vypínat motory
mechanik, formátovat, načítat a zapisovat stopy ve standardním amiga formátu,
zjišťovat informace o typu mechanik a navíc má speciální "raw" funkce,
umožňující číst a zapisovat stopy i v jiném, nestandardním formátu.
- carddisk.device: Existuje u Amig vybavených PCMCIA slotem. Umožňuje používat
RAM-kartu stejným způsobem jako disketu.
- scsi.device: Obsluhuje harddisk na AT-BUS sběrnici v A600/A1200. Navenek se
tváří jako SCSI zařízení.
- console.device: Toto zařízení je schopno vytvořit znakově orientovaný textový
vstup/výstup, jak ho známe např. z oken Shellu. Podporuje standard ANSI. Softwarová obsluha devices
Pokud jde o vnitřní strukturu, jsou devices podobná sdíleným knihovnám.
Stejně jako ony mají svou bázi se skokovými vektory na negativních offsetech a
datovými strukturami na pozitivních offsetech. Volání funkcí devices je však
koncipováno odlišně vzhledem k tomu, že device bývá obvykle samostatný task
který přijímá příkazy od ostatních tasků a postupně je zpracovává. Naproti tomu
u knihoven volaná funkce proběhne přímo v tasku který ji volá jako podprogram.
Existují samozřejmě výjimky - např. "timer.device" má několik "knihovních"
funkcí navíc pro práci s časem. Taktéž tzv. quick-io mód předávání příkazů je
výjimkou - viz dále.
K tomu, abyste mohli poslat nějakému device příkaz, potřebujete tzv. IORequest
strukturu. To je v podstatě message rozšířené o několik údajů jako je příkaz,
parametry příkazu, místo pro návratový chybový kód a identifikace konkrétní
jednotky (unit) zařízení které je příkaz určen. Jednotlivá konkrétní devices si
pak k této "základní" datové struktuře přidávají další položky specifické pro
toto zařízení. Každé device tedy obvykle mívá svou vlastní definici struktury
IORequest.
Takto vypadá minimální IORequest struktura, jak ji vyžaduje exec.library: (viz
exec/io.i)
STRUCTURE IO, MN_SIZE ;strukt. message
APTR IO_DEVICE ;pointer na bázi device
APTR IO_UNIT ;unit device (interní)
UWORD IO_COMMAND ;příkaz pro device
UBYTE IO_FLAGS ;spec. příznaky
BYTE IO_ERROR ;návratový kód
LABEL IO_SIZE ;velikost min. struktury Tato minimální IO struktura je pak standardně rozšířena o tato pole:
ULONG IO_ACTUAL ;aktuální počet přenesených byte
ULONG IO_LENGTH ;požadovaný počet přenesených byte
APTR IO_DATA ;pointer na io buffer
ULONG IO_OFFSET ;offset od začátku média
LABEL IOSTD_SIZE ;velikost IOSTD struktury Většina devices používá IOSTD strukturu rozšířenou ještě o soukromá pole
daného device. Význam standardních položek IOSTD je následující:
- message: normální message s řádně vyplněným MN_REPLYPORTem atd. Toto message
je použito pro předávání IORequestu mezi volajícím taskem a device. Po ukončení
zpracování IORequestu je message vráceno volajícímu tasku pomocí ReplyMsg().
- IO_DEVICE, IO_UNIT: Tato dvě pole jsou vyplněna při otevření device. IO_DEVICE
obvykle obsahuje bázi device (podobně jako bázi knihovny), odtud lze získat bázi
device v případě že device má nějaké "knihovní" funkce - ty se pak volají stejně
jako funkce sdílených knihoven pomocí offsetu a této báze. IO_UNIT je privátní
pointer na jednotku daného device. Pokud jsou datové struktury (nebo jejich
část) jednotek daného device veřejně známy, lze k nim přistupovat přes tento
pointer.
- IO_COMMAND: Sem vyplňujete kódy příkazů pro device. Tyto kódy jsou popsány u
každého device zvlášť a stejný kód nemusí nutně dělat u různých devices to samé.
Obvykle existují určité skupiny devices s velmi podobnou funkcí a stejnou sadou
příkazů - např. skupina "serial.device", kam patří všechny ovladače sériových
portů (např. "8n1.device", "BaudBandit.device", "artser.device", nebo skupina
"trackdisk.device", kam patří zařízení pro obsluhu disket v různých formátech
(např. "diskspare.device", "mfm.device"). Tím že jsou alternativní devices
stoprocentně kompatibilní s původní verzí device v systému je zajištěno snadné
rozšiřování hardware a vylepšování software. Pozor, IO_COMMAND je WORD! Kdysi
jsem zapomenuvše na to při svých experimentech udělal pár ošklivých těžko
odhalitelných chyb!
- IO_FLAGS: Některé speciální příznaky používané různě podle typu device, plus
příznak IOF_QUICK (viz dále)
- IO_ERROR: Návratový kód příkazu 0 znamená úspěch, jiná hodnota chybu. Stejnou
hodnotu vrací funkce DoIO() po jejím provedení (je odtud).
- IO_ACTUAL: Obsahuje obvykle počet skutečně přenesených byte (např. než vznikla
chyba)
- IO_LENGTH: Sem se obvykle vyplní počet byte který se má přenést mezi bufferem
a device.
- IO_DATA: Sem vyplníte adresu bufferu který budete používat.
- IO_OFFSET: Určuje začátek datové oblasti na device
Konkrétní postup při používání device je tedy následující:
1. Připravíte si odpovídající IORequest strukturu, vyplníte adresu portu pro
odpověď v message které je součástí IORequestu. Pokud port ještě nemáte, musíte
si ho vytvořit (např. funkcí CreateMsgPort()). Tento port bude použit po
ukončení příkazu k odeslání IORequestu zpět volajícímu tasku. Tím je zajištěna
možnost čekání na dokončení příkazu pomocí funkce Wait()..
2. Otevřete device pomocí funkce execu OpenDevice(). Tato funkce vyžaduje v A0
jméno otevíraného device (např. "trackdisk.device"), v D0 číslo jednotky (unit)
u devices, v A1 ukazatel na váš IORequest a v D1 příznaky (flags) které jsou
specifické pro dané device (např. u "trackdisk.device" lze pomocí flags
zajistit, aby v případě že odpovídající disketová mechanika není normální 3.5"
DD se device neotevřelo a vrátila se chyba). Voláním funkce OpenDevice se vyplní
položky IO_DEVICE a IO_UNIT ve vašem IORequestu.
3. Do položky IO_COMMAND vyplníte kód příkazu, který chcete aby device provedlo.
Většina devices podporuje základní příkazy jako CMD_READ a CMD_WRITE. Navíc
ještě mívá každé device několik specifických příkazů, které nejdou
"zaškatulkovat" mezi standardní obecné příkazy. Další položky IORequestu pak
vyplníte parametry podle toho, jaký příkaz provádíte.
4. Pro provedení příkazu zavoláte na IORequest funkci execu DoIO(). Tato funkce
vyžaduje v A1 ukazatel na IORequest s vyplněným příkazem. Vrací chybový kód
(0=úspěch).
5. Po skončení práce s device musíte device uzavřít funkcí exec CloseDevice().
Opět v A1 je pointer na IORequest. V tomto okamžiku už nesmí device zpracovávat
žádný váš příkaz (což při použití DoIO() ani není možné)
Toto je klasický způsob volání příkazů device. Pro některé případy je však
potřeba volat příkazy device asynchronně. Například si vezměte situaci: Chcete
načíst znak ze sériového portu. Pokud vám řekněme do 10 sekund žádný znak
nepřijde, chcete s tím přestat a stěžovat si uživateli, že vám nic nepřišlo.
Abyste mohli něco takového udělat, potřebujete mít dva IORequesty jeden pro
"serial.device" a druhý pro "timer.device". Zařízení "serial.device" nařídíte
aby načetlo jeden znak, zařízení "timer.device" požádáte o odměření časového
intervalu 10 sec. Pak už jenom koukáte, který IORequest bude dokončen dříve -
když čtení znaku, je všechno OK - přerušíte čekání na druhém IORequestu a jste
hotovi. Pokud je dřív hotov IORequest od "timer.device", prošel časový interval
přerušíte IORequest na "serial.device" a uživateli nahlásíte chybu.
Aby bylo toto realizovatelné, potřebujete další funkce execu:
SendIO() - pošle IORequest na device a okamžitě se vrátí. Device současně začne
zpracovávat požadovaný příkaz. V A1 je ukazatel na IORequest, funkce nic
nevrací.
CheckIO() - Funkce se podívá, jestli už IORequest jehož adresa je v A1 není
náhodou dokončen. Vrací nulu pokud ještě zpracovávání IORequestu nebylo
ukončeno. Pokud už byl IORequest dokončen, je momentálně umístěn na vašem portu
a je třeba ho odtamtud odstranit funkcí GetMsg() (pokud už se tak nestalo).
AbortIO() - Požádá device o přerušení zpracování IORequestu v A1. Pozor! Po
zavolání této funkce je třeba ještě počkat na dokončení IORequestu!
WaitIO() - Počká na dokončení zpracovávání IORequestu (v A1) pomocí funkce
Wait(). To je vhodné pokud čekáte na dokončení jediného IORequestu. Pokud jich
je více, je lepší nastavit messages v IORequestech tak aby replyporty ukazovaly
na stejný port a čekat na tomto portu.
V souvislosti s těmito funkcemi se zmíním také o tzv. quick-io (zrychleném)
způsobu volání příkazů pomocí IORequestu. Normální způsob spočívá v poslání
message jinému tasku, který po dokončení jeho zpracování message vrátí funkcí
ReplyMsg() zpět na replyport. To má obvykle za následek přepínání tasků, což
nějakou dobu trvá. Pokud je příkaz device dostatečně jednoduchý aby byl
proveditelný okamžitě a nezávisle na čase (např. dotaz na "serial.device", kolik
má v bufferu ještě načtených znaků, což je pouhé vrácení obsahu interní proměnné
"serial.device"), pak je výhodné použít právě quick-io mód. To uděláte takto:
- V IORequestu v položce IO_FLAGS nastavíte před voláním BeginIO() bit
IOB_QUICK. Tak device pozná, že byste chtěli použít quick-io. Pozor! Zdaleka ne
všechny příkazy device lze provést v módu quick-io!
- Zavoláte na IORequest funkci BeginIO(). Po skončení této funkce se podíváte do
IO_FLAGS, je-li bit IOB_QUICK stále ještě nastaven. Pokud ano, je příkaz už
proveden! Pokud byl IOB_QUICK vynulován, nelze tento příkaz provést v quick-io a
IORequest bude zpracován normálním způsobem, tj. zrovna se zpracovává a musíte
počkat na jeho dokončení a vrácení na váš port. Mód quick-io je tedy podobný
volání knihovních funkcí, protože zpracování IORequestu probíhá na vašem tasku a
ne na tasku device.
Tolik teorie, a příště uvedu seznam některých devices s jejich příkazy a
podíváme se na jejich praktické využití. Zatím tady máte malinký ale plně
funkční (a užitečný) příkládek na otevírání devices. Nevolají se v něm žádné
příkazy device, ale je tam ukázána technika přístupu k datovým strukturám
jednotlivých units "trackdisk.device". Prográmek zapne "neťukající" mód u všech
čtyř možných mechanik. Tento mód "trackdisk.device" existuje u OS 2.04+.
Mechaniky po spuštění prográmku ještě asi dvakrát kliknou, než
"trackdisk.device" zjistí, že nastala nějaká změna. Zároveň si všimněte "nové"
syntaxe zápisu adresových módů, která se používá u 68020+ (a lze ji klidně
použít místo staré). Všechny novější assemblery by měly novou syntaxi umět... ;==========================
; Tento program zmeni mod klikani
; disk. mechanik u OS 2.04+
;==========================
_LVOOpenDevice = -444
_LVOCloseDevice = -450
incdir "include:"
include "devices/trackdisk.i"
section prg,code
START moveq #0,D0
bsr.b ModifyUnit
moveq #1,D0
bsr.b ModifyUnit
moveq #2,D0
bsr.b ModifyUnit
moveq #3,D0
bsr.b ModifyUnit
moveq #0,D0
rts ModifyUnit
move.l (4),W,A6
lea (tdname).L,A0
lea (iorq).L,A1
moveq #1,D1
jsr (_LVOOpenDevice,A6)
tst.l D0
bne.b .none
lea (iorq).L,A1
move.l (IO_UNIT,A1),A0
bset #0,(TDU_PUBFLAGS,A0)
jsr (_LVOCloseDevice,A6)
.none rts
tdname dc.b "trackdisk.device",0
iorq dc.b IOTD_SIZE,0 Vytlačiť článok
Pozn.: články boli naskenované ako text a preto obsahujú aj zopár chýb. Taktiež neručíme za zdrojové kódy (Asm, C, Arexx, AmigaGuide, Html) a odkazy na web. Dúfame, že napriek tomu vám táto databáza dobre poslúži.
Žiadna časť nesmie byť reprodukovaná alebo inak šírená bez písomného povolenia vydavatela © ATLANTIDA Publishing
none
|