• Jak napisać własny slave? - część 1

02.01.2016 20:43, autor artykułu: Asman
odsłon: 3571, powiększ obrazki, wersja do wydruku,

Na pewno znakomita większość, jeżeli nie wszyscy użytkownicy, przynajmniej słyszeli o WHDLoad, dzięki któremu można uruchamiać gry z dysku twardego. Mało tego, możemy też z gry wyjść z powrotem do systemu. Wielu przeszła też myśl nie tylko o graniu za pomocą wyżej wspomnianego pakietu, a o możliwości stworzenia własnego patcha do gry. Mimo, że WHDLoad wspiera coraz więcej gier i być może kiedyś ziści się marzenie autora, że w końcu nie będzie gry uruchamianej z dyskietki, to wiele gier jeszcze jest do spatchowania. Zamiennie będę używał słowa naprawianie bądź ulepszanie, gdyż ja WHDLoad pojmuje nie w kategorii programu - degradera - umożliwiającego granie z dysku twardego, ale jako potężne narzędzie zarówno do ulepszania jak i środowiska do programowania bądź portowania gry. Czymże jest wspomniane patchowanie gry? Jest to nakładanie na oryginalną zawartość swoich danych. Podmieniać możemy kod, jak i dane - wszystko zależy od tego, co chcemy osiągnąć. Jeśli tylko chcemy zmienić grafikę na własną, to musimy sobie zdawać sprawę, że nie możemy robić tego w dowolny sposób. Na przykład, jeśli w grze jest obrazek tytułowy o wymiarach 128x100x4, to nie możemy w prosty sposób podmienić na grafikę w 256x200x8. Jasne jest, że taki patch jest możliwy, ale wymaga o wiele więcej pracy. Z kodem jest inaczej, o czym się wkrótce przekonamy. Podstawowym wymaganiem jest znajomość asemblera 68000. Po cichu zakładam, że czytelnik jest obeznany z Amigą i nie straszne mu rejestry hardware. Przyda się także wiedza z zakresu programowania pod systemem.

W wybranym przez nas miejscu instalujemy pakiet deweloperski dla WHDLoad. Najważniejszy dla nas będzie katalog autodoc, gdzie opisane są funkcje dostępne dla programisty i pliki z katalogu include, czyli WHDLoad.i. W nim to zdefiniowane mamy potrzebne stałe, tagi, struktury i makra. Dodatkowo w whdmacros.i można znaleźć także przydatne makra.

Podczas pracy nad slave, potrzebne będą również inne narzędzia. Do inżynierii odwrotnej, czyli deasemblacji pliku wynikowego do źródła w asemblerze, przyda się Resource 6.06 bądź darmowa IRA. Dla mnie osobiście bezkonkurencyjny jest pierwszy z nich. IRA działa z poziomu Shella, co może odstraszać początkujących, ale myślę, że warto się z nim zapoznać. W naszym kursie skupimy się tylko na Resource. Aby bezproblemowo rozpakowywać pliki, warto zainstalować pakiet xfd wraz z xfdDecrunch - znakomicie ułatwi nam to pracę. Oczywiście zdarzyć się może, że xfdDecrunch nie rozpakuje pliku mimo, że faktycznie został on spakowany zmodyfikowanym narzędziem. Wtedy mamy dwa wyjścia. Po pierwsze, spróbować naprawić spakowany plik, co wymaga dużo większej wiedzy, ale czasem przy odrobinie szczęścia wystarczy zmienić ciąg ASCII i ponownie użyć xfdDecrunch. Drugim wyjściem jest rozpakowanie ręczne, czyli wyłuskanie procedury depakującej, zmiana jej i uruchomienie, a następnie przegranie już rozpakowanych danych. Przyda się też DropHunk (dostępny na Aminecie) czy też podobna pchełka, pokazująca informacje o pliku wykonywalnym. Daje to ogólny obraz co w danym pliku siedzi. Dodatkowo można zaopatrzyć się w rippera modułów muzycznych, tak by wiedzieć gdzie (w jakiej lokalizacji) znajduje się muzyka z gry, co przyda się przy deasemblacji. Na końcu warto wspomnieć, że przy tworzeniu slave będziemy się posiłkować asemblerem Barfly.

