Sekalaiset   Sivukartta
MorphOS-ohjelmointia
Ilkka Lehtoranta <ilkleht@isoveli.org>

MorphOS tuo ohjelmoijalle uutta päänvaivaa. Uusi uljas PPC-käyttöjärjestelmä tuo tehoa ohjelmointiin, mutta kaikki ei olekaan enää niin kuin ennen. Vanhojen 68k-ohjelmien täytyy pyöriä sulassa sovussa uusien PPC-ohjelmien kanssa. Jotta yhteiselo ei päättyisi katastrofiin, täytyy PPC-ohjelmoijan antaa tilaa myös vanhalle 68k-kumppanille. Seuraavassa katsotaan lääkkeitä onnelliseen yhteiseloon ja vilkaistaan lopuksi, mitä uutta MorphOS on tuonut tullessaan.


Yleensä koukkujen (ns. callback hook) käyttö on varsin helppoa: varataan struktuurille muistia ja alustetaan se osoittamaan kutsuttavaan funktioon. Vanhassa 68k-pohjaisessa AmigaOS:ssa oli vain yksi konekieli eikä PPC-koodin kutsumista koukun takaa tuettu. MorphOS:ssa taas koukku voi johtaa kumpaakin eli se voi osoittaa niin 68k- kuin PPC-koodiinkin. Mutta miten saada koukku toimimaan niin että vanhakin siitä tajuaa? Ratkaisuna on trappaaminen ja pieni vaivannäkö ohjelmoijalta.

MorphOS laajentaa 68k:n käskykantaa $F-alkuisilla konekielikäskyillä. Normaalisti 68k antaisi siitä virheen, mutta MorphOS:n 68k-emulaattorille se on merkki alkavasta PPC-koodista. Oikeasti se on EmulLibEntry -struktuurin alku, josta löytyy osoitin varsinaiseen PPC-koodiin:

struct EmulLibEntry
{
   UWORD    Trap;          /* kutsumetodi */
   UWORD    Extension;     /* täytyy olla nolla */
   void     (*Func)(void); /* itse funktio */
};

Kun natiivi PPC-ohjelma kutsuu tuntematonta koukkua, oletuksena täytyy aina olla, että koukun takaa löytyy 68k-koodia. Natiivi PPC-ohjelma ei saa koskaan kutsua tuntemattomia funktio-osoittimia käsin. Aina täytyy käyttää CallHookPkt()-kutsua tai vastaavaa käyttöjärjestelmän metodia. Erityisen kiellettyä on yrittää itse kutsua EmulLibEntryssä olevaa funktiota.


alkuun
Hookin rakentaminen

Toimivan PPC-hookin rakentaminen on kuitenkin varsin yksinkertaista. Hook struktuurin h_Entryn täytyy osoittaa EmulLibEntryyn. EmulLibEntryn kenttä Trap sisältää halutun kutsumetodin (yleensä TRAP_LIB), Extension nollataan ja Func sitten sisältää todellisen osoitteen. Koska nämä kaksi struktuuria toimivat yhteistyössä kannattaa ne yhdistää:

struct EmulLibEntry
{
   UWORD    Trap;          /* kutsumetodi */
   UWORD    Extension;     /* täytyy olla nolla */
   void     (*Func)(void); /* itse funktio */
};

Toimiva koukku voitaisiin nyt määritellä seuraavasti:

struct EmulHook Koukku =
{
   NULL,
   NULL,
   (HOOKFUNC)&Koukku.emul,
   NULL,
   NULL,
   TRAP_LIB,
   0,
   (void *)(OhjelmaKoodi)
};

Assemblerilla koukku näyttäisi seuraavalta:

Koukku:
   .long 0
   .long 0
   .long Koukku+20
   .long 0
   .long 0
   .short 65280
   .short 0
   .long OhjelmaKoodi

Huomaa, että EmulLibEntryä voi joutua käyttämään myös muissa yhteyksissä kuin koukuissa. Se on siis yleistyökalu, joka kelpaa muuhunkin kuin koukkujen rakentamiseen.


alkuun
68k-parametrien lukeminen

Ennen kuin toimiva koukku saadaan rakennettua, meille jää vielä yksi ongelma. Koukkujenhan mukana välitetään mitä moninaisimpia parametreja 68k:n rekistereissä. Esimerkiksi CallHookPkt() jättää rekistereihin A0/A2/A1 arvoja, joita kutsuttava ohjelman pätkä voi käyttää hyödykseen. PPC:ssä näitä rekistereitä ei tietenkään ole, joten arvot on löydettävä jostain muualta. Jokaisella ohjelmalla rekisteri r2 osoittaa struktuuriin EmulHandle:

struct   EmulHandle
{
   ULONG       Dn[8];
   ULONG       An[8];
   ULONG       *PC;
   ULONG       SR;

   /* + paljon muuta... */
};

Yllä olevasta struktuurista löytyvät talletettuna kaikki 68k:n rekisterit, joita saa lukea vapaasti. Dn[] sisältää datarekisterit ja An[] puolestaan osoiterekisterit. EmulHandlen lukeminen on kuitenkin työlästä, joten Harry Sintonen on kirjoittanut avuksi makrot parametreille. Samalla myös ohjelman siirrettävyys eri alustoille (OS3, OS4 ja MorphOS) paranee:

struct Library *NATDECLFUNC_1(LibOpen, a6, struct Library *, mybase)
{
   DECLARG_1(a6, struct Library *, mybase)

   mybase->lib_Flags &= ~LIBF_DELEXP;
   mybase->lib_OpenCnt++;
   return(mybase);
}

Riippumatta siitä, mille alustalle kääntää, ovat parametrit aina oikein.


alkuun
Lopuksi flushataan
MorphOS:n exec.library sisältää paitsi kaikki OS 3.1:n funktiot, myös joukon uusia. Perinteinen AllocPooled() on saanut rinnalleen AllocVecPooled()-kutsun, joka vastaa vanhaa AllocVec()-kutsua mutta toimii muistipoolien kanssa. Aivan uutta on ohjelmakohtainen muistipooli, jonka käyttöjärjestelmä tuhoaa automaattisesti ohjelman suorituksen loputtua. Poolista voi varata muistia AllocTaskPooled()- ja AllocVecTaskPooled()-kutsuilla ja vastaavasti vapauttaa FreeTaskPooled()- ja FreeVecTaskPooled()- kutsuilla. Sokerina pohjalla on FlushPool(), jolla muistipoolin voi tyhjentää tuhoamatta sitä. Oheisessa taulukossa on koottuna uudet funktiot ja niiden parametrimallit. MorphOS:sta löytyy myös muita uusia funktioita, joten protoja kannattaa lukea ahkerasti. Eihän sitä koskaan tiedä...

Funktio Parametrit
AllocTaskPooled (ULONG Size)
AllocVecPooled (APTR poolHeader, ULONG Size)
AllocVecTaskPooled (ULONG Size)
FreeTaskPooled (APTR Address, ULONG Size)
FreeVecPooled (APTR poolHeader, APTR memory)
FreeVecTaskPooled (APTR memory)
FlushPool (APTR poolHeader)

Tarvittavat tiedostot ja lyhyt esimerkkikoodi pakettina.
Sekalaiset   Sivun alkuun   Sivukartta