Assembler a systémJan Hlavatý
... a ještě k multitaskingu Vítejte v druhé části naší malé exkurze do vnitřku multitaskingu Amigy.
Minule jsme si uvedli některé základní skutečnosti, se kterými se ohledně
multitaskingu setkáváme. Jakákoli teorie je bez patřičných příkladů praktického
využití poněkud nestravitelná, takže jsem pro vás připravil prográmek na
hraní... Navíc jsem nakreslil nějaké ilustrační obrázky k seznamům, protože z
textu není moc vidět, jak to funguje. Těch obrázků teď bude asi mnohem víc, aby
ten text nevypadal tak suše... Pokud se divíte, proč tak málo vysvětluji
assembler jako takový a radši se hrnu rovnou do systému, důvod je nasnadě: O
assembleru jako takovém se dneska dá ledacos sehnat (myslím tím různé knížky
atd.), i když to většinou postrádá spoustu důležitých informací. Tyto "díry"
jsem se pokusil ucpat v prvních dílech seriálu... Narozdíl od assembleru však o
operačním systému neseženete skoro nic - jediná knížka kterou jsem viděl se
zabývá výlučně OS 1.3 a je plná příkladů jak se nemá programovat... Problém je,
že kvantum informací o OS Amigy vydá na knihu tlustou 30 cm, pokud je podáno
dostatečně vyčerpávajícím způsobem. Pro tento seriálek jsem tedy zvolil taktiku
jednotlivých úzce zaměřených témat. V každé takové kapitolce se pokouším shrnout
"pozadí" dané tématiky a pak popsat jednotlivé funkce s ní související...
Neočekávejte tedy nějaký kompletní výpis všeho o všem... spíš "jak na to,
když..."
Takže teď k výpisu programu "message.s". Účelem programu je ilustrovat
komunikaci mezi procesy, kterou jsem popisoval minule. Konkrétně posílání
messages přes veřejný port. Jak se to používá? Program je v podstatě schopen
fungovat dvěma způsoby:
1. Jako přijímač message,
2. Jako vysílač message.
Abyste to vyzkoušeli, otevřete si 2 okna CLI (2 různé procesy) a v obou spusťte
program "message" (patřičně přeložený) Program po spuštění automaticky hledá
veřejný port se jménem "AHOJ_PORT". Pokud takový port v systému přítomen není,
program ho vytvoří a bude fungovat jako přijímač - vypisovat došlá messages
dokud nezmáčknete ctrl-c. Pokud "AHOJ_PORT" v systému existuje, program funguje
jako vysílač - pošle na něj message s textem a skončí. Jednoduché, ne? Původně
to měly být 2 různé programy lišící se jen v malém úseku kódu, tak jsem je
spojil do jednoho abyste toho nemuseli opisovat 2x víc... Takže: spustili jste?
Situace by měla vypadat jako na obrázku. Text, který vypíše přijímací strana
pochází od vysílajícího, i když ve skutečnosti je obsažen také v přijímacím
programu (anžto je to ten samý pro ram). Pokud nevěříte, zkuste si udělat dvě
různé verze programu message lišící se od sebe vysílaným textem (stačí přepsat
text za návěstím "posilanytext", uložit pod jiným jménem (např. "message2.s" a
přeložit (mimochodem, už je PhxAss v4.20).
Ještě k veřejným portům: Pozor na ně! Veřejný port spravuje obvykle nějaký jiný
program a může si s ním dělat co chce - tedy například v jakémkoliv okamžiku ho
ze systému odstranit a zrušit! Pokud tedy chcete na veřejný port něco posílat,
musíte učinit jistá bezpečnostní opatření . Musíte se ujistit že v okamžiku kdy
něco posíláte port ještě existuje a že nezmizí dřív než tam stihnete něco
poslat. Kdyby zmizel, nastala by nepříjemná situace - zhroucení systému, v
lepším případě se vám vaše message už nikdy nevrátí a budete na něj čekat do
vánoc (a to ještě bůh ví, kterého roku). Taktéž si musíte dát pozor když VY jste
vlastníkem veřejného portu - než port úplně zrušíte, musíte ho odstranit ze
seznamu veřejných portů (aby už nebyl k nalezení) a POTOM ještě zpracovat nebo
alespoň VRÁTIT messages, které ještě na portu zbyly. Teprve potom je možno
bezpečně port zničit... Pokud chcete vidět jak to všechno provést, jukněte do
"message.s" - jsou tam dvě rutiny: SoukromyMod a VerejnyMod. VerejnyMod je úsek
kódu pro přijímací mód, SoukromyMod je vysílací. U vysílání si všimněte že
dvojice volání FindPort() a PutMsg() je uzavřena mezi Forbid() a Permit().
Funkce Forbid() pozastaví přepínání tasků a tedy běh všech ostatních tasků kromě
vašeho, Permit() přepínání tasků zase povolí. To je dobré především na situace
kde by mohly vzniknout konflikty mezi tasky/procesy. V našem případě by se bez
Forbid()/Permit() mohlo stát, že těsně po tom, co náš task dokončí funkci
FindPort() s tím že port našel, druhy task vlastnící dotyčný port by ho mohl
právě v tom okamžiku zlomyslně zrušit - ještě než bychom na něj poslali message
pomocí PutMsg(). My bychom pak poslali message na neexistující port, a máme
zaděláno na havárii systému... Protože jsme však vychytralí, zakážeme na
kritickou dobu mezi FindPort() a PvtMsg() přepínání tasků a port nám zmizet
nemůže ani kdyby chtěl (protože ostatní tasky stojí). Jasný?
Teď se podíváme na přijímání a vlastnictví veřejného portu. Než začnete čekat na
příchod message na váš port, je dobré vždycky zkusit, Jestli tam už nějaké
nejsou, protože funkce Wait() nějakou tu chvilku zabere a proč čekat, když mi
třeba přišlo víc messages najednou? Chceme přece být příslovečně rychlí... Takže
obecně pro přijímání platí: Zkoušet GetMsg() a zpracovávat došlé messages dokud
funkce GetMsg() nevrátí NULL (0). Pak teprve začít uvažovat o nějakém Wait().
Tenhle mechanismus funguje skoro všude - třeba při zpracování událostí v okně
intuitionu. Další dobrý "grif" je vrátit message co nejdřív to bude možné, aby
se nic zbytečně nezdržovalo... Pokud se podíváte do "message.s" na VerejnyMod,
vidíte jak si dát pozor na zapomenutá message na odstraňovaném portu. Další
ptákovinkou zde zabudovanou je primitivní kontrola jestli je to moje message
nebo ne. Tohle jsem zabudoval kvůli ARexxu - zmatený uživatel by klidně mohl
pomocí ARexxu poslat message někam kde vůbec nemá co dělat. Můžete to zkusit.
Pusťte "message" a ARexx. Když zadáte v CLI: rx "say show(p,,ax)" vypíše se seznam veřejných portů v systému, z nichž však žádný nemusí být
nutně port ARexx-ový. Bludné ARexxové message si na port "AHOJ_PORT" můžete
poslat třeba takto: rx "address AHOJ_PORT baf!" Protože jsme tento případ částečně v "message" ošetřili, nezhroutíme se, což
ale nemusí být nutně případ všech veřejných portů v systému takže: neposílat na
neznámé porty! Pokud má aplikace ARexxový port, jistě je jeho jméno někde
napsáno...
Jak jste si jistě všimli, vypisuje "message" hlášky do okna CLI pomocí šikovné
rutinky, která funguje na OS 2.0... Ne že by něco podobného nešlo i na 1.3, ale
tam by to člověku dalo mnohem víc práce (pomocí funkcí Output(), RawDoFmt(),
Write() a ještě by si musel udělat buffer na text, jinak je výpis textu
stráááášně pomalý). Takže - tahle rutinka se vám bude určitě hodit... Umí totiž
víc než jen opsat přesně text - umí i vypisovat řetězce a čísla v různých
formátech... V podstatě jde o variantu standardní funkce jazyka C - printf().
Jako základní parametr zadáte funkci tzv. formátovací řetězec - tj. text, ve
kterém jsou označena místa kde se má vypsat nějaká hodnota a jakým způsobem.
Řetězcem samozřejmě rozumíme řadu znaků ukončenou nulovým bajtem (tzv. C-string,
protože tento způsob ukládání řetězců je známý z jazyka C). Všimněte si způsobu
předávání parametrů: Hodnoty jsou ukládány jako LONGy na zásobník, odkud si je
funkce přečte. Ukládají se od poslední k první, čímž pak na zásobníku vznikne za
návratovou adresou podprogramu skupina LONGů s hodnotami od první do poslední.
Tento způsob předávání parametrů je obvyklý v jazyku C umožňuje rekurzivní
volání podprogramů. Samozřejmě po návratu z podprogramu musíte nahromaděné
hodnoty ze zásobníku zase odstranit - jinak to špatně dopadne (zhroucení při
prvním RTS...) - pozor na to může to být zdrojem nepříjemných chyb... když už
mluvíme o funkcích v C - konvence je (jako v celém OS Amigy) že funkce nesmí
změnit žádný registr kromě D0, D1, A0, A1. Návratové hodnoty se předávají
obvykle v D0. (což Printf v message.s nesplňuje, ale já to vím a tak si dávám
pozor). Lepší verzi vám ukáže prográmek "printf.s"...
Ve formátovacím řetězci se místa pro vložení nějakých hodnot označují pomocí
znaku %", za kterým následuje popis typu hodnoty a způsobu výpisu. V OS 2.04+
jsou možné tyto kombinace:
%% - píše se jako jeden znak
"%", a y bylo vůbec možné tento znak vypsat
Za znakem % může následovat znak "-", který znamená že se má zarovnávat zleva
pokud je požadovaná délka celého kousku textu větší než skutečně vyjde ze
zadaných argumentů. Následuje desítkové číslo udávající maximální šířku položky.
Pokud je první číslice "0", bude k doplnění vlevo použita nula místo mezery. Při
výpisu stringu lze použít dvě čísla oddělená tečkou. Druhé číslo pak znamená
kolik znaků se má maximálně zobrazit z vypisovaného stringu. Za čísly následuje
modifikátor velikostí dat "I". Obvykle se argumenty předávají na zásobníku jako
LONGy (32-bitové hodnoty) aby zásobník zůstal long-aligned. Znak "I" na to
upozorňuje rutinu, protože ne z něj vy rutina načítala parametry jako WORDy.
Pozor na to! Nakonec následuje písmeno udávající jaký typ hodnoty se má vypsat.
Možné typy jsou:
"b" - BSTR (speciální formát stringu, který zůstal v DOSu jako dědictví po
starých verzích dos.library, napsaných v BCPL. BSTR musí začínat na adrese
dělitelné 4, protože ukazatel na začátek BSTR se předává jako adresa vydělená 4.
Na místě kam tato adresa ukazuje se nachází BYTE, ve kterém je uložena délka
stringu (0..255), za ním následují znaky. Někdy bývá BSTR ukončen nulou jako
C-string, ale není to zaručeno! S BSTR se setkáte pokud se budete rejpat v dosu
okolo handlerů a podobně...
"d" - desítkové číslo se znaménkem
"u" - desítkové číslo bez znaménka
"x" - hexadecimální číslo
"s" - string
"c" - jeden znak
Funkce VPrintf() z dos.library chce jako argumenty adresu formátovacího stringu
v D1 a adresu pole argumentů v D2. V rutince Printf v prográmku "printf.s" jsem
předávání argumentů a stringu vyřešil pomocí zásobníku jako v C. Před voláním
Printf se na zásobník uloží adresa formátovacího stringu a případné argumenty.
Rutina si vyzvedne adresu formátovacího stringu, dá ji do D1, a adresu na data
za ním předá jako pointer na pole argumentů v D2. Když pak VPrintf() vypisuje
formátovací string, pokaždé narazí-li na značku "%" vezme si z pole argumentů
hodnotu o patřičné velikostí (v našem případě LONG) a posune se v poli dál.
Funkce Printf tak jak je uvedena v "printf.s" nemění žádné registry a bude se
vám jistě hodit... ; ===================================
; printf.s - zkráceno kvůli nedostatku místa
; ===================================
Printf movem.l d0-d2/a0-a1/a6,-(sp) ;schovat
lea (7*4)(sp),a0
move.l (a0)+d1
move.l a0,d2
move.l _DOSBase,a6
jsr _LVOVPrintf(a6)
movem.l (sp)+,d0-d2/a0-a1/a6 ;obnovit
rts ; ===================================
; demo na posilani messages
; vyzaduje OS 2.04+
; ===================================
incdir "INCL:"
include "exec/exec lib.i"
include "dos/dos lib.i"
include "exec/ports.i"
include "dos/dosextens.i"
;definice formatu meho message:
RSRESET
moje_message rs.b MN_SIZE
;standard. message
moje_id rs.11 ;ma byt "MOJE"
moje_text rs.11 ;pointer na text
moje_SIZE rs.10 ;velikost toho vseho
section prg,code
START move.l 4.w,ab
lea dosname(pc),a1
moveq #37,d0
jsr _LVOOpenLibrary(a6)
move.l d0,_DosBase
bne.s .mamdos
moveq #20,d0
rts
.mamdos pea vyrabim(pc)
bsr Printf
addq.l #4,sp
move.l 4.w,a6
jsr _LVOCreateMsgPort(a6)
move.l d0,port
beq fuj1
move.l d0,a1
moveq #0,d0
move.b MP_SIGBIT(a1),d1
bset d1 ,d0
bset #SIGBREAKB_CTRL_C,d0
move.l d0,waitmask
sf flag_public
sr _LVOForbid(a6)
lea portname(pc),a1
jsr _LVOFindPort(a6)
tst.l d0
bne .uztamje
;vytvorit verejny (public) port...
move.l port,al
move.l #portname LN_NAME(a1)
jsr _LVOAddPort(a6)
st flag_public
.uztamje jsr _LVOPermit(a6)
tst.b flag_public
beq never
pea portname(pc)
pea verejny(pc)
bsr Print
addq.l #8,sp
bsr VerejnyMod
bra .konec
.never bsr SoukromyMod
.konec pea rusim(pc)
bsr Printf
addq.l #4,sp
move.l 4.w,a6
move.l port(pc),a0
jsr _LVODeleteMsgPort(a6)
.fuj1 move.l DosBase(pc),a1
move.l 4.w,a6
jsr _LVOCIoseLibrary(a6)
moveq #0,d0
rts
VerejnyMod sf flag_exit
.main tst.b flag_exit
beq .neexit
rts
.neexit pea ahoj(pc)
bsr Prinft
addq.l #4,sp
move.l 4.w,a6
move.l waitmask,d0
jsr _LVOWait(a6)
btst #SIGBREAKB_CTRL_C,d0
beq .zkus
move.l 4.w,a6
move.l port,a1
jsr _LVORemPort(a6)
st flag_exit
.zkus move.l port,a0
jsr _LVOGetMsg(a6)
move.l a0,aktmessage
beq .main
move.l aktmessage(pc),a5
cmp.l #MOJE,moje_id(a5)
beq .jemoje
pea nenimoje(pc)
bsr Printf
addq.l #4,sp
bra .vratto
.jemoje pea zprava(pc)
bsr Pnntf
addq.l #4,sp
move.l moje_text(a5),d0
beq .vratto
move.l d0,-(sp)
pea jetam(pc)
bsr Printf
addq.l #8,sp
.vratto move.l aktmessage(pc),a1
move.l 4.w,a6
jsr _LVOReplyMsg(a6)
bra.s .zkus
SoukromyMod
pea posilam(pc)
bsr Printf
addq.l #4,sp
;vyplnit message
lea mojemessage(pc),a5
move.l port,MN_REPLYPORT(a5)
move.w #moje_ŠIZE,MN_LENGTH(a5)
move.l #MOJE,moje_id[a5]
move.l #posilanytext,moje_text(a5)
;pokusit se poslat na verejny port...
move.l 4.w a6
jsr _LVOForbid(a6)
lea portname(pc),a1
jsr _LVOFindPort(a6)
tst.l d0
bne.s .jeport
jsr _LVOPermit(a6)
pea portneni(pc)
bsr Printf
addq.l #4,sp
bra .konec
.jeport move.l d0,a0
lea mojemessage(pc),a1
jsr _LVOPutMsg(a6)
jsr _LVOPermit(a6)
pea cekam(pc)
bsr Printf
addq.l #4,sp
move.l 4.w,a6
move.l port,a0
jsr _LVOWaitPort(a6)
move.l port,a0
jsr _LVOGetMsg(a6)
.konec rts
Printf lea 4(sp),a0
move.l (a0)+d1
move.l a0,d2
move.l _DosBase(pc),a6
jmp _LVOVPrintf(a6)
cnop 0,4
_DosBase dc.l 0
port dc.l 0
waitmask dc.l 0
aktmessage dc.l 0
dosname dc.b "dos.library",0
portname dc.b "AHOJ_PORT",0
mojemessage dcb.b moje_SIZE,0
vyrabim dc.b "Vyrabim port",10,0
verejny dc.b "Pridavam ho do systemu"
dc.b " jako verejny port"
dc.b "%s",10,0
rusim dc.b "Rusim port...",10,0
ahoj dc.b "Cekam na message"
dc.b "nebo ctrl-C...",10,0
nenimoje dc.b "Prislo mi cizí message!?"
dc.b "POMOOOOC!",10,0
zprava dc.b "** Prislo mi message **"
dc.b 10,0
jetam dc.b "Je v nem text: °/s"
dc.b 10,0
flag_public dc.b 0
flag_exit dc.b 0
posilam dc.b "Posilam message...",10,0
cekam dc.b "Cekam na vraceni"
dc.b "message...",10,0
posilanytext dc.b "Halooo halooo, tady"
dc.b "je jiny proces!",0
portneni dc.b "Verejny port uz "
dc.b "neexistuje!",10,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
|