Kilka słów o samym procesie patchowania - nie ukrywam, że w niektórych przypadkach jest to bardzo żmudna i czasochłonna praca. Żmudna w tym sensie, że ilość czasu spędzonego na testowaniu patcha może spowodować, że na daną grę przez jakiś czas nie spojrzymy. Czasochłonna, bo aby sprawdzić czy patch w pełni działa, trzeba grę ukończyć, a gdy rozwiązanie nie działa, powoduje często zawieszenie komputera i tylko reset może pomóc. Z tego też między innymi powodu mój blat Workbencha jest minimalistyczny, aby system startował bardzo szybko.

Zabieramy się do działania

Najpierw wybierzemy grę. Na początek wystarczy produkcja mieszcząca się w jednym pliku, którą można bez problemu załadować z poziomu Shella. Wydawać by się mogło, że przecież wystarczy kierować się rozmiarem pliku wykonywalny i w ten sposób mamy łatwego patcha do zrobienia. Odpowiedź nie jest już taka oczywista, bo wszystko zależy od tego, co napotkamy przeglądając, czyli deasemblując owy plik. Dla naszych celów wybrałem AppleHunt - grę, którą bez problemów możemy pobrać z Aminetu.

W wybranym przez nas katalogu tworzymy nowy o nazwie AppleHuntWHD i w nim będziemy pracować. Tutaj będzie docelowy plik z rozszerzeniem slave i ikona służąca do uruchamiania gry. Będąc w nim, tworzymy kolejne katalogi. Najważniejszy z nich to data - tu bedą przechowywane pliki z gry. Następnie icons, zawierający różne ikony - standardową, dla pakietu newicons itd. W katalogu sources znajdzie się kod źródłowy naszego slave'a, a podkatalog RCS służy do wersjonowania. Dalej katalog texts, gdzie zwykle trzymam pliki tekstowe z różnymi informacjami na temat zarówno gry, jak i techniczne detale, tak abym po przerwie, mógł w miarę szybko się zorientować, co zostało zrobione, gdzie utknąłem. Ostatni katalog to versions, zawierający podkatalogi crack i original. Tam przechowuję różne wersje gry - jak widać są tam też wersje scrackowane. Z tymi ostatnimi to różnie bywa, jeśli chodzi o patchowanie, bo zdarza się, że kod różni się od oryginalnej wersji i trzeba się napracować, aby dodać obsługę takiej wersji. Przy pracowaniu nad kolejnymi patchami takie tworzenie całej tej struktury katalogów jest nudne i męczące, dlatego ja posiłkuje się skryptem dosowym.

.KEY NAME,DIR
.BRA {
.KET }
.DEF DIR="DH2:WHD"      ;default directory
.DEF NAME="Empty"       ;default name
;
ECHO "WHDLoad template"
IF EXISTS {DIR}
    PCD {DIR}
    SET NAMEWHD{$$} {NAME}WHD
    IF NOT EXISTS $NAMEWHD{$$}
        MAKEDIR $NAMEWHD{$$}
        CD $NAMEWHD{$$}
        MAKEDIR data
        MAKEDIR icons
        MAKEDIR sources sources/RCS
        MAKEDIR texts
        MAKEDIR versions versions/crack versions/original
        ECHO "$NAMEWHD{$$} zrobione."
    ELSE
        ECHO "Nazwa *"$NAMEWHD{$$}*" istnieje."
    ENDIF
    PCD
ELSE
    ECHO "Nie ma takiego katalogu *"{DIR}*" ."
ENDIF 

Pobieramy archiwum z grą z Aminetu i kopiujemy go do odpowiedniego katalogu, a z rozpakowanej wersji, kopiujemy tylko plik AppleHunt do katalogu data. Mimo, że nie zaczęliśmy jeszcze właściwej pracy, to już wiemy, że mamy do czynienia z plikiem wykonywalnym. Co to oznacza dla nas? To, że plik składa się z odpowiednich hunków i że w nim zawarte są zarówno dane, jak i kod. Warto w tym miejscu na szybko przejrzeć AppleHunt za pomocą FileMastera korzystając z opcji ShowHex czy też użyć innego narzędzia pozwalającego zobaczyć plik w hex i w ascii. Przeglądając plik zwracamy uwagę czy przypadkiem nie widać ciągów, które normalnie można przeczytać, po pewnym czasie wystarczy tylko szybkie spojrzenie, aby stwierdzić, że gra została spakowana. Ten szybki podgląd ma dać nam podstawę do użycia narzędzia rozpakowującego xfdDecrunch - nikt nie broni nam używać tego narzędzia zawsze, nawet gdy mamy do czynienia z rozpakowanym plikiem. W naszym przypadku AppleHunt został spakowany PowerPackerem, o czym informuje nas xfdDecrunch.

