Also. Ich will mal einige Daten loswerden, die bei mir auf der Festplatte sonst vergammeln. Das hier ist mein Projekt "Längster Foren-Post wo gibt". Mithilfe dieses Projekts bin ich wirklich schonmal an die sechzigtausend-Zeichen-Grenze für Posts gestoßen.
Das war eine Untersuchung und Dokumentation über MegaFamilyBros. Nach eineinhalb Monaten (20.08. - 06.10.2012) habe ich das Projekt aufgegeben, einmal, weil es *zu ausführlich* bzgl. der Subroutinen war, und ansonsten kamen wieder viele andere Dinge dazwischen, die spannender waren.
Anyways, viel Spaß. Und, falls jemand noch einmal wie ich den Einstieg ins Programmieren mithilfe dieser Opensource-Homebrew-Demo sucht, kann er meinen Kram vielleicht nutzen, erweitern, fertigstellen, korrigieren. Falls irgendjemand das für sinnvoll hält. Viel Spaß.
~~~
Inhaltsverzeichnis:
-
Inhalt der Zipfile: Alle Dateien mit Ein-Satz-Erklärung. Zum Überblick.
-
Inhalt der relevanten Dateien: Zur Übersicht, welche Subroutinen (SR) und Makros in den Dateien enthalten sind. Das ist eher ein Nachschlagewerk, wo man welche SR findet, und was sie genau macht. Nicht zwangsläufig aufschlussreich, wenn man sie von oben bis unten liest.
-
Ablauf des Programms: Nennt die Reihenfolge und die Verschachtelung der relevanten Makros und Subroutinen.
Dieser Teil ist das, was man in seiner Reihenfolge lesen sollte (und dann die Einzelfunktionen genauer untersuchen solle)
-
Nutzung der Variablen: Stichwortverzeichnis, Teil 2
Inhalt der Zipfile
bg1.asm
boss1.asm
bossStep.asm
colision.asm
compilation.bat --- Batchdatei, die beim Kompilieren das Tippen in der Kommandozeile abnimmt.
deadlevel.asm
foe.Asm
gestion.asm
graphics.asm --- Leere Datei.
header.inc --- Kopfdaten. Standarddatei.
init.asm --- Weist verschiedenen Grafik-Koordinaten-Variablen Werte zu.
intro.asm --- Der Bildschirm mit dem Team-Neo-Logo.
joypad.Asm
level.asm
mainSprite.asm --- Leere Datei.
megafamilybros.asm
megafamilybros.link --- Datei relevant zur Kompilierung mit WLA DX
megafamilybros.obj --- Eine Art Zwischenergebnis-Datei von WLA DX
megafamilybros.smc --- ...
menu.asm
menuselection.asm --- Enthält die Subroutinen für die beiden Menüs (Level-/Charakterwahl).
rominfo.txt --- Sieht wie ein Dump von ZSNES oder einem anderen Emulator aus.
Snes_Init.asm --- Leert die Register für einen sauberen Start. Standarddatei.
sprite.asm
stock.asm
textdrawing.asm
ToolBox.asm --- Allerlei Macros. Für DMA, Bildschirmfunktionen, Dingelchen.
variable.asm --- Variablendefinition, SR zur Leerung/Reset der Variablen
vblank.asm --- VBlank Handling Subroutine. Ruft Subroutine "UpdateSprite" auf.
/build/wla-65816.exe --- Compiler, Teil eins
/build/wlalink.exe --- Compiler, Teil zwei
/image/ --- 45 .pic- & .clr-Dateien, also Bitplanes und Paletten
/image/etoile.pic --- Die Bilddaten der Münzen
/image/hitby.pic --- Die "Rückstoß bei Treffer"-Grafik des Körpers der Spielerfiguren im Spiel
/image/tete.pic --- Die Bilddaten der drei großen Köpfe der Spielfiguren (siehe Fußnote 1)
/image/workingtitouRun10.pic --- Die Laufanimation des Körpers der Spielerfiguren im Menü
/image/workingtitouRun10W.pic --- Die Laufanimation des Körpers der Spielerfiguren im Spiel
/image/workingtitouStand10.pic --- Die Standgrafik des Körpers der Spielerfiguren im Menü
/image/workingtitouStand10W.pic --- Die Stand- und Sprunggrafiken des Körpers der Spielerfiguren im Spiel
/image/lastText.pic.old --- eine Sicherheitskopie?
/image/master.pal --- Keine Ahnung. Letzte Änderung: 30.12.2010, im Vergleich zu allen anderen Dateien, die im März oder Mai 2011 erstellt, recht alt. Vielleicht eine Datenleiche.
/map/bg2map0.asm --- Tilemap, ASM-Datei (.DW $2000, $2000... etc. pp.)
/map/bg2map1.asm --- Tilemap, ASM-Datei
/map/map0.Asm --- Tilemap, ASM-Datei
/map/map1.asm --- Tilemap, ASM-Datei
/map/map2.asm --- Tilemap, ASM-Datei
/map/menuselection.asm --- Tilemap, ASM-Datei
/map/neo.map --- Tilemap, Binärdatei
/map/title.map --- Tilemap, Binärdatei
/spc/ --- 3 SPC-Musikdateien aus DKC, SD2 und, öh, TDSP?
/spc/loadspc.asm --- Joe Lees SPC-Player. Alles, was die Musik in MegaFamilyBros bringt
Fußnote 1: tete.pic ist #$800 Byte groß. Der Kopf der ersten Spielfigur, Hugo/Mega Krito, liegt bei #$600. Die anderen beiden Köpfe sind ab #$200. Das heißt, der Platz zwischen #$000 und #$1FF und #$400 bis #$5FF wird nicht genutzt.
~~~
Inhalt der relevanten Dateien
bg1.asm
..........
Ich bin hier noch nicht fertig gewesen!!!
boss1.asm
..........
Ich bin hier noch nicht fertig gewesen!!!
bossStep.asm
..........
Ich bin hier noch nicht fertig gewesen!!!
colision.asm
..........
Ich bin hier noch nicht fertig gewesen!!!
deadlevel.asm
..........
Ich bin hier noch nicht fertig gewesen!!!
foe.Asm
..........
Ich bin hier noch nicht fertig gewesen!!!
gestion.asm
..........
Ich bin hier noch nicht fertig gewesen!!!
init.asm
.....
SR InitBG1
.......... Setzt die Variablen XPos, XPos2, YPos und YPos2 auf #$00.
.......... Setzt die Variable SPR1XPos auf #112 (= #$70, einen 32 Pixel breiten Sprite
.......... setzt das direkt in die horizontale Bildschirmmitte).
.......... Setzt die Variable SPRYPos auf #68 (= #$44, irgendwo im oberen Drittel des
.......... Bildschirms).
.......... Setzt die Variablen BG1XOFFSET und BG2XOFFSET auf die Werte #$7414 und #$7814.
intro.asm
.....
SR Intro
..........
Am besten die Kommentare in dieser Datei nicht beachten
.......... Leert VRAM und Palette (ClearVRAM, ClearPalette)
.......... Setzt den Grafikmodus auf Mode 3 (vergesst den "Mode 1"-Kommentar.
.......... Copy & Paste-Artefakt)
.......... Setzt den Versatz (Offset) für die Tiles für BG1/BG2 ($2105) auf #$0000
.......... (unnötig, erledigt die Snes_Init.asm schon und der Wert wird dazwischen
.......... nicht geändert).
.......... Der Anfang der Tilemap wird bei $C000 gesucht (durch das #$C0 --> $2107)
.......... Mittels CopyVRAM verschiebt er erst die Tilemap und dann die zugehörigen
.......... Tiles in den VRAM. Als nächstes werden die 256 Farben der Palette per
.......... introPalSelection-Loop übertragen.
.......... Als nächstes leert er einige Register für das BG-Scrolling ($210E - $2110),
.......... die aber dank Snes-Init.asm schon leer sein müssten.
.......... Er leert die Variable XPos (for whatever reason), aktiviert BG1 (der Kommentar
.......... sagt BG1 & 2; was allerdings nicht stimmt. Übrigens ist in der Zeile darüber ein
.......... Tippfehler im lda-Befehl. Er gibt nur eine 7stellige Zahl an. Nett, dass der SNES
.......... bzw. WLA scheinbar doch versteht, was er meint).
.......... Danach erlaubt er NMIs und Joypad-Interaktion, wobei ich nicht direkt weiß,
.......... weshalb es notwendig ist, das erst zu machen, wenn man einen kompletten
.......... Screen aufgebaut hat(!?)
.......... Danach lässt er die Bildschirmhelligkeit ganz dunkel auf ganz hell hochlaufen,
.......... indem er loopToLight hochlaufen lässt. relevant ist: loopToLight ist so programmiert,
.......... dass der Status der vollständigen Helligkeit niemals erreicht wird. Und, statt dass
.......... er seine Subroutine umschreibt, schiebt er das dann lieber per Hand nach (durch
.......... die Zeilen "lda #%00001111" und "sta $2100".
.......... Als nächstes folgt der "loopIntro":
.......... "WAI" heißt: Warte auf die nächste vertikale Austastlücke. Ich bin mir nicht sicher,
.......... ob das benötigt ist, dass das SNES ein "Zeitfenster" bekommt, die Buttonsignale
.......... auszulesen.
.......... Das nächste ist der "HandleJoyIntro"-Loop, in dem solange gewartet wird, bis der
.......... Controller bereit ist, dass man seine Signale auslesen kann. Ich bin mir auch hier
.......... nicht sicher, aber ich kann mir nicht vorstellen, dass das bei jedem Auslesen nötig
.......... ist, sondern nur einmal am Anfang des Programms. Könnte man mal testen, ob es auch
.......... ohne ginge.
.......... Als nächstes wird das Register ausgelesen, in das hineingeschrieben wird, wenn u. a.
.......... Startbutton gedrückt wird ($4219), es wird der eine Bit für den Start-Button
.......... freigestellt (and #JoyStart), und anschließend geprüft, ob er gesetzt ist. Andernfalls
.......... gehts wieder an den Anfang des loopIntro.
.......... Wenn gedrückt wurde, gehts zu FinIntro (so viel französisch kann man ja. ;D), wo
.......... "loopToDark" aktiviert wird, anschließend wird diese Subroutine beendet.
joypad.Asm
.....
MACRO handleInput
..........
Ich bin hier noch nicht fertig gewesen!!!
level.asm
.....
SR levelmatch
..........
Entsprechend dem Inhalt von Variable level, die in LoopMenuLevel gesetzt wird, werden hier
..........
die Leveldaten geladen.
..........
.......... Es wird zu Beginn wird die Variable level in den Akkumulator geladen und nach den drei Fällen
.......... #$00, #$01 und #$02 entschieden, ob Level 0, 1 oder 2 geladen werden muss.
.......... Der folgende Ablauf ist prinzipiell ähnlich, unterscheidet sich nur durch die jeweiligen
.......... Daten, die verarbeitet werden.
.......... ClearVRAM und ClearPalette werden aufgerufen.
.......... Es werden Adresse der Tilemaps für BG1 und BG2 sowie deren Bank in die Variablen map0, map0Bank,
.......... map1 und map1Bank geladen.
.......... Im folgenden wird in die Variable blockLimit ein Wert (Level 2: #$48, sonst: #$A0).
.......... Der Bildschirm wird komplett abgeschaltet (die letzte Stufe, die bei loopToDark grund-
.......... sätzlich fehlt), ab diesem Punkt werden einige SR aufgerufen, die sich bei den Levels
.......... unterscheiden:
..........
..........
..........
..........
..........
Ich bin hier noch nicht fertig gewesen!!!
megafamilybros.asm
..........
Ich bin hier noch nicht fertig gewesen!!!
menu.asm
.....
SR title
.......... Ist de facto dasselbe wie die SR intro, siehe deren Erklärung.
.......... Unterschied ist, dass logischerweise einige Labels anders heißen (weil sie sonst
.......... doppelt definiert wären, WLA so nicht arbeiten kann und dann erstmal Brotzeit macht)
.......... und dass am Anfang der Bildschirm ausgemacht wird (Force Blank, "lda #%10000000" und
.......... "sta $2100"). Logischerweise kann einiges hier auch wegfallen, weil bereits in der
.......... Intro-SR viele Dinge doppelt sind.
.....
MACRO LoopMenu
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
MACRO SetMode1ForMenu
.......... Allerlei Videoeinstellungen, nichts weltbewegendes ($2105, $210b, $2107, $2108):
.......... Die BG-Einstellung auf Mode 1, setzt die Adressen der Tilemaps für BG1 und BG2 fest.
.......... Mehr nicht.
menuselection.asm
.....
MACRO changeColor (b, b, b)
.......... (A muss 8 Bit groß sein, SEP #$20)
.......... Das erste Argument enthält die Nummer der Farbe in der Palette, die geändert werden
.......... soll, das zweite Argument ist der Low Byte, das dritte Argument der High Byte des
.......... Farbwerts.
.....
MACRO updateSprXY (b, b)
.......... A wird auf 8 Bit gesetzt (SEP #$20)
.......... Das erste Argument wird in SPR1XPos gespeichert, das zweite in SPR1YPos.
.....
SR LoopMenuSelection
..........
Dasselbe wie LoopMenuLevel, mit einigen Abweichungen. Führe hier nur die Abweichungen auf.
..........
.......... BG1 und BG2 werden ohne horizontal flip und vertical flip initiiert (Register $2107 und $2108).
.......... Ich denke, dass hat keinen Effekt auf das Endergebnis.
.......... Logischerweise wird für BG1 eine andere Datei geladen (das ist die "Schicht", in der der
.......... Menütext erscheint, wo logischerweise jetzt die Namen der Figuren erscheinen sollen).
.......... Ein Teil zur Übertragung einer Sprite-Palette, der in LoopMenuLevel auskommentiert war,
.......... ist hier aktiv - und überschreibt Werte, die in InitSpriteAndPalette geschrieben und nicht
.......... genutzt wurden.
.......... Der Vollständigkeit halber sei erwähnt, dass ein Teil, der sich mit dem Laden einer Musikdatei
.......... beschäftigt, in "LoopMenuLevel" auskommentiert ist, und hier komplett fehlt.
.......... Es werden einige Variablen mit Werten gefüttert, die die Sprites betreffen, zusätzlich Spielwerte
.......... (Geld, Lebensenergie des Spielers).
.......... Die "tempB"-Variable wird geleert. (Wird hier die Nummer des ausgewählten Elements enthalten)
..........
.......... (loopSelection)
.......... Im Loop werden nach dem Ablauf des Timers (alle 6 Durchläufe) folgende Subroutinen aufgerufen:
.......... mainSpriteAnimation, updateSpriteAnimation und updateSpriteHead:
............. Durch den Wert von #$00, der zuvor in "SPR1ACTION" gespeichert wurde, und dem Wert #$01 in "SPR1STATUS"
............. wird mittels mainSpriteAnimation erwirkt, dass in "spr1Adress" die Adresse der ersten Grafik für die
............. Laufanimation steckt, sodass diese Grafik dann mit updateSpriteAnimation in den VRAM übertragen wird.
............. Der "richtige" Kopf wurde mit der Eingabe von "1536" (= #$600) in headoffset festgesetzt, wobei diese
............. Bilddaten dann mit updateSpriteHead übertragen werden. In der Grafikdatei für die Köpfe, tete.pic, liegt
............. der Kopf für die erste Figur bei $600.
.......... Anschließend wird, unabhängig vom Timer, bei jedem Durchlauf dieser SR die SR MajSprite aufgerufen.
..........
.......... (Ab hier (d. h. Label followStepMenü) erfolgt, sicherheitshalber, wieder eine Zeile-für-Zeile-Betrachtung.
..........
.......... Im nächsten Schritt (ab Label HandleJoy) wird geprüft, ob der Controller bereit ist; ist dies der Fall, werden
.......... die Daten ausgelesen und geprüft, ob der Rechts-Button gedrückt wird. Wenn, dann wird geprüft, ob tempB = 2 ist.
.......... tempB enthält den Wert des aktuell ausgewählten Spielers (null bis zwei). Ist tempB = 2, wird dasselbe gemacht,
.......... wie wenn Rechts nicht gedrückt wird (ab Label noRight).
.......... (ab Label updateTempB) Wenn rechts gedrückt wird und tempB < 2 ist, wird tempB um eins erhöht und die SR
.......... changeMapSelection aufgerufen, um das Fenster für die nächste Spielfigur darstellen zu lassen.
.......... (ab Label noRight) Wenn rechts nicht gedrückt wird, wird im selben Verfahren wie oben geprüft, ob der Links-
.......... Knopf gedrückt wurde. Wenn nicht, geht es weiter bei noLeft. Ansonsten wird tempB um eins verringert, wenn es
.......... nicht sowieso schon null ist, und changeMapSelection aufgerufen, um ein anderes Fenster auf den Bildschirm zu
.......... bringen.
.......... (ab Label noLeft) Nun wird geprüft, ob Start gedrückt wird. Wenn ja, geht es weiter bei FinSelection. Ansonsten
.......... wird mittels Variables XPos der karierte Background gescrollt und am Ende der loopSelection von vorne begonnen.
.......... (ab Label FinSelection) Der Bildschirm wird abgedunkelt und die Subroutine verlassen.
.....
SR initplayervalue
.......... (Mega Krito ingame; Hugo in den Kommentaren)
.......... Wenn "player" den Wert "0" besitzt, wird "argent" (Geld) auf 0 und "playerlife" auf 2 gesetzt.
.......... Nächster Sprungpunkt: initplayer2
.......... (Mega Kikoo ingame; Tristan in den Kommentaren)
.......... Wenn "player" den Wert "1" besitzt, wird "argent" (Geld) auf 10 und "playerlife" auf 3 gesetzt.
.......... Nächster Sprungpunkt: initplayer3
.......... (Mega Mame in Game; Mae in den Kommentaren)
.......... Wenn "player" den Wert "1" besitzt, wird "argent" (Geld) auf 10 und "playerlife" auf 5 gesetzt.
.....
SR changeMapSelection
..........
SR, die den Bildwechsel zwischen den Bildschirmen der unterschiedlichen Spielfiguren herbeiführt
..........
.......... Der Bildschirm wird langsam abgedunkelt (via loopToDark).
.......... Es wird geprüft, ob tempB = 0 ist, wenn nicht, wird zu Label test2 (für den zweiten Test) gesprungen.
.......... Bei test2 wird geprüft, ob tempB = 1 ist, wenn nicht, ist es logischerweise 2, in diesem Falle geht es bei
.......... Label test3 weiter.
.......... Das Handling dieser drei Fälle ist im Prinzip dasselbe, nur die Werte unterscheiden sich. Es wird zunächst
.......... die Tilemap per CopyVRAM-Makro in den VRAM kopiert. Als nächstes werden die Sprite-Koordinaten mit
.......... updateSprXY neu gesetzt. Mittels changeColor werden zwei Farben geändert (die für die Rüstungsteile z. B.
.......... an den Knien und Unterarmen). HeadOffset wird geändert, dass beim nächsten "updateSpriteHead" andere
.......... Grafikdaten für den Protagonisten-Kopf in die dafür vorgesehene VRAM-Adresse geladen werden.
.......... Es wird der SPR1XSpeed auf #02 gesetzt und eine Nummer in "player" gespeichert, die der jeweiligen
.......... Spielerfigur entspricht (01 bis 03).
.......... Danach wird zum Abschluss (ab Label endSelection) MajSprite aufgerufen und mit updateSpriteHead
.......... noch die richtige Grafik in den VRAM geladen, danach mit loopToLight der Bildschirm wieder an
.......... gemacht und die SR beendet.
.....
SR LoopMenuLevel
.......... (Hat große Ähnlichkeit mit der SR intro)
..........
Stellt den Level-Wahl-Bildschirm da. BG2 enthält ein Kachelmuster, das sich kontinuierlich
..........
bewegt; BG1 enthält Schrift ("Level 0"), wobei man mit der Hoch- und Runter-Taste zwischen
..........
Level 0, 1 und 2 wählen kann.
..........
LoopMenuLevel ist in vielen Punkten Identisch mit LoopMenuSelection am Anfang der menuselection.asm.
..........
Der größte Unterschied ist, dass sich in LoopMenuLevel viele Einstellungen, die mit Sprites zu tun
..........
haben, auskommentiert sind. LoopMenuLevel wurde auf der Grundlage von LoopMenuSelection geschrieben
..........
und alles, was daraus nicht benötigt wurde, wie z. B. die Sprites, wurde so einfach deaktiviert.
..........
.......... Bildschirm aus (Force VBlank)
.......... Leert den VRAM.
.......... Bildschirmeinstellungen (mit einigen unnötigen Einstellungen)
.......... Per CopyVRAM-SR werden die Tilemaps für die beiden BGs an die entsprechenden
.......... Positionen im VRAM übertragen.
.......... Alles zwischen dem ";Load Palette" und "bne LoadPalLevel" dient dem
.......... Übertragen der Palette in den CGRAM.
.......... Alles danach bis zum "bne LoadPalSelectionSprite" dient dem Leeren des Sprite-
.......... speichers (OAM) und dem Befüllen mit einer Palette - alles auskommentiert, da
.......... beim Level-Wahl-Bildschirm keine Sprites verwandt werden.
.......... Danach werden die Tiles für BG1 und BG2 per CopyVRAM-Makro übertragen. Beide
.......... BGs benutzen dasselbe Tileset, was auch Sinn macht, da beide nicht viele Tiles
.......... enthalten.
.......... Bildschrim wird weiterhin eingestellt (Sprites, BG2 und BG1 werden dargestellt: $212C)
.......... Es werden *wieder einmal* VBlank und JoyPad aktiviert (EnableVBlankWithJoypad).
.......... Die Register $210D bis $2110 werden auf null gesetzt - diese Register sind für die Verschiebung
.......... von BG1 und BG2 zuständig.
.......... Die Variable XPos wird geleert, genauso wie "tempB", die laut Kommentar als Container für
.......... das gewählte Element (Level) fungiert (was nicht stimmt; es wird hier "level" genutzt; "tembB"
.......... wird in LoopMenuSelection genutzt).
.......... (Auskommentiert sind einige Einstellungen der Sprite-Einstellungen)
.......... Es wird der Bildschirm von komplett dunkel auf komplett hell hochgedreht (von "jsr
.......... loopToLight" bis "sta $2100")
.......... "cli" ermöglicht Interrupts. Ich halte es hier für unnötig, lasse mich aber gern eines
.......... Besseren belehren.
.......... Es wird noch eine Grafikeinstellung vorgenommen, die was mit dem Aufbau von Tilemaps zu
.......... tun hat (#$00 --> $2115), danach geht es in einen etwas komplizierten Loop.
.......... loopLevel:
.......... Springt zur SR showLevel (steht ganz am Ende der Datei)
.......... Erhöht den Inhalt von "Timer", "Timer" läuft konstant von #00 bis #05 (bei #06 wird Timer
.......... auf #00 resettet).
.......... Das Register mit den Controller-Daten wird ausgelesen ($4219) und geprüft, ob "Hoch" gedrückt wurde.
.......... Wenn, dann wird geprüft, ob keyflag = 0 ist (= ob gerade "Hoch" gedrückt wurde, oder ob es
.......... gehalten wird, das wäre dann keyflag = 1). Wenn nicht, dann wird nichts am Level geändert
.......... (das bedeutet die Jump-Mark "NothingLevel").
.......... Nun wird die Variable level geladen und geprüft. Wenn sie "2" ist, kann sie nicht weiter
.......... erhöht werden ("beq NothingLevel"), ansonsten wird die level um eins erhöht und der
.......... keyflag auf 1 gesetzt. Danach wird die Prüfung, ob "runter" gedrückt wurde übersprungen
.......... und direkt dasselbe gemacht, als wenn das Level nicht geändert wird (--> NothingLevel).
.......... Die "Runter"-Prüfung funktioniert genauso wie der Teil vor: Verringere level, wenn level
.......... nicht 0 ist, und wenn der keyflag nicht 1 ist. Ansonsten gehe direkt zu "NothingLevel",
.......... andernfalls anschließend.
.......... Bei "NoDown:" wird geprüft, ob Start gedrückt wird. Wenn ja, wird zu FinLevel gesprungen,
.......... der Bildschirm mit LoopToDark gemacht und diese Subroutine beendet.
.......... Sind die Button-Prüfungen (Hoch, Runter, Start) erfolglos geblieben, wird jetzt mit
.......... "stz keyflag" der keyflag auf null gesetzt. Danach laufen alle Fäden wieder zusammen, die
.......... bei der Button-Prüfung auseinanderliefen. Das NothingLevel-Label wird gesetzt.
.......... Danach wird mittels der Variable XPos der Kachelhintergrund eins weitergeschoben und
.......... wieder zum Anfang von loopLevel gesprungen.
.....
SR showLevel
.......... "showLevel" prüft den Inhalt der Variable "level" und verändert die den entsprechenden
.......... Tile, sodass auf dem Bildschirm "Level 0", "Level 1" oder "Level 2" steht.
sprite.asm
.....
SR mainSpriteAnimation
..........
Eine Subroutine, die in Abhängigkeit vom Inhalt der Variablen SPR1ACTION und SPR1STATUS
..........
entscheidet, welche Grafikdaten in den VRAM geschoben werden müssen. Das heißt, in dieser
..........
Subroutine werden diese beiden Status-Variablen in die entsprechenden Grafiken umgesetzt.
..........
Es sei darauf hingewiesen, dass Dieudunet nicht alle kompletten Animationsschritte seiner
..........
Figuren in den VRAM lädt und dann dem jeweiligen Sprite im OAM eine neue Grafik zuweißt,
..........
sondern dem Sprite eine "feste Adresse für seine Grafik" im OAM zugewiesen hat.
..........
Pro: Er spart Platz im VRAM, weil er nicht jeden der Animationsschritte dort "vorrätig" hat.
..........
Contra: Er muss jedes Mal #$200 Byte Grafikdaten updaten, statt #$4 Byte im OAM. Größere
..........
CPU-Belastung.
..........
.......... SPR1ACTION = %1xxxxxxx
............. Es wird die Adresse von spr1StandW in spr1StandAdress gespeichert.
............. Es wird die Adresse von spr1RunW in spr1RunAdress gespeichert.
.......... SPR1ACTION = %0xxxxxxx (ab Label "NoSpecialAction")
............. Es wird die Adresse von spr1Stand in spr1StandAdress gespeichert.
............. Es wird die Adresse von spr1Run in spr1RunAdress gespeichert.
...............
Als Hinweis: Die beiden Dateien mit dem W am Ende werden im Spiel selbst für die Stand- bzw.
...............
Laufanimation genutzt, die beiden Dateien ohne W nur im Menü.
.......... SPR1ACTION <> %x00xxxxx (ab Label "specialOK")
............. Es wird die Adresse der "hitby"-Grafik in "spr1Adress" gespeichert (und dann beim nächsten
............. Aufruf von "updateSpriteAnimation" hochgeladen).
............. Als nächstes wird der Inhalt von "sprite1Offset" von #0 auf #512 (#$200) gewechselt (oder andersrum)
............. Dieser neu berechnete Wert wird in "sprite1Offset" abgespeichert.
............. Anschließend wird dieser Wert mit "spr1Adress" addiert und in "spr1Adress" gespeichert.
.............
Das bedeutet, dass jeweilswechselnd die Adresse des ersten und zweiten Sprites in der Variable steht.
.......... SPR1ACTION = %x00xxx00 (ab Label "continueUpdate")
............. Es wird geprüft, ob "SPR1STATUS" auf null steht (
das "AND #$FF" kann man sich doch sparen, oder?)
............. Das bedeutet, wenn in dieser Variable ein anderer Wert als null ist, läuft noch eine andere
............. Animation.
.............
............. SPR1STATUS = %00000000
............... Wenn "SPR1STATUS" auf null steht, wird der Inhalt von "spr1StandAdress" in "spr1Adress"
............... gespeichert (also wird mit dem nächsten "updateSpriteAnimation" eine Grafik aus der jeweiligen
............... Datei in den Speicher geschoben, die zu Beginn dieser Subroutine in "spr1StandAdress" gespeichert
............... wurde).
............... Es wird #0512 (#$200) in spr1Offset gespeichert, was anscheinend der Standardwert ist.
............... Anschließend wird dieser Wert auf den Adresswert in "spr1Adress" gerechnet und dort wieder
............... abgespeichert (scheinbar soll der erste Sprite der Datei [die ich mal auf #$200 Byte Größe
............... schätze] grundsätzlich übersprungen werden, vermutlich weil das leer ist?).
............... Danach springt er zum Ende der SR (Label "spritemainend", direkt vorm "RTS").
............. SPR1STATUS = %xxx0xxxx (ab Label "MoveSpriteMain")
............... Es wird die Adresse der Grafikdaten für die Laufanimation in "spr1RunAdress" in "spr1Adress"
............... gespeichert. Der Wert in "spr1Offset" wird um #0512 ($200) erhöht. Ist der Wert in "spr1Offset"
............... #5120 (#$1400), wird er wieder auf 0 gesetzt.
............... Es wird jetzt der Offset auf den Wert in "spr1Adress" draufaddiert und das wieder in "spr1Adress"
............... gespeichert. Danach wird die SR beendet (Jump to Label "spritemainend").
............. SPR1STATUS = %xxx1xxxx (ab Label "MoveSpriteJumpUpMain") und
............. SPR1ACTION = %xxxxxx01 (ab Label "MoveSpriteJumpUpMain")
............... Wie in der Verfahrensweise, die schon mehrfach in dieser SR angewandt wird, wird die
............... Adresse des dritten Sprites in der Grafikdatei mit den Stand- und Sprungsprites (#1024 = $#0400)
............... in "spr1Adress" gespeichert und der "spr1Offset"-Wert auf erwähnte #1024 gesetzt.
............... Der dritte Sprite ist offensichtlich der Sprite für den Aufwärtssprung.
............. SPR1STATUS = %xxx1xxxx (ab Label "MoveSpriteJumpDownMain") und
............. SPR1ACTION <> %xxxxxx01 (ab Label "MoveSpriteJumpDownMain")
............... Wie in der Verfahrensweise, die schon mehrfach in dieser SR angewandt wird, wird die
............... Adresse des vierten Sprites in der Grafikdatei mit den Stand- und Sprungsprites (#1536 = $#0600)
............... in "spr1Adress" gespeichert und der "spr1Offset"-Wert auf erwähnte #1536 gesetzt.
............... Der vierte Sprite ist offensichtlich der Sprite für den Fall/"Abwärtssprung".
.....
SR updateSpriteHead
.......... Addiert die Variable headOffset mit der Adresse der Grafikdaten tete (die Daten für die
.......... übergroßen Köpfe) und speichert Sie in Temp.
.......... Danach werden vier Mal 128 Byte in den VRAM übertragen, begonnen werden mit den Daten,
.......... deren Adresse in "Temp" steht. Übertragen werden in die Positionen $6040, $6140, $6240 und
.......... $6340.
Wichtig: Das war gerade auch ein Verständnisfehler von mir, darum weise ich darauf hin:
.......... 128 Byte sind $80 Byte. Es werden aber NICHT folglich z. B. die Positionen $6040 bis $60A0 beschrieben,
.......... sondern nur $6040 bis $604F, denn jedes Register ($6040, $6041...) kann jeweils 8 Byte aufnehmen, also
.......... braucht man für 128 Byte (128 = $80 ... $80 : $8 = $10 ... $10 = 16) nur 16 Register mit je einem Byte
.......... Speicherplatz, um diese Daten abzuspeichern.
.....
SR updateSpriteAnimation
.......... Schiebt vier Mal 128 Byte in den VRAM, gemäß dem Startwert, der in der Variable "spr1Adress"
.......... steht. Der Wert in "spr1Adress" ist nach Aufruf dieser SR um $240 höher als vorher.
.....
SR updateWeapon
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
SR updateWeaponAnimation
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
SR InitSpriteAndPalette
.......... Die Größe der Sprites und deren Ablageposition ab $C000 eingestellt.
..........
Letzteres interpretiere ich so. Das muss geprüft werden, ob das stimmt.
.......... Es werden die Adressregister für CGRAM und OAM auf die Anfangspositionen für Spritedaten
.......... gesetzt (was beim CGRAM #128 und beim OAM #00 ist).
.......... Danach werden per Schleife Daten übertragen:
............. "palette" (in der Schleife "palette2")
............. "foe1Pal" (in der Schleife "pal")
............. "Tir1Pal" (in der Schleife "paltir")
............. "ghostpal" (in der Schleife "ghosttir")
............. "boss1Pal" (in der Schleife "bosspal")
............. "biosparkpal" (in der Schleife "biosparkPal")
.......... Augenscheinlich werden hier keine Spritedaten übertragen.
.......... Anschließend wird die Variable "priority" auf #$20 gesetzt und die SR beendet.
.....
SR updateSXSprite
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
MACRO pushSXfromX (b)
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
MACRO pushXfromX (b)
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
SR forSXinitXY
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
SR getSXinitXY
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
SR initPaletteFoe
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
SR MajSprite
..........
Updated die Einträge der ersten beiden Sprites im OAM - Sprite 0 ist der Kopf, Sprite 1
..........
ist der Körper -, wenn sich die Positionen oder die horizontale Spiegelung (links/rechts)
..........
ändern müssen.
..........
.......... Mit dem "#%00001010" in dem OAMTemp+512 schreibt er in die "High Table" des OAM für die ersten
.......... beiden Sprites, dass diese "groß" sind, in diesem Fall 32x32 Pixel statt normal 16x16 Pixel.
.......... Als nächstes werden die Daten des Kopfes geupdated:
.......... Die X-Position wird SPR1XPos entnommen und in den ersten Byte der "Low Table" gespeichert.
.......... Die Y-Position wird mit 4 addiert und dann in den zweiten Byte der "Low Table" gespeichert;
.......... die +4 sind augenscheinlich der Versatz, mit dem der Kopf-Sprite über den Rumpf-Sprite überlappt,
.......... damit es so aussieht, dass der Kopf auf Hals und Schultern sitzt, und nicht unverbunden darüber
.......... schwebt.
.......... Im dritten Byte der "Low Table" wird #%00000100 geschrieben, das heißt, Grafik Nummer 4 soll
.......... für den Sprite genutzt werden, was nicht verwundert, da in updateSpriteHead an diesen Platz die
.......... Kopf-Grafikdaten gespeichert werden (was die vier in der Adresse $6040 und folgende bedeutet).
.......... Der Wert für den vierten und letzten Byte wird der Variable priority entnommen.
..........
.......... Anschließend wird in priority geprüft, ob Bit 6 gesetzt ist. Wenn priority in den vierten Byte
.......... eines OAM-Datensatzes eingesetzt wird, entscheidet dies über eine horizontale
.......... Spiegelung, also bedeutet diese Prüfung hier, ob so gesagt werden soll, dass die
.......... Spielfigur nach links guckt.
..........
.......... Abhängig von der Blickrichtung wird auf SPR1XPos 3 addiert (rechts) oder subtrahiert (links).
.......... Dies erfolgt, um den Körper richtig "unter den Kopf" zu positionieren. Danach wird SPR1XPos
.......... im OAMTemp gespeichert.
.......... Für den zweiten Byte des Körper-Sprite-Datensatzes zu SPR1YPos 24 hinzuaddiert. Da hier wiederum
.......... nicht 32, sondern 24 addiert wird, bedeutet das, dass der Körpersprite noch einmal 8 Pixel "nach
.......... oben" geschoben wird, um direkt unter/am dem Kopfsprite zu sein.
.......... Der dritte Byte wird der Variable sprite entnommen, der vierte wieder priority, und damit endet
.......... die Subroutine.
.....
MACRO initbonus
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
SR realY
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
SR checkIfMinus
.......... Funktion ohne Wert. Die Indexregister werden auf den Stack verschoben und wieder
.......... heruntergezogen. Vermutlich war mal früher dazwischen etwas passiert.
.....
SR destroyBonusAndUpdateMoney
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
SR destroySprMethod
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
SR bonusSpriteAnimation
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
MACRO UpdateSprite
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
MACRO ClearOAM
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
SR updateSpriteTir1
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
SR CopyTir1
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
SR tir1AnimBeginLeftMove
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
SR updateAnimationTir1
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
SR checkIfFallAndDied
..........
Ich bin hier noch nicht fertig gewesen!!!
stock.asm
.....
SR controlUpdateFoe
..........
Ich bin hier noch nicht fertig gewesen!!!
textdrawing.asm
.....
MACRO writeValue
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
SR rwvalue
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
SR resfreshLife
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
valueReference1To10
.......... Direkte Word-Definition: Eine Zahlenfolge von #$2008 bis $202A, immer in Zweierschritten erhöht.
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
zerovalue
.......... Direkte Word-Definition: #$2008
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
SR resfreshLife
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
SR push2119followingBG
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
SR incTimeandWrite
..........
Ich bin hier noch nicht fertig gewesen!!!
.....
SR writeText
..........
Ich bin hier noch nicht fertig gewesen!!!
ToolBox.asm
..... Kommentar Aufbau des Statusregisters (Flags)
.....
MACRO registerATo16bit
.......... Enthält nur ein einziges "REP #$30". ô_o
.......... Beim zweiten Gucken ist das aber doch interessant. "REP #$30" setzt nämlich
.......... gleichzeitig auch X und Y auf 16 bit zurück. Lässt mich vermuten: Der Programmierer
.......... kam wohl niemals in die Verlegenheit, X und Y auf 8 bit setzen zu müssen.
.....
MACRO pushToColor (b, b)
.......... SEP #$20 (8 bit Akkumulator)
.......... Nimm den ersten bytegroßen Wert, der dir übergeben wurde, speicher ihn in "darkValue".
.......... Nimm den zweiten bytegroßen Wert, der dir übergeben wurde, speicher ihn in "tempB".
.......... Rücke vor bis auf SR pushToColor.
.....
SR pushToColor
.......... Verringert die Bildschirmhelligkeit von "darkvalue" auf "tempB":
.......... "darkValue" wird genommen und per Schleife so oft in $2100 gespeichert (Register
.......... für Bildschirmhelligkeit) und um eins verringert, bis der Wert "tempB" entspricht.
.....
SR pushToDark
.......... Verringert die Bildschirmhelligkeit von 100% auf 0%:
.......... "darkValue" wird auf #$0F setzt (Achtung! Er schreibt hier in Dezimal-, nicht in
.......... Hexadezimalzahlen, also: #15). In einer Schleife wird der Wert um eins verringert
.......... und wieder in $2100 (Register für Bildschirmhelligkeit) gespeichert, bis
.......... "darkValue" = 0 ist.
.....
SR pushToLight
.......... Verringert die Bildschirmhelligkeit von 0% auf 100%:
.......... Wie pushToDark, nur umgedreht.
.....
MACRO registerATo8Bit
.......... "sep #20"
.....
MACRO clearWord (w)
.......... Löscht den Byte, dessen Adresse man übergibt, und den daneben.
.....
MACRO CopyVRAM (w, label, w)
.......... Checker-Erklärung: VRAM-DMA, über Channel 0 (Ziel: $2118/9)
.......... Non-Checker-Erklärung: Makro, dass schnell große Mengen an Daten an den VRAM (Grafikspeicher)
.......... überträgt. Dieses Makro dient zum Übertragen von Tilemaps, Paletten oder Grafiken (für
.......... Tiles oder Sprites).
.......... Argumente:
............. (1) Ziel im VRAM ($2116)
............. (2) Adresse der zu übertragenen Daten
............. (3) Anzahl der Bytes, die zu übertragen sind
.....
MACRO CopySprVRAM (w, w-variable, label(?), w)
.......... Wie CopyVRAM, aber andere Argumente
.......... Argumente:
............. (1) Ziel im VRAM ($2116)
............. (2) Adresse der zu übertragenen Daten (Offset/ "Versatz" wird übernommen)
............. (3) Adresse der zu übertragenden Daten (Bank wird übernommen)
............. (4) Anzahl der Bytes, die zu übertragen sind
.......... Hinweis: Dieses Makro unterscheidet sich in einem Punkt von CopyVRAM, dass
.......... der Versatz (engl. Offset) und die Bankadresse von zwei unterschiedlichen
.......... Argumenten übernommen werden.
.......... Dabei ist zu beachten: CopyVRAM nimmt bei der Offset-Adresse ein Label (#\2), CopySprVRAM
.......... nimmt den
Inhalt einer ihm übergebenen Variable (\2).
..........
Ein Checker möge bitte prüfen, ob ich das hier falsch herum verstanden habe
.....
MACRO addToPtrDirect (w, w)
.......... Nimm die Adresse von Argument zwei, addiere die Adresse von Argument eins, und speicher
.......... das Ergebnis in Argument eins. Logischerweise müssen Argument eins eine Variable/
.......... Speicheradresse sein.
.....
MACRO addToPtr (w, w)
.......... Nimm die Adresse von Argument eins, addiere
den Wert von Argument zwei, und speicher das
.......... Ergebnis in Argument eins. Hier muss nur Argument eins eine Variable/Speicheradresse
.......... sein.
.....
MACRO CopyMapVRAM (w, w-variable, b-variable, w)
.......... Wie zuvor, jedoch sind sowohl Bank als auch Offset in einer Variable gespeichert.
.....
SR(?) ClearVRAM
.......... Würde mich wundern, wenn das funktioniert, weil nach ClearVRAM ein Semikolon steht.
.......... Gehört dort nicht ein Doppelpunkt hin?
.......... ClearVRAM überschreibt in einer Schleife den gesamten Inhalt im VRAM mit #$0000
.....
MACRO EnableVBlankWithJoypad
.......... Checker-Erklärung: $4200 = #$81. Mehr nicht.
.......... Non-Checker-Erklärung: Schreibt in das Interrupt-Enable-Register ($4200), dass es
.......... Interrupts (Unterbrechungen) durch die vertikale Austastlücke (VBlank / NMI)
.......... und durch das Drücken von Buttons auf Controllern zulassen soll.
.......... Ersteres schafft die Möglichkeit, Animationen ablaufen zu lassen, zweiteres
.......... ermöglicht dem Programm, auf Befehle vom Controller zu reagieren.
.......... Das ist für Spiele so essenziell, wie es sich anhört.
.....
SR clearPalette
.......... (Schon wieder dieses Gegurke mit dem Semikolon, wo ich einen Doppelpunkt erwartet
.......... hätte... was soll das!?)
.......... Soll die Palette leeren. Verstehe ich aber nicht wirklich.
.......... 1) Wenn ich LDA.l 0, x mache, wobei X gleichzeitig als Counter genutzt wird,
.......... lädt er nach meinem Verständnis auf der aktuellen Bank aus den Registern $0000, $0001 etc.
.......... die Werte und packt sie in den CGRAM als Farben... das wiederum trifft nicht das, was ich
.......... unter "clearPalette" verstehen würde. Der nächste Punkt: Warum läuft der Counter bis
.......... #$64 (= hundert)? Benutzt er maximal 100 Farben? Oder ist es schlichtweg gurkig
.......... programmiert (wovon ich nicht ausgehe)?
variable.asm
.....
Variablendefinition
.......... Alle Spielwerte werden ab $C8(00) definiert.
.......... Anschließend werden die Adressen für den temporären Speicherplatz für die Spritedaten
.......... definiert, auf Bank $7E (das ist IIRC der RAM), bei $1600 und bei $1801 (und damit müsste
.......... $1800 doch eigentlich leer sein und bleiben, oder?).
.......... Als drittes werden die Namen für die Controllertasten definiert.
.......... Danach kommt eine Definition der Register der Register zur Kommunikation mit dem SPC700.
.......... Als letztes werdeen "XY_8BIT" und "A_8BIT" definiert, ganz offensichtlich sind die
.......... angedacht, das wechseln zwischen 8bit- und 16bit-Registern zu vereinfachen (SEP/REP).
.....
SR emptyVariable:
.......... Entsprechend dem Namen werden viele der Spielwert-Variablen geleert.
.......... Ausnahmen:
.......... SPR1ACTION --- wird gesetzt auf #$02: "; Falling"
.......... SPR1YSpeed --- wird gesetzt auf #$01
.......... headOffset --- wird gesetzt auf #512 (#$200)
.......... jumpCount --- wird gesetzt auf #19 (#$13): "; falling index"
.......... bossLife --- wird gesetzt auf #$03
vblank.asm
.....
SR VBlank
.......... Die Subroutine, die abläuft, wenn das eine Bildschirmbild fertig dargestellt wurde
.......... und das nächste Bildschirmbild noch nicht begonnen wurde, dargestellt zu werden.
.......... Maßgeblich werden die Registerinhalte auf den Stack geschoben, die Subroutine "UpdateSprite"
.......... aufgerufen und anschließend die Register wieder vom Stack gezogen.
/spc/loadspc.asm
.......... Als allgemeiner Kommentar:
.......... Ich werde diese Datei nicht kommentieren, weil das für mich zu Hardcore ist. Tatsächlich ist
.......... der Inhalt dieser Datei, wie der Kommentar auch anzeigt, de facto der SPC Player von Joey Lee,
.......... den er unter Public Domain gestellt hat und der u. a. auch
hier erklärt wird.
.......... Glaube ich.
.......... Gelesen habe ich es bisher noch nicht.
.......... Dieses Programm ist BTW auch mit Sicherheit der Grund, weshalb das Spiel nur Musik und keine
.......... Soundeffekte hat. (Bei dem Kram, der jetzt kommt, bin ich mir ziemlich sicher. Wenn jemand es
.......... besser weiß, mag er das bitte korrigieren).
.......... Schon mal aufgefallen, dass SPC-Dateien mit Vorliebe 64kb groß sind? SPC-Dateien sind nämlich
.......... "Gipsabdrücke" des RAM vom SPC700. Das heißt: Es wurde geguckt, was im Arbeitsspeicher des
.......... Soundchips liegt, wenn ein Lied abgespielt wird, und dann *alles* rüberkopiert. Selbiges
.......... Verfahren rückwärts ermöglicht z. B. auch den SPC-Player (auf vergleichsweise einfache Art und
.......... Weise). Aber eben auch nicht mehr.
.......... Die SPC-Dateien werden komplett von Anfang bis Ende in den RAM geschmissen und selbigem wird
.......... gesagt: "Mach mal", und er spielt ab. Das ist jetzt stark vereinfacht - wenn es wirklich
so
.......... einfach wäre, wäre die loadspc.asm wesentlich kleiner.
.......... In jedem Fall kann man, wenn man eine der SPC-Dateien mit einem Hexeditor öffnet, dort gegen
.......... Ende weite Teile sehen, die nur mit $00 oder $FF gefüllt sind. Und man muss kein Programmierer
.......... sein, um zu wissen, dass das wohl kaum Musikdaten sind. Vielmehr sind es leere Stellen, wo man
.......... theoretisch andere Daten einfügen könnte. Aber dazu müsste man einen genaueren Überblick haben,
.......... wo diese freien Stellen sind, und wo bereits Musikdaten liegen. Und dazu braucht man ein
.......... wesentlich größeres Verständnis vom SPC700 und dem Umgang damit, als ich habe.
.......... Ich wiederhole mich: Ich will dieses Ding nicht schlecht machen, es ist besser als alles, was ich
.......... bisher zustande gebracht habe. Allerdings ist der Code, mit dem die Musik eingefügt wurde, eine
.......... Sackgasse, und darauf möchte ich hinweisen. Ich denke nicht, dass man "mal eben" es noch einfügen
.......... könnte, dass nebenbei Soundeffekte abgespielt werden. Ich schätze eher, dass man am effektivsten
.......... dahin kommt, wenn man die Fütterung des SPC700 komplett neu schriebe. Joe Lees SPC-Player
.......... umzuschreiben würde vermutlich mindestens genauso viel Aufwand bedeuten. *Newbie-Einschätzung*