PROBLEMY Z KOMPATYBILNOÔCIÂ >Dawno minëîy juû czasy, w których okreôlenie "Amiga" oznaczaîo Amigë 500 z 1 MB pamiëci. Takûe termin "Amiga AGA" nie podaje nam bliûszych informacji o danym sprzëcie poza typem sterownika graficznego oraz procesorem zgodnym z MC68020. Dziô za sprawâ niezaleûnych producentów nasze Amigi mogâ mieê dowolny procesor z rodziny MC680x0, czy teû pamiëê ulokowanâ pod (prawie) dowolnym adresem. Bardzo waûne jest wiëc takie pisanie programów (a dem w szczególnoôci), aby uruchamiaîy sië na dowolnej konfiguracji. A jak to wyglâda u nas? Z produkcji wydanych na Polish Summer Party'96 na demo czy intro compo na Amidze z monitorem multisync i procesorem '060 uruchamiajâ sië dwie, przy czym jedna z nich zawiesza sië w poîowie... Noster/Venture Artykuî ten powstaî, aby pomóc wszystkim bîâdzâcym do dziô koderom. Omówione zostanâ tu typowe problemy z uruchamianiem dem na róûnych konfiguracjach. Zacznijmy od Sprawdzania konfiguracji Typ procesora sterujâcego komputerem, na którym uruchamiamy nasze demo, moûna odczytaê w bardzo prosty sposób. W polu AttnFlags biblioteki exec.library znajdziemy informacje o procesorze i koprocesorze arytmetycznym. Ustawione poszczególne bity oznaczajâ: bit 0 - MC68010 bit 1 - MC68020 bit 2 - MC68030 bit 3 - MC68040 bit 4 - MC68881 bit 5 - MC68882 bit 6 - FPU040 i FPU060 bit 7 - MC68060 Jak widaê, wykrycie procesora MC68000 jest odnotowywane poprzez wyzerowanie wszystkich bitów. Dla pozostaîych ukîadów obowiâzuje zasada ustawiania poprzednich bitów, np. jeûeli system wykryje procesor '030, to ustawione zostanâ bity 0, 1 i 2. To samo dotyczy koprocesorów. Naleûy teû zauwaûyê wprowadzenie bitu 6 dla koprocesorów wbudowanych w procesory '040 i '060. Wydawaîoby sië, ûe jest on zawsze ustawiany, jeôli wykryty zostaje jeden z tych procesorów. Tak jednak nie jest. Bit ten ustawiany jest po zaîadowaniu kodu emulujâcego peîen zestaw instrukcji wykonywanych przez ukîady zewnëtrzne ('881 i '882), inaczej mówiâc, po wykonaniu komendy SetPatch. ; przykîadowa procedura sprawdzajâca czy dana Amiga ma procesor co ; najmniej 020 - po powrocie w rejestrze d0 mamy odpowiedú (d0=1 -> 020+) AttnFlags =$128 mc68020_check: move.l 4.w,a6 move.w AttnFlags(a6),d0 btst #1,d0 beq.s mc68020_not_found moveq #1,d0 mc68020_not_found: rts Sprawdzanie ukîadów graficznych takûe nie powinno sprawiê problemów. W polu ChipRevBits (offset $ec) w bibliotece graphics.library odczytujemy, w jakie ukîady wyposaûona jest nasza Amiga. Wykrycie ukîadów AGA moûliwe jest dopiero po wykonaniu komendy SetPatch. Obecnoôê ukîadów ECS sprawdzamy przez testowanie bitu pierwszego w tym polu, a ukîadów AGA przez sprawdzenie bitu drugiego lub trzeciego. Monitory Gdy wiemy juû, jakie ukîady graficzne ma nasz komputer, wypadaîoby sprawdziê, jaki monitor jest do niego podîâczony oraz czy pozwala on na wyôwietlenie np. trybu PAL, w którym pracuje nasze demo. Jest to problem wystëpujâcy w przytîaczajâcej wiëkszoôci polskich dem. Prawdë mówiâc, tylko nieliczni wykonujâ powyûsze czynnoôci, chociaû sâ one bardzo proste. I tym razem z pomocâ przychodzi nam system operacyjny. W bibliotece graphics.library znajdujemy pole DisplayFlags, a w nim ustawiony bit drugi oznacza moûliwoôê pracy w systemie PAL. Ale samo sprawdzenie nie rozwiâzuje jeszcze problemu, gdyû monitor naleûy przeîâczyê w tryb PAL! Wykonujemy to za pomocâ procedury LoadView() z graphics.library, przechowujâc najpierw "view" systemowy, który naleûy przywróciê przy wyjôciu z dema, a nastëpnie wywoîujâc LoadView z zerem jako parametr. Przykîad: ActiView =$22 cpinit =$26 LoadView =-$de WaitTOF =-$10e init: move.l graphicsbase,a6 move.l ActiView(a6),prev_view ;przechowujemy aktualny "view" systemu move.l copinit(a6),prev_cop ;to samo z copperlistâ sub.l a1,a1 ;move.l #0,a0 jsr LoadView(a6) jsr WaitTOF(a6) jsr WaitTOF(a6) ... exit: move.l graphicsbase,a6 move.l prev_view,a1 jsr LoadView(a6) ;przywracamy "view" systemowy jsr WaitTOf(a6) jsr WaitTOf(a6) move.l prev_cop,$dff080 ;przywracamy copperlistë move.w #0,$dff088 ... Kod programu W zwiâzku z wyposaûeniem wiëkszoôci procesorów rodziny MC680x0 w pamiëê cache naleûy unikaê stosowania samomodyfikujâcego sië kodu. Niezastosowanie sië do tych zaleceï moûe spowodowaê "dziwne" zachowanie sië naszych procedur (z zawieszeniem wîâcznie). Przykîad? Proszë bardzo: ... move.l #adres_skoku,skok+2 skok: jmp 0 ... Taki fragment kodu spowoduje zawieszenie systemu na procesorach wyposaûonych w pamiëê cache (020+). Jeôli juû koniecznie chcemy zastosowaê kod samomodyfikujâcy sië, to musimy pamiëtaê o przepisywaniu zawartoôci pamiëci podrëcznej do RAM-u w odpowiednich momentach. W powyûszym przykîadzie naleûaîoby dodaê przed rozkazem skoku nastëpujâce linie: CacheClearU =-$27c move.l 4.w,a6 jsr CacheClearU(a6) Dodatkowo naleûy pamiëtaê, ûe procesor '060 nie posiada niektórych instrukcji, które majâ starsze procesory. Sâ to miëdzy innymi 64-bitowe instrukcje mnoûenia i dzielenia (MULx.L ,Dh:Dl, DIVx.L ,Dh:Dl), porównania rejestru z granicami CMP2 oraz inne raczej nie uûywane w demach. Zbyt czëste ich stosowanie (szczególnie dotyczy to instrukcji mnoûenia i dzielenia) moûe spowodowaê "niezbyt pîynne" dziaîanie naszej procedury, co moûna zaobserwowaê np. w "Burning Chrome". Wynika to z tego, ûe rozkazy te muszâ byê emulowane na '060 programowo. Przerwania Po co w ogóle wykorzystywaê przerwania? Choêby po to, aby na róûnych Amigach dany efekt wykonywaî sië w tym samym czasie. Przykîadowo jeôli efektem jest wektor, to umieszczenie procedury zmieniajâcej np. kât obrotu lub pozycjë obserwatora na przerwaniach spowoduje, ûe nasz wektor bëdzie sië poruszaî w ten sam sposób na róûnych Amigach (z tym ûe na wolniejszych Amigach bardziej skokowo, a na szybszych bardziej pîynnie). Przy korzystaniu z przerwaï pomocne okaûâ sië dwie systemowe procedury: AddIntServer i RemIntServer. Obie znajdujâ sië w exec.library. Dodanie naszej procedury do îaïcucha procedur systemowych wyglâda nastëpujâco: INTB_VERTB =5 AddIntServer =-$a8 RemIntServer =-$ae init: moveq.l #INTB_VERTB,d0 ;przerwanie nr 5 - vertical blank move.l 4.w,a6 lea vblk,a1 ;adres naszej struktury jsr AddIntServer(a6) ... exit: moveq.l #INTB_VERTB,d0 move.l 4.w,a6 lea vblk,a1 jsr RemIntServer(a6) ... vblk_proc: movem.l d2-d7/a2-a4,-(sp) ... ;procedura obsîugi przerwania movem.l (sp)+,d2-d7/a2-a4 rts vblk: dc.l 0,0 dc.b 2 vblk_pri: dc.b 0 dc.l vblk_name dc.l 0,vblk_proc vblk_name: dc.b 'Nazwa procedury przerwaï',0 Procedurë przerwania koïczymy rozkazem RTS, a nie RTE, poniewaû nasza procedura bëdzie wywoîywana przez system operacyjny, a nie bezpoôrednio przez procesor. Pole vblk_pri zawiera priorytet, jaki bëdzie przypisany naszej procedurze przerwania. Jeûeli ustawimy go na wartoôê wiëkszâ niû 9, to dodatkowo przed rozkazem RTS bëdziemy musieli ustawiê znacznik Z (np. przez moveq #0,d0). Czasami moûe sië jednak okazaê, ûe bëdziemy musieli obyê sië bez pomocy systemu. Co naleûy wówczas zrobiê? Trzeba wpisaê adres procedury przerwania bezpoôrednio do tablicy wektorów procesora. Najpierw naleûy przechowaê poprzedniâ wartoôê wektora, aby móc jâ póúniej przywróciê. I tu znów czyha na nas puîapka, w którâ wpaôê jest bardzo îatwo. Na Amidze 500 wektory przerwaï znajdowaîy sië zawsze w tym samym miejscu i z ich zmianâ nie byîo ûadnych problemów. Jednak na Amigach wyposaûonych w procesor MC68010 (lub nowszy) udostëpniono uûytkownikowi rejestr VBR (Vector Base Register), który w MC68000 byî zawsze wyzerowany -- stâd niezmienne poîoûenie wektorów. Jednak wielu amigowców posiadajâcych pamiëê Fast przepisuje tablicë wektorów do tej pamiëci i zmienia wartoôê wskaúnika w VBR. Jeôli tego nie uwzglëdnimy w naszej produkcji, to w najlepszym wypadku obracany na przerwaniach wektor bëdzie w ramach protestu staî w miejscu (co moûna zaobserwowaê np. w "PulpFiction") lub nie bëdzie go wcale, jeôli uruchomimy demo na jakimô dziwnym procesorze, np. '060... SuperVisor =-$1e intenar =$dff01c intena =$dff09a intreq =$dff09c init: move.w intenar,old_int ;przechowanie maski przerwaï bsr.s getvbr ;pobranie VBR move.l d0,a0 move.l vblk_int(a0),old_vblk ;przechowanie starego adresu move.w #$0020,intreq ;chwilowe zablokowanie przerwania vblk move.l #my_vblk,vblk_int(a0) ;wpisanie adresu naszej procedury move.w #$8020,intena ;wîâczenie przerwania vblk ... exit: move.l vbr_ptr,a0 move.w #$0020,intreq ;chwilowe zablokowanie przerwania vblk move.l old_vblk,vblk_int(a0) ;wpisanie adresu poprzedniej procedury or.w #$8000,old_int ;ustawienie bitu set/clr move.w old_int,intena ;przywrócenie maski przerwaï ... my_vblk: movem.l d0-a6,-(sp) ... ;procedura obsîugi przerwania movem.l (sp)+,d0-a6 rte getvbr: move.l 4.w,a6 lea gvbr_proc(pc),a5 ;adres procedury pobierajâcej VBR jsr SuperVisor(a6) move.l d0,vbr_ptr rts gvbr_proc: movec VBR,d0 rte vbr_ptr: ds.l 1 old_vblk: ds.l 1 old_int: ds.w 1 Wyjaônienia wymaga chyba sposób odczytu rejestru VBR. Otóû rejestr ten nie jest dostëpny podczas normalnego trybu pracy procesora. Odczytaê go moûna tylko w trybie nadzorcy (supervisor). Wyîâczanie multitaskingu Czasami zachodzi potrzeba wyîâczenia tej wspaniaîej cechy naszego systemu operacyjnego. Dlaczego? Odpowiedzi moûe byê kilka: aby przyôpieszyê lub zwiëkszyê pîynnoôê dziaîania naszego dema, wówczas ûaden inny program pracujâcy w tle nie bëdzie zabieraî nam czasu procesora. Takûe, gdy nie chcemy dopuôciê do pisania w CLI lub klikania na ikonce Workbencha (co moûe byê 'niebezpieczne' -- osobiôcie posiadam gadget "Reset" w Toolmanagerze i czësto podczas czytania jakiegoô magazynu nagle resetowaî mi sië komputer). Co wiëc naleûy zrobiê, aby pozbyê sië innych tasków? Korzystamy ponownie z biblioteki exec. Forbid =-$84 Permit =-$8a move.l 4.w,a6 jsr Forbid(a6) ;zablokowanie multitaskingu ... move.l 4.w,a6 jsr Permit(a6) ;odblokowanie multitaskingu ... Wszystko piëknie, tylko teraz system nie wykrywa nam np. zmiany dysku. Aby usunâê të wadë, moûna zamiast blokowania multitaskingu ustawiê priorytet naszego tasku na najwyûszy (127). Wtedy ze zmianâ dysków nie ma juû takich problemów (w razie problemów naleûy spróbowaê zmniejszyê taskpri do 20 lub 10). FindTask =-$126 SetTaskPri =-$12c move.l 4.w,a6 sub.l a1,a1 jsr FindTask(a6) move.l d0,a1 moveq #127,d0 jsr SetTaskPri(a6) Doczytywanie danych Wydawaîoby sië, ûe doczytywanie danych nie powinno sprawiaê ûadnych kîopotów. I w zasadzie tak jest, ale moûna by je jeszcze trochë usprawniê. Kaûdego zapewne denerwuje narastajâca z kaûdym nowym programem liczba assignów, jakie trzeba dodawaê do user-startupu. Rozwiâzanie tego problemu jest bardzo proste: wystarczy skorzystaê z systemowego assigna pt. "PROGDIR:". Pozwala on odczytywaê dane z katalogu, z którego zostaî uruchomiony dany program. Odtwarzanie muzyki Do odtwarzania muzyki naleûy uûywaê procedury nie korzystajâcej z pëtli opóúniajâcej (mam na myôli odtwarzaczkë z Protrackera, przynajmniej w wersji do 2.3), gdyû naleûaîoby dla róûnych procesorów ustawiaê róûne wartoôci opóúnienia. Na pewno godnâ polecenia odtwarzaczkâ jest The Player v6+, posiadajâca duûe moûliwoôci dostosowania sië do zachcianek uûytkownika oraz, co najwaûniejsze, dziaîa na róûnych konfiguracjach (sprawdza nawet, czy Amiga uruchomiona jest w systemie PAL czy NTSC). Uruchamianie z ikony Czësto zdarza sië, ûe zachciewa sië nam uruchomiê nasze super demo z ikonki. Jednak to, co ukazuje sië naszym oczom, raczej go nie przypomina. Najczëôciej jest to guru o numerze 87000004. Oznacza to, ûe nasze demo potrzebuje dodatkowego kodu pozwalajâcego na uruchamianie z Workbencha. Moûna teû ominâê të niedogodnoôê w inny sposób, ale nas interesuje przecieû czysto koderskie rozwiâzanie tego problemu. Workbench podczas uruchamiania programu z ikonki wysyîa do niego pewne informacje. Nie sâ one nam co prawda do niczego potrzebne, ale musimy je chociaû odebraê. W przeciwnym wypadku nasze demo bëdzie powodowaê zawieszenie systemu. Najpierw musimy pobraê adres naszego tasku. Nastëpnie w polu pr_CLI sprawdziê, czy program uruchomiono z CLI, czy teû z Workbencha. Jeôli uruchomiono go z CLI, to moûemy juû przejôê do gîównej czëôci. W przeciwnym wypadku musimy odebraê systemowâ wiadomoôê, a przy wyjôciu z dema odpowiedzieê na niâ. Przed odpowiedziâ na systemowâ wiadomoôê musimy jednak wîâczyê multitasking! FindTask =-$126 WaitPort =-$180 GetMsg =-$174 ReplyMsg =-$17a pr_CLI =$ac pr_MsgPort =$5c move.l 4.w,a6 sub.l a1,a1 jsr FindTask(a6) ;pobranie adres tasku move.l d0,a4 tst.l pr_CLI(a4) bne.s from_cli lea pr_MsgPort(a4),a0 jsr WaitPort lea pr_MsgPort(a4),a0 jsr GetMsg(a6) move.l d0,message from_cli: ... exit: tst.l message beq.s done move.l 4.w,a6 jsr Forbid(a6) move.l message,a1 jsr ReplyMsg(a6) Kompresja Jeôli ktoô myôli, ûe to juû koniec atrakcji czyhajâcych na nieôwiadomego niebezpieczeïstw kodera, to sië myli. Wszystkie dema, a intra w szczególnoôci, sâ zazwyczaj kompresowane przy uûyciu róûnych PowerPackerów czy Imploderów. I tu wîaônie ukryta jest ostatnia (chyba) puîapka. Naleûy zwróciê uwagë na dwa popularne kompresory: PowerPacker i CrunchMania. Oba praktycznie nie nadajâ sië do kompresowania za ich pomocâ programów, ze wzglëdu na úle napisane decrunchery. Programy skompresowane PowerPackerem bardzo îadnie "szatkujâ" pamiëê, a CrunchManiâ nie uruchamiajâ sië na Amigach wyposaûonych w procesor '060. Na placu boju pozostajâ wiëc: StoneCracker, Imploder, ProPack (RNC), Titanic, Jan Heweliusz (prawdopodobnie w chwili, gdy to czytacie jest on juû dostëpny). Mam nadziejë, ûe wymieniîem tu wiëkszoôê problemów, jakie mogâ wystâpiê podczas prób uruchamiania naszych dem, czy teû gier nie korzystajâcych z systemu. Jak widaê, system potrafi jednak byê wielce pomocny w wielu sytuacjach i nie naleûy go zbyt pochopnie odrzucaê. Na tym chyba koïczë ten artykuî i ûyczë powodzenia przy pisaniu wîasnych dem.