>c:xfdDecrunch AppleHunt 
xfdDecrunch 1.9 (09.09.1999) © 1994-99 Georg Hörmann, Dirk Stöcker

Reading "AppleHunt" (116224 bytes)...
Decrunching "PowerPacker 4.0" file...
Writing "AppleHunt" (233352 bytes)...

W wyniku plik główny podwoił swój rozmiar i pozostał w dalszym ciągu plikiem wykonywalnym. A co byłoby, gdyby to był plik binarny? Skąd byśmy to wiedzieli? xfdDecrunch by nas poinformował, że po rozpakowaniu pliku następuje skok do określonej lokacji. Tylko po co rozpakowywać plik, skoro będzie zajmował więcej miejsca? Przecież wystarczy go odpowiednio spatchować. Tak, to prawda, że można w ten sposób podejść do sprawy. Ja robię to w ten sposób dlatego, aby było możliwe użycia packera wspieranego przez WHDLoad. Wówczas plik może być krótszy, bo nie ma procedury depakującej i sam proces rozpakowywania jest dla nas transparentny. Poza tym procedury rozpakowujące mogą powodować, że trzeba w najlepszym przypadku wyłączyć cache w procesorach 68060. Ja nie znam się dobrze na depakowaniu, a tym bardziej nie wiem co się może dziać na takim procesorze i wolę ten problem ominąć, zamiast ślęczeć nad kodem, który tak naprawdę ma mało wspólnego z grą. Czyli w dalszym ciągu będziemy pracować nad rozpakowaną wersją AppleHunt. Informacje od xfdDecrunch warto odnotować w katalogu texts, umieszczając tam stosowny plik. A jak to zrobić? Wystarczy przekierować wyjście na plik podczas ponownego uruchamiania narzędzia, robiąc to na spakowanej wersji pliku. Skoro mam plik wykonywalny, to przyjrzyjmy się bliżej, sprawdźmy jak jest z hunkami. Robimy DropHunk AppleHunt.

Hunk layout of AmigaDOS load file "AppleHunt" (233352 bytes filesize)

--- Starting with hunk #0 ----------

	HUNK_HEADER      
		Number of hunks: 1. First hunk to load #0, last hunk to load #0.
		-> Hunk #0 requires a storage of 232344 bytes.	» Hunk will be forced to CHIP-memory.

	HUNK_CODE        232344 bytes
	HUNK_RELOC32     
		 239 long relocation(s) for hunk #0

--- End of hunk #0 -----------------

Done.

DropHunk's rating: Less than or equal to 4 relocations per 1000 bytes.
»» Yes, nothing against it; strong compiler used.

Przeanalizujmy wynik. Widać tu, że autor gry nie napracował się, aby umieścić osobno dane i kod. Plik wykonywalny zawiera także tabelę relokacji, czyli przesunięcia niezbędne w procesie relokacji programu do dowolnego adresu. Przeskanujmy jeszcze AppleHunt ripperem muzycznym. Odnajdziemy moduł muzyczny 'glenn aage beate' o długości 123536 bajtów a przesunięcie względem początku gry wynosi $19c00 = 105472, czyli po szybkiej kalkulacji moduł znajduje się prawie na końcu pliku.

Przystąpmy do pisania naszego slave'a. Plan jest bardzo prosty.

A. Załadować AppleHunt.
B. Spatchować.
C. Uruchomić.

Aby poprawnie załadować plik wykonywalny, przeważnie używamy do tego celu funkcji z biblioteki dos o nazwie LoadSeg. W rejestrze D1 podajemy wskaźnik na string zawierający nazwę pliku i jeśli operacja się powiodła, to otrzymamy wskaźnik BCPL do listy segmentów. Dalej to przetwarzamy BCPL na normalny wskaźnik i uruchamiamy kod. Ponieważ WHDLoad zatrzymuje system, więc nie mamy do niego dostępu i musimy sobie jakoś z tym poradzić. Mamy trzy możliwe rozwiązania załadowania pliku wykonywalnego.

1. skorzystać z OSEmu i funkcji LoadSeg z dos.library
2. skorzystać z KickEmu (Kickstart 1.3 bądź 3.1) i funkcji LoadSeg z dos.library
3. skorzystać z funkcji resload_LoadFile i resload_Relocate.

Dwa pierwsze rozwiązania bazują na emulacji systemu podczas pracy WHDLoad. Pierwszy z nich to najwcześniejsza próba poradzenia sobie z problemem patchowania gier, które w niewielkim stopniu wykorzystują OS, na przykład alokują pamięć, ładują pliki poprzez dos.library. Niewątpliwą zaletą jest mały rozmiar OSEmu, który to można dodatkowo odchudzić, lecz tu trzeba większej wiedzy. Minusem jest, że skrypt instalacyjny dorzuca dodatkowy plik do katalogu data. Największą bolączką OSEmu jest częściowa emulacja OS i co za tym idzie, że podczas testów po wielu godzinach patchowania, uruchomianiu i grania, dowiadujemy się, że gra korzysta z funkcji systemu, której nie ma w OSEmu bądź jest zaimplementowana szczątkowo. KickEmu pozbawiony jest ostatniej wady OSEmu, ale za to wymagania pamięciowe dla naszej gry rosną. My skupimy się na opcji trzeciej.

Stwórzmy w ulubionym edytorze plik AppleHuntWHD.s i umieśćmy tam początkowy blok komentarza, tak aby było jasne.

;---------------------------------------------------------------------------
; Author        Asman
; Version       1.0
; History       2007-03-10
; Requires      -
; Copyright     Public Domain
; Language      68000 Assembler
; Translator    Barfly V2.9
;
; Game		AppleHunt
;
; $Id$
;---------------------------------------------------------------------------

Ponieważ ja używam do wersjonowania plików programu RCS, to dziwny ciąg $Id$ będący identyfikatorem zostanie zastąpiony odpowiednim podczas pracy z RCS. Oczywiście można używać innego systemu wersjonowania plików bądź też nie używać żadnego. Ja gorąco zachęcam, bo niejednokrotnie RCS ratował mi skórę przed pisaniem slave'a od nowa, gdy okazało się, że zmiany dokonane przeze mnie wieszają grę. Pod blokiem komentarzy dodamy includy, etykietę RELEASE, która to na ten moment będzie zakomentowana i dyrektywy dla Barfly (jeśli używasz innego, to nie dodawaj ich) dla WHDLoad

	INCDIR	Includes:
	INCLUDE	WHDLoad.i
	INCLUDE	whdmacros.i

;RELEASE

	IFD BARFLY

	OUTPUT "Ram:AppleHunt.slave"

	IFD RELEASE

	BOPT   O+             ;enable optimizing
	BOPT   OG+            ;enable optimizing

	ELSE

	BOPT   O-             ;disable optimizing
	BOPT   OG-            ;disable optimizing

	ENDC	;END IFD RELEASE

	BOPT   ODd-           ;disable mul optimizing
	BOPT   ODe-           ;disable mul optimizing
	BOPT   w4-            ;disable 64k warnings
	BOPT   wo-            ;disable optimize warnings
	SUPER

	ENDC	;END IFD BARFLY

Słowem wyjaśnienia dyrektywy te nakażą asemblerowi stworzenie pliku wynikowego AppleHunt.slave i umieszczenie go w RAM:. Dodatkowo, jeśli etykieta RELEASE zostanie zdefiniowana, to nastąpi optymalizacja kodu. Aby ułatwić sobie nieco pracę, zdefiniujemy macro, pomocne przy pisaniu wywołań procedur z WHDLoad, dodamy etykiety mówiące o zapotrzebowaniu naszego slave'a na pamięć. Na początek 0.5 MB Chip wystarczy, gdyby to się okazało za mało, to zwiększymy do 1 MB. I dalej mamy strukturę opisująca slave'a.

;
; \1 - nazwa procedury bez przedrostka resload_ 
;
callwhd	MACRO
		move.l	_resload(pc),a2
		jsr	resload_\1(a2)
	ENDM

CHIPMEMSIZE = $80000
FASTMEMSIZE = 0
slv_Version = 15
slv_Flags   = 0
slv_keyexit = $59 	;F10

	IFNE slv_Version-15
	FAIL   wersje ponizej 15 nie sa wspierane
	ENDC	;END IFNE slv_Version-15

slv_base	SLAVE_HEADER			;ws_Security + ws_ID
		dc.w	slv_Version		;ws_Version
		dc.w	slv_Flags		;ws_flags
_basemem	dc.l	CHIPMEMSIZE		;ws_BaseMemSize
		dc.l	0			;ws_ExecInstall
		dc.w	_start-slv_base		;ws_GameLoader
		dc.w	slv_CurrentDir-slv_base	;ws_CurrentDir
		dc.w	0			;ws_DontCache
_keydebug	dc.b	0			;ws_keydebug
_keyexit	dc.b	slv_keyexit		;ws_keyexit
_expmem		dc.l	FASTMEMSIZE		;ws_ExpMem
		dc.w	slv_name-slv_base	;ws_name
		dc.w	slv_copy-slv_base	;ws_copy
		dc.w	slv_info-slv_base	;ws_info

	IFD BARFLY
	IFND .passchk
	DOSCMD   "WDate  >T:date"
.passchk
	ENDC	;END IFND .passchk
	ENDC	;END IFD BARFLY

slv_CurrentDir	dc.b	"data",0
slv_name	dc.b	"Apple Hunt",0
slv_copy	dc.b	"19xx xxx",0
slv_info	dc.b	"installed & fixed by Asman",10
		dc.b	"Version 1.0 "

	IFD BARFLY
		INCBIN   "T:date"
	ENDC	;END IFD BARFLY
		dc.b  0
	EVEN

Przed samą strukturą dodałem warunek sprawdzający na wypadek przypadkowej zmiany wersji na wyższą. Sama struktura zaczyna się od makra, które to odnajdziemy w pliku WHDLoad.i. Tworzy ono zabezpieczenie przed uruchomieniem slave'a jako pliku wykonywalnego, dodatkowo dodaje napis "WHDLoadS" za tym kodem. Następne ważne pole to wersja struktury - my będziemy korzystać z wersji 15. Potem są pomocne flagi dla WHDLoad, jak na przykład flaga mówiąca o tym, żeby wyczyścić pamięć zerami przy starcie. ws_GameLoader to różnica między początkiem struktury, a kodem slave'a, czyli przesunięcie. Kolejne pole to ws_CurrentDir, które jest przesunięciem do podkatalogu, w którym znajdują się pliki gry. W tym przypadku będzie to katalog "data". Pola ws_keydebug i ws_keyexit zawierają kody raw klawiszy dla wyjścia z gry, przy czym pierwsze z nich powoduje dodatkowo zrzut pamięci, a miejsce zrzutu określa zmienna CoreDumpPath w pliku WHDLoad.prefs. Potem jest ws_ExpMem zawierające ilość pamięci dodatkowej - w zależności co mamy na pokładzie naszej maszyny, będzie to w pierwszej kolejności pamięć typu Fast a potem Chip. Dalej mamy pola ws_name, ws_copy i ws_info - są to przesunięcia do nazwy gry/dema, informacji o produkcji (czyli rok, autorzy/wydawca) i dodatkowych informacji. Zwyczajowo umieszcza się tu również podziękowania za oryginalne obrazy do gry i znajdują się tu też dane o autorze, wersji i dacie wypuszczenia slave'a.

W końcu nadszedł czas na napisanie kodu slave'a:


	GAME_STARTADR = $10000

	_start:
		;zachowanie bazy resload
			lea	_resload(pc),a1
			move.l	a0,(a1)

		;zaladowanie i rozpakowanie pliku
			lea	game_name(pc),a0
			lea	GAME_STARTADR,a1
			callwhd	LoadFileDecrunch

		;relokacja pliku
			lea	GAME_STARTADR,a0
			sub.l	a1,a1
			callwhd	Relocate

		;patchowanie gry
			lea	_patch_base(pc),a0
			lea	GAME_STARTADR,a1
			callwhd	Patch

		;start gry
			jmp	GAME_STARTADR
	;-----------------------------------------------------------------------------
	_patch_base
			PL_START
			PL_END
	;-----------------------------------------------------------------------------
	;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	game_name	dc.b	"AppleHunt",0
		EVEN
	_resload	dc.l	0

Na samym początku, gdy WHDLoad wywołuje kod slave'a, to w rejestrze A0 mamy adres resload. Zapamiętujemy go, bo jest on wymagany przez funkcje WHDLoad. Określa on początek tabeli wskaźników na funkcje i wygląda to tak:

	dc.l	resload_Install
	dc.l	resload_Abort
	dc.l	resload_LoadFile

Więcej informacji można odnaleźć w pliku WHDLoad.i odnajdując strukturę ResidentLoader. Wykonanie takiej funkcji w asemblerze ma postać:

	move.l	_resload(pc),a2
	jsr	8(a2)	;wykonaj jsr resload_LoadFile(a2)

Po zapamiętaniu resload, wczytujemy plik (i ewentualnie go rozpakowujemy) jako binarny i na to trzeba zwrócic uwagę, bo jak wiemy ten plik jest wykonywalny. Do A0 podajemy wskaźnik na nazwę pliku, a w rejestrze A1 ląduje docelowy adres. Potem użyjemy funkcji resload_Relocate, która przerelokuje plik, do adresu docelowego. Pamiętam, że długo nie mogłem zrozumieć jak działa ta funkcja. Próbowałem ładować plik za pomocą dosowej funkcji loadSeg, bo przecież tak się poprawnie wczytuje pliki wykonywalne, dalej używałem resload_Relocate i jakież było moje zdziwienie, że nic nie działa. Dopiero mail od autora WHDLoad, nie pierwszy zresztą w tej sprawie, uświadomił mi, że ta funkcja działa na wykonywalnym pliku, który to wczytujemy do pamięci jako plik binarny. Na przykład korzystając z resload_LoadFile albo dosowej Read. Czyli można powiedzieć, że funkcje z WHDLoad, LoadFile + Relocate, dają to samo co LoadSeg z biblioteki dos.

Następnie łatamy grę, używając resload_Patch. Aby jej poprawnie użyć, w rejestrze A1 podajemy adres pamięci, pod jakim gra się znajduje, w A0 musi się znaleźć adres tak zwanej patch listy. Jest to zbiór makr, które bardzo ułatwiają i przyspieszają patchowanie. Spójrzmy na mały przykład.

;
; remove blthog and empty loop
;
	lea 	GAME_STARTADR,a0
	add.l	#$86,a0
	move.w	#$4e71,(a0)+	; write NOP
	move.w	#$4e71,(a0)+
	move.w	#$4e71,(a0)+
	move.w	#$4e71,(a0)+		
	move.w	#$4e71,(a0)+
;
; fix clr.w aud
;
	lea	GAME_STARTADR,a0
	add.l	#$1034,a0
	move.w	#$4eb9,(a0)+	; write JSR
	pea	fixClrAudX(pc)
	move.l	(sp+),(a0)+
	move.w	#$4e71,(a0)+
	move.w	#$4e71,(a0)+
	move.w	#$4e71,(a0)+
	move.w	#$4e71,(a0)+
	move.w	#$4e71,(a0)+
	move.w	#$4e71,(a0)+
	move.w	#$4e71,(a0)+
	move.w	#$4e71,(a0)+
	move.w	#$4e71,(a0)+

Zanim przeanalizujemy przykładzik, to muszę się wytłumaczyć z paru rzeczy. Jak wspominałem wyżej łatanie polega na zamianie jednych danych na inne, bardziej ciekawsze dla nas. W przypadku kodu musimy wiedzieć, ile dana instrukcja zajmuje i jak taka instrukcja wygląda w pamięci. Na przykład instrukcja NOP to słowo o wartości $4e71. I jeśli mamy instrukcję asemblera, która zajmuje 4 bajty (przykładowo niech to będzie move.b #10,d0), to aby wypełnić kod "nopami", trzeba użyć ich dwie sztuki, bo jeden NOP zajmuje dwa bajty. Zatem aby pisać slave'y, nie wystarcza pobieżna wiedza o asemblerze 68000 na zasadzie kompilacji kodu czy znajomości poszczególnych mnemoników, ale także trzeba znać, przynajmniej pobieżnie, jak to wygląda w pamięci komputera. Z pomocą przychodzi tu Resource, gdzie można sobie w prosty sposób zobaczyć, ile instrukcja zajmuje. Czyli wystarczy wiedzieć mniej więcej ile dana instrukcja zajmuje, bo zawsze możemy to sprawdzić. Także w Asm-One łatwo to sprawdzić, korzystając z komendy D adres czy też używać sztuczki i odjąć jedną etykietę od drugiej. Na razie nie będziemy wnikali dlaczego i co fixujemy, chodzi mi tylko o pokazanie mechanizmu. W pierwszej części przykładu pokrywamy odpowiednią ilością NOP instrukcje odpowiedzialne za ustawienie blthog i pustą pętlę. W drugiej zaś blok instrukcji zamieniamy na jsr fixClrAudX i odpowiednią ilość NOP. Ktoś sprytny może zauważyć, że pewne instrukcję się powtarzają i można zapisać je tak.

	lea	GAME_STARTADR,a0
	lea	($86,a0),a1

	REPT 5
	move.w	#$4e71,(a1)+		;NOP
	ENDR	;END REPT 5

	lea	$1034(a0),a1
	move.w	#$4eb9,(a1)+		;JSR
	pea	fixClrAudX,(pc)
	move.l	(sp)+,(a1)

	REPT 9
	move.w	#$4e71,(a1)+
	ENDR	;END REPT 9

Za pomocą makr z WHDLoad.i możemy ten mały przykład zapisać w następujący sposób:

_patch_base
		PL_START
	; remove blthog and empty loop
		PL_NOP	$86,$a
	; fix clr.w aud
		PL_PSS	$1034,fixClrAudX,$12
		PL_END

Wstęp mamy za sobą. Stworzyliśmy bardzo prostego slave'a, który poza tym, że wczytuje poprawnie grę, to nic innego nie robi. Pełny plik można pobrać stąd. Po skompilowaniu otrzymamy plik AppleHunt.slave umieszczony w RAM. Zapomniałem wspomnieć, że ja korzystam z małego programiku WDate, który generuje aktualną datę i jest on w katalogu WHDLoad/Src/programs. Proponuje go skopiować do katalogu C bądź zmienić ścieżkę w źródle.

Spróbujmy odpalić naszego slave'a. Można to uczynić korzystając z ikonki, którą trzeba sobie przygotować albo wpisujac z shella:

WHDLoad slave=Ram:AppleHunt.slave preload

Naszym oczom, o ile mamy "odhaczony" w ustawieniach WHDLoad tryb expert, ukaże się komunikat o błędzie. Klikając w przycisk ShowReg zobaczymy dokładniejsze dane. Powodem błędu jest próba otwarcia biblioteki graphics.library. Bez EmuKicka nie będzie to możliwe, gdyż jak wiemy WHDLoad zatrzymuje system. Musimy więc zacząć łatać grę, ale pierwszy krok został już zrobiony. Próbując uruchomić pierwszy slave, przeprowadziliśmy w ten sposób pierwszy test.

 głosów: 9   tagi: WHDLoad, slave, kurs, szkółka
komentarzy: 7ostatni: 06.12.2021 15:28
Na stronie www.PPA.pl, podobnie jak na wielu innych stronach internetowych, wykorzystywane są tzw. cookies (ciasteczka). Służą ona m.in. do tego, aby zalogować się na swoje konto, czy brać udział w ankietach. Ze względu na nowe regulacje prawne jesteśmy zobowiązani do poinformowania Cię o tym w wyraźniejszy niż dotychczas sposób. Dalsze korzystanie z naszej strony bez zmiany ustawień przeglądarki internetowej będzie oznaczać, że zgadzasz się na ich wykorzystywanie.
OK, rozumiem