You are not logged in.

Wie bekomme ich Zugriff aufs Cartmoddingforum | Fire Emblem: Das Siegelschwert Übersetzer gesucht | Videotutorial - Spielpatches benutzen! | Spenden für die Seite

Dear visitor, welcome to Snes-Projects.de. If this is your first visit here, please read the Help. It explains in detail how this page works. To use all features of this page, you should consider registering. Please use the registration form, to register here or read more information about the registration process. If you are already registered, please login here.

KillBill_158

Supergenie

  • "KillBill_158" is male
  • "KillBill_158" started this thread

Posts: 1,967

Thanks: 136

  • Send private message

1

Tuesday, June 19th 2012, 3:39pm

[Experte] Architektur des Super Nintendo Entertainment Systems (SNES)

1 Einleitung



Diese Dokumentation behandelt den Aufbau des Super Nintendo Entertainment Systems (SNES). Es soll einen Überblick darüber verschaffen, mit welchen technischen Tricks eine, für damalige Verhältnisse, leistungsstarke und doch recht günstige Konsole für den Homeentertainment-Bereich entwickelt wurde. Dieses Dokumentation gliedert sich dabei grob in vier Teile. Im ersten Teil, dem Kapitel 2, verschaffen wir uns zunächst einen Überblick darüber, gegen welche Systeme der SNES konkurrieren musste. Um dies zu veranschaulichen führen wir auch die technischen Daten der Mitbewerber auf.
Der zweite Teil (Kapitel 3) beschäftigt sich mit dem technischen Aufbau des Systems. Es werden viele der eingebauten Elemente näher erläutert. Auf eine ausführliche Erklärung einiger Elemente wird jedoch verzichtet, da diese Standardkomponenten der Elektronik sind und daher auch nur bekannte Funktionen ausführen.
Im Anschluss an den Aufbau des SNES behandelt der dritte Teil des Dokuments (Kapitel 4 + 5) den Aufbau der Module (engl. Cartridge). Zunächst soll der allgemeine Aufbau und das Pinout dieser Module dargestellt werden. Danach werden einige der Co-Prozessoren, welche in machen Modulen vorhanden waren, ausführlich erklärt.
Im letzten Teil der wird auf den praktischen Teil eingegangen. Es soll der Aufbau eines SNES-Programms erklärt und einige einfache Programmbeispiele dargestellt werden. Zudem wird beschrieben mit welcher Software der Quelltext kompiliert wird und welche Testmöglichkeiten man hat.

2 Computertechnik dieser Zeit

Spoiler Spoiler


Als der SNES 1990 erstmals auf den Markt kam, musste sich dieser in direktem Konkurrenzkampf mit dem SEGA MegaDrive und handelsüblichen PCs / Amigas begeben. Der Vorteil für die Konsolenhersteller war jedoch, dass das Zusammenspiel der Komponenten aufeinander ausgelegt war und auch viele spezielle Co-Prozessoren vorhanden waren, welche trotz langsamerer Taktfrequenz zum Teil aufwändigere Spiele abarbeiten konnten als der Hauptprozessor eines PCs. Außerdem war ein PC damals nicht als Spielesystem ausgelegt. Für damalige Verhältnisse aufwändige Grafiken mussten alleine von der CPU errechnet werden, wohingegen der SNES 2 PPUs (Picture Processing Units) zur Grafikdarstellung besaß. Diese Arbeitsweise ist erst Mitte der 90er mit dem Erscheinen der ersten 3D-Grafikkarten in die PC-Welt übernommen worden. Auch waren Soundkarten im PC eher eine Ausnahme, da selbst die günstigen Modelle noch über 150 DM kosteten. Jedoch besaß der PC andere Vorzüge gegenüber dem SNES. Er konnte nicht nur für Spiele sondern auch für Büroanwendungen verwendet werden. Auch war er fast beliebig erweiterbar, auch wenn aufgrund von fehlender Standards noch vieles inkompatibel zueinander war (Beispiel: CD-ROM Schnittstellen). Was wohl auch für den PC sprach, war die Möglichkeit, Software einfach kopieren zu können. Diese Möglichkeit zum Kopieren der Module gab es später zwar auch für Konsolen, war aber noch recht teuer und umständlich.
Im Vergleich zum PC war der Amiga, auch wenn Hersteller Commodore es anders sah, ein reiner Spielecomputer und somit das Bindeglied zwischen dem PC und den Konsolen. Im Gegenteil zum PC war dieser nicht beliebig erweiterbar, bot aber bereits eine vorinstallierte Soundeinheit. Somit war er als Spielecomputer recht beliebt. Auch die Softwarepiraterie erlebte mit dem Amiga einen drastischen Anstieg, da Spiele, wie beim PC auch, auf Diskette und ohne Kopierschutz-mechanismen vertrieben wurde.
Im Konsolenlager lieferten sich lange Zeit Nintendos SNES und Segas MegaDrive einen erbitterten Kampf. Der MegaDrive war zur Zeit der Veröffentlichung des SNES bereits zwei Jahre auf den Markt. Da Japan jedoch eine Hochburg von Nintendo war und auch die USA von Nintendo mehr angetan waren, konnte Sega mit dem MegaDrive hier keine großen Erfolge erzielen. Lediglich in Europa konnte sich der MegaDrive etablieren, da diese 16 Bit Konsole hier zwei Jahre vor dem 16Bit SNES auf den Markt kam. 1993 bzw. 1994 endete die Ära der 16Bit Konsolen, nachdem 3DO in Amerika eine, überteuerte und daher wenig verkaufte, Konsole mit CD-ROM-Laufwerk anbot. 1994 veröffentlichte Sony dann die PlayStation, welche den SNES weitestgehend ablöste.



3 Das System

Spoiler Spoiler


Dieses Kapitel behandelt den Aufbau des Super Nintendo Entertainment Systems. Es werden hier die einzelnen Bauelemente und Zusammenhänge erklärt, welche zur Erzeugung der Video- und Audiosignale verwendet werden. Es wird jedoch darauf verzichtet, darauf einzugehen, wie Daten genau verarbeitet werden und wie diese schließlich auf PAL bzw. NTSC umgewandelt werden. Nachdem bereits im vorangegangenen Kapitel ein kleiner Überblick über das Gesamtsystem gegeben wurde, steigen wir nun in die Arbeitsweise des Gerätes ein. Hierzu wird im folgenden Unterkapitel zunächst ein Einblick in die Konsole gegeben. Die darauf folgenden Unterkapitel beschreiben anschließend die Funktionsweise der einzelnen Bauelemente.


3.1 Das Gesamtsystem

Spoiler Spoiler


Das Blockschaltbild des Systems sieht wie folgt aus:



Bevor wir zu den einzelnen Komponenten des Systems kommen, möchten wir auf das Zusammenspiel der Komponenten eingehen.
Der CIC-Chip ist der Kopierschutz des Systems (siehe Kapitel 3.5). Er verfügt über eine Reset-Leitung, mittels der er die zweite Grafikeinheit abschalten kann. Diese gibt das Reset-Signal dann weiter an die erste Grafikeinheit, die Soundeinheiten, dem RAM und die SNES-CPU.
Die SNES-CPU ist an zwei Adressbus-Systeme angeschlossen. Zum einen der 24 Bit breite A-Bus und zum anderen der 8 Bit breite B-Bus. Mittels des A-Busses ist es der CPU möglich, den gesamten RAM sowie den gesamten ROM-Speicher des Moduls zu adressieren. Der B-Bus hingegen dient zur Kommunikation mit den anderen Bauteilen. So haben die Sound- & Grafikeinheiten nur Zugriff auf einen kleinen Teil des RAMs. Allen Einheiten gemein ist der 8 Bit breite Datenbus.
Die beiden PPUs haben zudem einen eigenen Speicher (32KB pro Einheit). Die Besonderheit bei der Verdrahtung ist jedoch dass nur die erste Einheit in der Lage ist, den Speicher zu adressieren. Somit ist die zweite PPU auf das Anlegen der richtigen Adresse auf dem Adressbus auf die erste PPU angewiesen.
Die Soundeinheiten des Systems können nur über die Sound CPU mit dem Rest des Systems kommunizieren. Auf welche Art die Soundeinheiten untereinander kommunizieren, wird im entsprechenden Kapitel behandelt.


3.2 Hauptprozessor

Spoiler Spoiler


Der Hauptprozessor des Systems ist eine Spezialanfertigung für Nintendo welche mit dem Western Design Center 65816 bzw. 65C816 kompatibel ist. Der von Nintendo verwendete Name dieses Prozessors ist „5A22“. Technisch gesehen entspricht dieser Chip einer Weiterentwicklung des Prozessors welcher auch im NES verbaut war (MOS Technologies 6502). Prinzipiell kann der SNES somit den Programmcode des NES verarbeiten, jedoch scheitert die Umsetzung an den PPUs welche nicht kompatibel zum NES-Pendant sind.
Im Gegensatz zum 6502 besitzt dieser Prozessor auf 16Bit erweiterte Alu, X & Y, Index-Register und Stack-Pointer. Über einen Softwareschalter lässt sich die CPU jedoch in einen 8Bit Modus umschalten. Wie bereits zuvor angesprochen verfügt die CPU über einen Anschluss an den 24Bit Adressbus.
Kommen wir nun zum internen Aufbau der Einheit. Zur Verständnis stellen wir nachfolgend das Blockschaltbild des Prozessors, welches aus der offiziellen Dokumentation stammt, dar.



Der Hauptprozessor verfügt über drei 8-Bit-Schnittstellen, über die er mit den anderen Bauteilen auf dem Chip kommunizieren kann. Dies sind zwei reine Adress-Schnittstellen, die die Datenleitungen A0 bis A15 bereitstellen, sowie eine Schnittstelle, die wahlweise für Adressen oder Daten genutzt werden kann und bei Adressen die Leitungen A16 bis A23 bereitstellt. Somit kann parallel eine 24-Bit-Adresse ausgegeben werden, oder eine 16-Bit-Adresse und gleichzeitig 8 Bit Daten.
Die Datenleitungen werden in den Prozessor weitergeleitet, im Gegensatz zu den Adsressleitungen. Bei Befehlsbytes geschieht dies über die Data Latch/Predecode-Einheit, bei Datenbytes über das Databank-Register. Adressen werden über den Internal Adress Bus ein- und ausgelesen. Über die Data Latch/Predecode-Einheit werden Befehle in das Instruction-Register geschrieben. Das Databank-Register gibt Daten weiter auf den Internal Data Bus, von dem sie weitergegeben werden können an Program Counter, Accumulator, ALU oder Direct-Register (welches bei bestimmten Adressierungsarten benötigt wird). In die Register X und Y können die Daten nur über einen weiteren Schritt über den Internal Special Bus geladen werden, auf den sonst nur Accumulator, ALU und Stack-Pointer sowie die Transfer Switches Zugriff haben.
Neben den drei 8-Bit-Schnittstellen verfügt der Hauptprozessor noch über eine Reihe von Steuerleitungen, die die System Control, Timing Control und Interrupt Logic-Einheiten sowie den Clock Generator steuern bzw. entsprechende Befehle und Signale weitergeben.


3.3 Grafikprozessoren (Nintendo 5C77 & Nintendo 5C78)

Spoiler Spoiler


Der SNES kann Auflösungen von 256*224 bis 512*478 Pixeln darstellen, wobei die Darstellung jeweils unterschiedlich abläuft. Bilder mit Auflösungen von 256*224 bis 512*239 werden progressive aufgebaut, also Zeile für Zeile von oben nach unten. Bilder mit Auflösungen von 512*448 oder 512*478 werden interlaced aufgebaut, also erst die ungeraden Zeilen, dann die geraden. Dies geschieht, damit das Auge keine halben Bilder wahrnimmt, weil der Aufbau des ganzen Bildes zuviel Zeit in Anspruch nimmt.
Die beiden Grafikprozessoren arbeiten als eine Einheit, können also auch als solche wahrgenommen werden. Der erste Grafikchip ist für den Zugriff auf den internen Grafikspeicher zuständig und mit diesem über einen Adressbus verbunden. Der zweite Chip sendet die Farbinformationen an die RGB-Einheit, die diese Daten weitergibt. Der interne RAM umfasst 64 KB Speicher für Daten, 544 Byte für Objekte (Sprites im Weiteren) und 512 Byte für Farbdaten. Die Farben setzen sich aus den RGB-Farbwerten (15 Bit Farben) zusammen, was eine Bandbreite von 32.768 Farben ermöglicht. Allerdings werden im Farbdaten-RAM nur Paletten von 4 bis 256 Farben pro Palette gespeichert, aus denen die jeweiligen Elemente auf dem Bildschirm wählen können. 256 Farben ist gleichzeitig die Speichergrenze des Farbdaten-RAM (Eine Farbe braucht 2 Byte Speicher), wodurch die 256-Farben-Palette auch als „full palette“ bezeichnet wird. Jede Palette enthält die Farbe „durchsichtig“, wodurch dahinter liegende Objekte gezeigt werden.
Der SNES kann in 8 Modi Grafiken darstellen, die sich jeweils grundsätzlich unterscheiden. Allen Modi gemeinsam ist, dass sie Layer verwenden, wobei die Anzahl der Layer je nach Modus variiert. Jeder Layer ist aufgebaut aus Tiles, „Kacheln“, die eine Größe zwischen 32*32 und 128*128 Pixeln haben können. Die Daten für die Tiles sind im internen Daten-RAM hinterlegt und können auch mehrfach verwendet werden. Die möglichen Farben für die Tiles sind wiederum vom Modus abhängig. Die Tiles können in zwei Planes, „Ebenen“, angeordnet werden, die Vordergrund und Hintergrund des Layers darstellen. Alle Layer können unabhängig voneinander auf dem Bildschirm horizontal und vertikal verschoben werden.
Auf jede Plane jedes Layers können sich Sprites, „Objekte“, befinden. Diese Sprites können die Größe von 8*8 bis 64*64 Pixeln haben, sind für sich bewegbar sowie horziontal und vertikal drehbar. Die Daten der Sprites werden im Objekt-RAM gespeichert, wobei maximal 32 Sprites gleichzeitig dargestellt werden können. Auch
Sprites werden aus Tiles geformt, wobei diese für Sprites immer 8*8 Pixel groß sind und über eine 16-Farben-Palette verfügen.
Die Modi im Einzelnen:

Modus 0: 4 Layer stehen zur Verfügung, jeder Layer verfügt nur über eine 4-Farben-Palette.

Modus 1: 3 Layer, zwei mit jeweils 16-Farben-Paletten, einer mit einer 4-Farben-Palette.

Modus 2: 2 Layer, jeder mit einer 16-Farben-Palette. Jedes Tile kann einzeln verschoben werden.

Modus 3: 2 Layer, einer mit der „full palette“, 256 Farben, einer mit einer 16-Farben-Palette. Zusätzlich kann der Layer mit der full palette auch Farben aus einem 11-Bit-Farbraum (RGB 443) direkt definieren.

Modus 4: 2 Layer, einer mit der „full palette“, einer mit einer 4-Farben-Palette. Zusätzlich kann der Layer mit der full palette auch Farben aus dem 15-Bit-Farbraum direkt definieren. Jedes Tile kann einzeln verschoben werden.

Modus 5: 2 Layer, einer mit einer 16-Farben-Palette, einer mit einer 4-Farben-Palette. Veränderte Codierung der Tiles, um interlaced-Darstellung zu erleichtern.

Modus 6: 1 Layer, der eine 16-Bit-Palette verwendet. Ebenfalls veränderte Tile-Codierung zur Erleichterung von interlaced-Darstellungen, zusätzlich kann jedes Tile einzeln verschoben werden.

Modus 7: Dieser Modus bildet eine große Besonderheit des SNES. Es wird nur ein Layer verwendet, der entweder nur über eine Plane mit full palette verfügt, oder über zwei Planes mit jeweils 128 Farben. Besonderheit ist, dass der Layer unter Benutzung von Matrix-Transformationen gedreht, gekippt und kleiner oder größer skaliert werden kann. Dies soll Dreidimensionalität simulieren und perspektivische Darstellung ermöglichen.

Weitere Möglichkeiten der Farbgestaltung ergaben sich aus gröberer Pixelung der Layer oder Farbaddition bzw. –subtraktion der Farben von Layer und Sprite, was eine wiederum größere Farbvielfalt ermöglicht, als eigentlich vorgesehen.


3.4 Soundsystem

Spoiler Spoiler


Das Soundsystem des SNES besteht aus einem Sony 8 Bit Prozessor (SPC700) und einem Sony DSP (WWW210R6X) gefolgt von einigen Filtern und D/A-Wandlern. Eingehen wollen wir in diesem Kapitel nur auf den Prozessor, da dieser maßgeblich für die Leistung der Soundausgabe ist.



Der Sony SPC700 CMOS 8 Bit Prozessor ist die Verwaltungseinheit des Soundsystems.



Aufgabe dieses Chips ist es, die zur Tonerzeugung benötigten Daten anzufordern und in den 64KB großen Sound-RAM zu übertragen.
Zudem wird der SPC700 zur Steuerung des DSPs verwendet. Dieser arbeitet die Anweisungen der Sound CPU zu entsprechender Zeit mit den Daten aus dem Sound-RAM ab und gibt die erzeugten digitalen Daten an die D/A-Wandler & Filter weiter.
Nun soll die Funktionsweise des SPC700 genauer erläutert werden.


3.4.1 Speicher

Spoiler Spoiler


Da der SPC700 maximal 64KB Adressieren kann und dies bereits mit der Größe des RAMs ausgereizt ist, besteht ein Konflikt mit dem 64Byte großen IPL ROM, welcher den initialen Zustand der Soundeinheit nach dem Starten der Konsole enthält. Als Lösung wurden vom Adressbereich des RAMs 64Byte an den ROM abgetreten.
Der verbleibende Adressbereich ist jedoch nicht vollständig für beliebige Daten verfügbar. So sind weitere 128Byte für die Special Function Register (kurz: SFR) reserviert.
Alle noch verfügbaren Adressbereiche stehen für Sounddaten zur Verfügung. Zu beachten ist nur, dass der Bereich von 0200H – 7FFH der Bereich ist, welcher für Datentransfers benötigt wird. So sollten Daten, die öfter benötigt werden, aus diesem Bereich verschoben werden, um der Gefahr zu entgehen, dass diese bei einem weiteren Datentransfer überschrieben werden.



3.4.2 Kommunikation mit der SNES-CPU

Spoiler Spoiler


Die zuvor angesprochenen Datentransfers des SPC700 laufen alle über den Datenbus, den B-Bus und der SNES-CPU ab. Hierzu stehen der SPC700 vier Ports zur Verfügung. Von jedem der vier Ports stehen zwei Versionen zur Verfügung: jeweils ein Read- und ein Write-Register. Die Read-Register können von der SNES-CPU beschrieben und von der Sound-CPU nur gelesen werden. Anders herum ist es bei den Write-Registern, welche nur vom SPC700 beschrieben werden können. Aufgrund der Tatsache, dass sich die Speicherstellen im Work-RAM (kurz: WRAM) des SNES befinden greifen, die SNES-CPU und die Sound-CPU mit unterschiedlichen Speicheradressen auf diesen zu. Der Sound-CPU steht nur der 8 Bit breite B-Bus zur Verfügung, die SNES-CPU kann hingegen den 24 Bit breiten A-Bus verwenden.



3.4.3 Timer / Counter

Spoiler Spoiler


Die 8 Bit Timer der Sound-CPU sind in Sets zusammen mit den 4Bit Countern geschaltet (je ein Timer und ein Counter). Zwei der drei Sets werden mit 8KHz angesteuert, das dritte Set hingegen mit 64KHz. Die vor den Countern befindlichen Timer können dabei wie üblich mit einem max. Wert vorbelegt werden. Sobald der Timer in Betrieb ist und den Vorgabewert erreicht, wird ein Signal an den entsprechenden Counter im Set gesendet. Dieser wird dann um 1 inkrementiert.



Auf die Vorgehensweise wie aus den Binärdaten ein Audiosignal erzeugt wird, möchten wir hier nicht näher eingehen. Ziehen Sie bitte hierzu Kapitel 3-2-1 aus dem 1. Entwicklerhandbuch zu Rate.


3.5 Der Kopierschutz

Spoiler Spoiler


Über den Kopierschutzchip ist wenig in Erfahrung zu bringen. Nintendo hält sich verständlicherweise auch sehr bedeckt bei dem Thema. Die Funktionsweise des Kopierschutzes hingegen ist bekannt.
Beim Einschalten der Konsole beginnt der Chip mit einem baugleichen Chip auf dem Modul zu kommunizieren. Stellt sich dabei heraus, dass kein Chip oder auch ein falscher Chip (die NTSC-Version unterscheidet sich von der PAL-Version) auf dem Modul vorhanden ist, setzt der CIC-Chip im SNES das RESET-Signal und stoppt somit alle im SNES vorhandenen Einheiten. Welche Daten zwischen den beiden Einheiten ausgetauscht werden, ist nicht bekannt. Es ist auch nicht bekannt, ob es sich bei jedem Startvorgang um die gleichen Daten handelt, die übermittelt werden; es ist aber davon auszugehen, dass der CIC-Chip im SNES ein zufälliges Signal generiert, auf welches der Chip im Modul entsprechend antwortet.
Um auf eine Frage aus unserem Vortrag zurückzukommen, möchten wir an dieser Stelle noch einmal erläutern, warum es nicht immer möglich ist, einfach die RESET-Leitung zu durchtrennen um den Kopierschutz unwirksam zu machen. Bei unseren Recherchen sind wir auf genau diese Vorgehensweise gestoßen. Und dieses Vorgehen funktionierte auch bei den ersten verfügbaren Modulen. Bei den neueren hingegen wird dieses Vorgehen vom Modul erkannt und verweigert daraufhin den Datentransfer. Auf welche Art dies vom Modul ermittelt wird ist uns aufgrund der unbekannten genauen Funktionsweise des Chips unbekannt. Wir vermuten jedoch, dass der CIC-Chip im SNES beim Setzten des RESET-Signals auch alle Übertragungen zum Pendant im Modul abbricht. Dieser könnte diesen Abbruch der Kommunikation als Indiz für das Setzten des Resets im SNES nehmen und somit den Datenaustausch zwischen ROM und SNES unterbinden.


3.6 Diverse Komponenten

Spoiler Spoiler


Abschließend werden wir noch einen kleinen Überblick über alle nicht angesprochenen Elemente geben. Dies sind u.A. die I/O-Schnittstellen und die ausgebenden Bausteine.



4 Das Modul

Spoiler Spoiler


Die Spiele für den Super Nintendo wurden in den meisten Fällen auf Modulen sog. Cartridges ausgeliefert (nur ein Bruchteil wurde über ein nur in Japan und Groß-Britannien erhältliches Satellitenmodem veröffentlicht). Die Cartridges beinhalten immer einen ROM und einen CIC Chip. Außerdem können diese einen SRAM und eine Batterie, einen MAD-1 und / oder einen Co-Prozessor enthalten. Diese Komponenten sollen nachfolgend kurz erklärt werden:



Die Verdrahtung dieser Komponenten soll hier nicht weiter behandelt werden, da diese je nach Ausstattung des Moduls unterschiedlich ist.
Lediglich das Pinout soll hier noch behandelt werden. Dieses können Sie der nachfolgenden Abbildung und Tabelle entnehmen.



Die Angaben stammen von zwei Internetseiten, bei denen die Autoren ausdrücklich darauf hinweisen, dass die Angaben aus Dokumentationen übernommen wurden und nicht auf Richtigkeit geprüft wurden. Dennoch gehen wir davon aus, dass zumindest die Pins 5-27 & 36-58 korrekt sind, da diese auf allen von uns gefundenen Quellen identisch waren. Bei den eben nicht genannten Pins (1-4, 28-35 & 59-62) haben wir jedoch nur eine Quelle gefunden, die diese beschreibt. Daher sind diese mit Vorsicht zu betrachten. Die genannten Pins gehören zu den Co-Prozessoren (welche weiter unten behandelt werden) und sind daher nicht in jedem Modul vorhanden (Siehe Abbildung 1 & Abbildung 2).


Abbildung 1: SNES-Cartridge ohne Co-Prozessor (Spiel: "Super R-Type")


Abbildung 2: SNES-Cartridge mit Co-Prozessor (Spiel: "Starwing")


Nun soll noch eine Erklärung zu den Pins 5-27 und 36-58 folgen. Die Pins A0-24 sind die Adressleitungen. Der Super NES kann hier über seinen Bus A die gewünschte Adresse anlegen, um den Inhalt einer Speicherstelle im ROM oder im RAM auszulesen bzw. zu schreiben. Welche Datenquelle bzw. welches Datenziel die CPU auswählt, legt diese mit den Leitungen /RAM Enable bzw. /ROM Enable fest. Über die Pins /READ bzw. /WRITE wird angegeben, in welche Richtung die Daten über die Datenleitungen D0-7 übertragen werden sollen.
Der Pin /IRQ wird zum Senden bzw. Empfangen eines Interrupts verwendet. Die vier CIC-Pins übertragen Daten zum Überprüfen des Kopierschutzes. VCC und GND ist die Spannungsversorgung für das Modul.
Die 24 Adressleitungen unterteilen sich in zwei Bereiche. Über die Leitungen A0-15 wird der Offset und über die Leitungen A16-23 wird die Speicherbank ausgewählt. Auffallend ist, dass über 24 Adressleitungen ein Speicher von max. 128MB bzw. 1024Mbit Byteadressierbar ist. Laut Nintendo ist jedoch die Modulgröße auf max. 8MB bzw. 64Mbit beschränkt. Weshalb es zu einer solchen Einschränkung kommt, ist uns nicht bekannt, soll jedoch hier auch nicht behandelt werden, da dieses Kapitel nur einen kurzen Einblick in das Modul geben sollte.


5 Externe Co-Prozessoren

Spoiler Spoiler


Da die CPU des SNES mit den max. 3,6MHz recht schwach ist, muss diese für manche Spiele unterstützt werden. Für die ersten Spiele wie „Super Mario World“ reichte die Rechenleistung noch aus. Bei Spielen wie „Starwing“ oder „Super Mario World 2“ welche für damalige Verhältnisse aufwendige Grafiken besaßen, wurden Spezialchips als Co-Prozessoren benötigt. Jedoch waren Grafikberechnungen nicht der einzige Aufgabenbereich solcher Spezialchips. So wurde auch die Berechnung der KI, die Dekompression von Daten aus dem ROM oder auch die Systememulation (Game Boy) auf solche Spezialchips ausgelagert. Ein Teil der möglichen SNES Co-Prozessoren wurde von Nintendo selbst oder von einer von Nintendo beauftragten Firma entwickelt. Meist sind dies universell einsetzbare Co-Prozessoren. Der andere Teil sind herstellerspezifische Eigenentwicklungen. Hierbei handelt es sich meist um Prozessoren, die nur für eine bestimmte Aufgabe entwickelt wurden.
Nachfolgend werden die bekannten von Nintendo entwickelten Spezialchips mit deren Funktion aufgeführt:



Separat von den „offiziellen“ Chips werden nun die herstellerspezifischen Spezialeinheiten aufgeführt:



Diese speziellen Einheiten ersetzen trotz ihrer hohen Leistung nicht die Haupteinheit des SNES, sondern unterstützen diese. Somit wird durch das Einsetzen eines Spielmoduls mit einem solchen Chip das SNES zu einer Multiprozessoreinheit.


5.1 Der SA-1

Spoiler Spoiler


Der SA-1 Chip ist der leistungsstärkste Spezialchip des SNES. Als Kern dieser Einheit dient die gleiche CPU wie im SNES (WDC65816). Dies macht es leicht, vorhandenen Programmcode auf diesen Prozessor auszulagern. Zudem besitzt der SA-1 noch einige weitere hardwaregestützte Funktionen, welche an entsprechender Stelle in diesem Kapitel behandelt werden.
Die Taktfrequenz dieser Einheit ist um den Faktor 4 höher als die der SNES-CPU im Standard Modus (SNES: 2,68MHz; SA-1: 10,74MHz). Somit ist unter Verwendung dieses Prozessors eine bis zu fünffach höhere Performance des Gesamtsystems möglich.


5.1.1 Speicheranbindung

Spoiler Spoiler


Gegenüber einem Modul ohne Co-Prozessor bei dem die Adress- und Datenleitungen direkt am RAM und ROM anliegen, werden in diesem Fall die Adressen und Daten an den SA-1 übergeben. Dieser übernimmt dann die Ansteuerung des Speichers. Nachfolgendes Bild soll diesen Aufbau veranschaulichen.



In dieser Darstellung ist nicht erkennbar, dass die SNES CPU auf fast den gesamten Speicher des Moduls zugreifen kann. Lediglich die internen Register des im SA-1 enthaltenen WDC65816 sind der SNES-CPU nicht zugänglich. Der SA-1 hingegen hat selbstverständlich Zugriff auf diese Register, jedoch besitzt diese Einheit keine Möglichkeit Daten, direkt aus dem WRAM des SNES zu beziehen.



Verallgemeinert kann man sagen, dass die SA-1 Einheit auf den gesamten auf dem Modul befindlichen Speicher zugreifen kann, jedoch keinen Zugriff auf den im SNES implementierten Speicher bekommt. Dies ist auch sinnvoll, da die SNES-CPU die Steuereinheit des Gesamtsystems ist und eine Umsetzung eines gemeinsamen Speichers recht aufwändig ist. Als Beispiel hierfür kann man die Multiprozessor-Server-Systeme nennen.


5.1.2 Interner Aufbau

Spoiler Spoiler


Der interne Aufbau des SA-1 entspricht dem des nachfolgenden Blockschaltbildes. Die einzelnen dort dargestellten Elemente werden nachfolgend erläutert.



Auf die CPU soll hier nicht eingegangen werden, da diese bis auf wenige Unterschiede (Siehe Entwicklerhandbuch 2 Kapitel 1-5-5) die gleiche wie im SNES ist. Informationen zur Arbeitsweise der Einheit entnehmen Sie bitte Kapitel 3.2. Auch beim 2KB bzw. 16Kb großen RAM wird auf eine Erklärung verzichtet, da davon ausgegangen wird, dass die Funktionsweise von RAM bekannt ist. Die „Super NES CPU I/O“ (siehe Abbildung oben) ist an sich keine Einheit sondern lediglich das Pinout, mittels welches der SNES mit dem Modul verbunden ist.
Allen einzelnen Einheiten gemein ist die Konfigurationsart. Sowohl das Einstellen der Einheiten als auch die Kommunikation wird über SFR (Special Function Register) gesteuert. Hierzu stehen eine Vielzahl an Registern zur Verfügung, die aufgrund des Umfangs nicht alle erläutert werden sollen. Eine ausführliche Beschreibung ist dem Entwicklerhandbuch von Nintendo zu entnehmen.


5.1.2.1 Super MMC:

Spoiler Spoiler


Die Super MMC ist ein Speichercontroller welcher die Ansteuerung des GamePak-Roms übernimmt. Diese Einheit ermöglicht dem SNES ein ROM mit über 32Mb Speicher zu adressieren. Hierzu wird der sog. Map Mode 22 verwendet. Um diesen zu veranschaulichen, sollte zunächst ein Blick auf die Memory-Map des SA-1 geworfen werden (Abbildung unten)



Im linken Bereich der Abbildung befinden sich vier separate Bereiche, welche mit „Game Pak Rom Select“ gekennzeichnet sind (0000H – FFFFH in den Bänken C0H – FFH). Jedes dieser Elemente hat einen Adressbereich, der zum Adressieren von 8Mbit nötig ist. Über spezielle Register, welche anschließend noch genauer beschrieben werden, kann nun angegeben werden, welcher Adressteil des GamePak-Roms mit diesen Bänken dargestellt werden soll. Diese Konfigurationsregister ermöglichen zudem die Zuweisung bestimmter ROM-Adressbereiche in die virtuellen Adressbereiche 00:8000H – 1F:FFFFH, 20:8000H – 3F:FFFFH, 80:8000H – 9F:FFFFH, A0:8000H – BF:FFFFH. Dabei besteht eine spezielle Verknüpfung der Bänke Cx, Dx, Ex, Fx mit den zuvor genannten Adressbereichen. Bank Cx ist dem Bereich 00:8000H – 1F:FFFFH zugewiesen, wohingegen Bank Dx mit dem Bereich 20:8000H – 3F:FFFFH verknüpft ist. Diese Art der Verbindung besteht auch für die übrigen beiden Bänke Ex und Fx.
Welche Adressbereiche des ROMs in diesen Adressraum gelegt werden können, hängt somit vom in der zugehörigen Bank ausgewählten Speicherbereich ab. Entweder es wird der Adressbereich der Bank in den Speicherbereich kopiert oder es wird der Block des ROMs abgelegt, welcher dieselbe Nummer hat wie der Adressbereich (siehe hierzu Abbildung unten). Eine genauere Erklärung folgt weiter unten in diesem Kapitel.
Sowohl der SA-1 als auch die SuperNES CPU können über die in diesen Bereichen dargestellten Speicherbereiche auf den Speicher des ROMs zugreifen.
Wie bereits erwähnt, wird über SFR ausgewählt, welche GamePak-ROM-Bereiche an welcher Stelle in der Memory Map vertreten sind. Ein solches Register ist wie folgt aufgebaut:



Dieses Register gibt an, welche Adressen des Roms in der Bank Cx und im Bereich 00:8000H – 1F:FFFFH dargestellt werden. Die Register für die anderen Bänke besitzen den gleichen Aufbau wie das oben dargestellte.
Die Bits CB0-CB2 geben an, welche 8Mb des ROMs mittels der Bank Cx in der Memory Map angesprochen werden soll. Sollte z.B. der Speicherbereich 40:0000H – 4F:FFFFH in Bank Cx abgelegt werden, so entspricht dies dem fünften Element des ROMs und somit der Bitfolge 100. Nun gibt es noch das Bit, welches mit „CBM“ gekennzeichnet ist. Dieses gibt an, welches Element in den ersten Game Pak ROM Image-Bereich geladen wird (für Bank Dx wäre es das zweite Game Pak ROM Image, beim Ex das Dritte und bei Fx das Vierte). Setzt man das CBM Bit, so ist im entsprechenden Game Pak ROM Image Bereich der gleiche ROM-Bereich abgebildet wie in Bank Cx. Ist das Bit nicht gesetzt, so wird der erste Bereich des ROMs in den ersten Game Pak ROM Image-Bereich gelegt (bei Bank Dx der zweite Teil in den zweiten Bereich und so fort)
Auf die Speicherbänke 40H – 6FH und die BW-RAM Image Bereiche gehen wir nicht näher ein. Lediglich der Verwendungszweck soll hier kurz erklärt werden. Die Bänke 40H-6FH dienen zum Adressieren des Backup-RAMs. Dieser Speicherbereich kann in Blöcke à 64Kb unterteilt werden. Einer dieser Blöcke kann in die BW-RAM Image-Bereiche projiziert werden. Hierbei ist zu beachten, dass der BW-RAM-Image-Bereich in den Bänken 00H – 3FH den identischen Inhalt hat wie der in den Bänken 80H - BFH.
Die weiteren noch nicht besprochenen Bereiche sind nicht im Zusammenhang mit dem Super MMC zu sehen, da dieser nur die Speicherverwaltung zum externen ROM und RAM übernimmt.


5.1.2.2 Arithmetic Circuit

Spoiler Spoiler


Diese Einheit dient zum schnellen Abarbeiten von arithmetischen Funktionen. Es wird die Multiplikation, die Division und die kumulative Summe unterstützt. Alle Operanten, mit Ausnahme des Divisors, können dabei „signed“ sein.
Gesteuert wird die Einheit durch das Register 2250H, in welchem die Bits ACM und M/D die durchzuführende Operation angeben. Wie diese gesetzt werden können und wie viele Takte (bei einer Taktfrequenz von 10,74MHz) für die Verarbeitung notwendig sind, entnehmen Sie der nachfolgenden Tabelle.



Werte, die diese Einheit verarbeiten soll, müssen dazu in speziellen Registern gesichert werden. Welcher Operand in welches Register eingetragen werden muss, entnehmen Sie bitte dem 2. Entwicklerhandbuch Kapitel 1-7-2 folgende.


5.1.2.3 Character Conversion Circuit

Spoiler Spoiler


Die CCC dient zum Umwandeln der im BW-RAM oder I-RAM abgelegten Bitmap-Daten in ein PPU-konformes Format. Aufgrund des Aufbaus von Bitmap-Daten können mit diesem Format z.B. Rotationen und Größenanpassungen schneller durchgeführt werden. Jedoch ist die PPU nicht in der Lage, Bitmap-Daten zu verarbeiten. Wie diese Umwandlung durchgeführt wird, entnehmen Sie bitte Kapitel 1-6-1 des 2. Entwicklerhandbuchs.


5.1.2.4 Variable Length Bit Processing Unit

Spoiler Spoiler


Diese Einheit ist in der Lage, unterschiedlich lange Datensätze aus dem GamePak ROM zu lesen. Aus Sicht dieser Einheit besteht der gesamte ROM-Speicher aus einem Datenstring welcher an beliebiger Stelle gelesen werden kann. So muss nicht mehr Byteweise aus dem Speicher gelesen werden, was zu einem Geschwindigkeitsschub führt. Umgesetzt wird diese Funktion mittels eines Barrel-Shifts.
Die Arbeitsweise dieser Einheit ist in Kapitel 1-8-1 im 2. Entwicklerhandbuch beschrieben.


5.1.2.5 Timer & DMA Circuit

Spoiler Spoiler


Aufgrund der Tatsache, dass die Funktionsweise dieser Einheiten bekannt sein sollte, verzichten wir an dieser Stelle auf eine Beschreibung. Sollten dennoch Informationen zu diesen Einheiten erwünscht sein, so ziehen sie bitte Kapitel 1-9-1 des zweiten Entwicklerhandbuchs für den DMA-Circuit zu Rate. Das Kapitel der Timer ist leider nicht in dieser Version des Dokuments vorhanden.


5.1.3 Kommunikation mit dem SNES

Spoiler Spoiler


Der SA-1 ist nicht in der Lage, der SNES-CPU Befehle aufzuzwingen, da das SA-1 System den Slave darstellt. Dennoch muss natürlich eine Kommunikation zwischen beiden Einheiten stattfinden. Dies geschieht über spezielle Register, welche Interrupts auslösen. Hierzu stehen zwei Arten von Interrupts zur Verfügung. Zum einen der normale IRQ sowie der NMI (Not Maskable Interrupt). Letzerer ist nur in eine Richtung möglich. Dem SA-1 ist es nicht möglich, einen NMI an die Konsole zu senden.
Nach dem Empfang eines IRQs versucht der Empfänger anhand eines Registers festzustellen, von welcher Einheit der Request gesendet wurde. Anschließend wird der Request vom Empfänger zurückgesetzt.
Neben den eigentlichen IRQ kann noch eine zusätzliche 4Bit große Nachricht übertragen werden, welche dem Empfänger ermöglicht, auf spezielle Weise auf den Interrupt zu reagieren. Da dies jedoch nicht immer ausreichend ist, können zusätzliche Informationen über dem von beiden Prozessoren erreichbaren I-Ram ausgetauscht werden. Auch der BW-RAM könnte hierfür verwendet werden, jedoch ist dieser während einer Character Conversation nicht zugänglich.
In diesem Zusammenhang soll auch der „Internal Controller“ etwas näher erläutert werden. Aufgabe dieses Elementes ist es, Kollisionen bei Datentransfers zu vermeiden. Dies gilt sowohl für die im SA-1 verbauten Einheiten als auch für die Transfers nach Extern. Sollte die SNES-CPU und ein Element des SA-1 simultan auf den RAM zugreifen wollen, so wird der SNES-CPU Vorrang gewährt.


5.1.4 Operating Modes

Spoiler Spoiler


Die SA-1 Erweiterung kann in verschiedenen Modi zusammen mit der SNES-CPU arbeiten. Diese Modi werden nicht durch SFR eingestellt, sondern dynamisch von der SNES-CPU gewählt. Allen Modi gemein ist, dass die SA-1 keinen Einfluss auf die Wahl hat. Dieser ist im Master-Slave-System der Slave und führt daher nur die Operationen aus, die diesem zugewiesen wurden.
Folgende 3 Modi stehen zur Wahl:


5.1.4.1 Accelerator Mode

Spoiler Spoiler


Dieser Modus ist aus Programmierer-Sicht der am einfachsten umzusetzende. Hierbei dient der SNES-CPU nur zum Hochladen der auszuführenden Routinen zum SA-1. Dieser verarbeitet dann die ihm zugeschickten Daten. Während der Verarbeitung im SA-1 wartet der SNES-CPU im Idle-Zustand. Erst sobald die SA-1 per Interrupt an die SNES-CPU signalisiert, dass die Programmroutinen verarbeitet wurden, beginnt die CPU weitere Routinen an den SA-1 zu senden.
Die gesamte Abarbeitung des Programmcodes wird also durch die SA-1-Einheit durchgeführt. Dass die SNES-CPU dabei Untätig ist, macht diesen Modus zum langsamsten von allen.
Nachfolgend ist diese Verarbeitungsmethode dargestellt:



5.1.4.2 Parallel Processing Mode

Spoiler Spoiler


Bei diesem Modus werden zuvor sequenziell abgearbeitete Programmteile parallel auf den beiden CPUs abgearbeitet. Dieser Modus ist somit schneller als der Accelerator Mode, da auch die SNES-CPU an der Abarbeitung des Programmcodes beteiligt ist. Es können jedoch auch hier Idle-States entstehen, wenn Programmteile auf Ergebnisse von Routinen im anderen Prozessor warten müssen.




6 Programmierung des Systems


6.1 Allgemeiner Aufbau der Instruktionen

Spoiler Spoiler


Der SNES wird mit einer Variante des Assembler-Befehlscodes programmiert. Diese Variante verfügt über eine große Bandbreite von Befehlen und kann verwendet werden, um alle Bauteile des SNES zu programmieren. Auch die allermeisten SNES-Emulatoren verwenden diesen Befehlscode, allerdings gibt es auch eine Abwandlung von C, die zur Programmierung verwendet werden kann. Wir haben nur die Assembler-Variante verwendet.
Die große Bandbreite von Befehlen von unterschiedlicher Länge lässt vermuten, das es sich um einen CISC-Befehlssatz handelt, sicher lässt sich dies aber für den SNES nicht sagen, da das Produkt des Compilers nicht bekannt ist, sondern nur der geschriebene Code, und wir ausserdem nicht alle Befehle kennen. Der Assembler-Befehlscode verfügt über drei genutzte Register (Akkumulator, X, Y), für die jeweils Lade- und Speicher-Befehle existieren. Weiterhin gibt es Befehle, um Daten von einem ins andere Register zu verschieben. Ein eigener Speicherbefehl speichert den Wert null in einer Speicherzelle. Auch gibt es einer Reihe von Sprungbefehlen, vom einfachen Sprung bis zu Varianten wie ‚bpl „Wert“’ (Branch on Plus, Springe, wenn der Wert „Wert“ positiv ist). Daneben gibt es noch eine ganze Reihe weiterer Befehle, deren Beschreibung hier den Rahmen sprengen würde. Zumal es nicht sicher ist, dass uns alle Befehle bekannt sind, da es nicht möglich war, einen vollständigen Befehlssatz zu finden.
Jeder Befehl kann maximal einen angegebenen Parameter haben, der zweite Parameter ist grundsätzlich durch den Befehl vorgegeben, so überhaupt ein zweiter Parameter nötig ist. Als Beispiel soll hier der Befehl ‚lda’ dienen, der einen Wert in den Akkumulator lädt. Lda lässt sich verschiedentlich laden, so kann man einen Wert direkt angeben, der in den Akku geladen werden soll (lda #$00 , lade den Wert(#) 00 Hex($) in den Akku), oder eine Speicherstelle angeben, deren Wert in den Akku geladen werden soll (lda $2214 , lade den Wert der Speicherstelle 2214 Hex($) in den Akku). Auch kann Registerindirekt adressiert werden (lda $2214,x , lade den Wert in den Akku, der sich in der Speicherstelle befindet, die durch 2214 Hex($) plus den Inhalt des X-Registers(x) adressiert wird). Statt einer 16 Bit-Adresse kann auch eine 24 Bit-Adresse angegeben werden, oder es kann über das Direct-Register adressiert werden.


6.2 Initialisierungsparameter

Spoiler Spoiler


Um einen SNES emulieren zu können, muss sich ein Emulator auch wie ein solcher verhalten. Daher muss er auch den Initialisierungsalgorithmus abbilden, der im SNES zum Zeitpunkt des Anschaltens abläuft. Hierzu gibt es im Netz zwei Init-Routinen, die einfach in jedes Emulator-Programm eingebunden werden können, und den Emulator auf den Stand setzen, dass er das eigentliche Programm genau so abarbeitet, wie ein echter SNES es tun würde. Die Routinen sind in den Anhängen A und B zu finden, die Inhalte seien hier noch mal kurz erläutert:
header.inc
In der header.inc werden allgemeine Dinge definiert, die teilweise beim echten SNES durch die Hardware schon vorgegeben sind, teilweise von SNES zu SNES variieren können. Allen gemeinsam ist aber, dass sie bei einem SNES immer gleich sind. Am wichtigsten sind hier die Definitionen der ROM-Bänke und der ROM-Startadressen, so dass klar ist, über wie viel Speicher der simulierte ROM verfügt und wo dieser adressiert wird. Als Zusatz lässt sich hier der Name des zu schreibenden Programms definieren.
Snes_Init.asm
Die Snes_Init.asm ist, entgegen der header.inc, keine Header-Datei, sondern beinhaltet ein Makro, welches ein Unterprogramm aufruft. Somit könnte sie auch im eigentlichen Programmcode stehen. Dass sie dennoch ausgegliedert ist, liegt einfach daran, dass der Code sich immer wieder verwenden lässt, und als eigene Datei einfacher einzubinden ist. Der SNES verfügt über nur wenige Register in der CPU, sondern lässt viele wichtige Werte im RAM an eigens dafür vorgesehenen Stellen ablegen. Die Snes_Init.asm schreibt an diese Stellen die benötigten Werte, so dass das Programm mit einem Minimum an Änderungen auskommt, ohne irgendwo undefinierte Werte stehen zu haben. Das wiederum heisst in den meisten Fällen, dass die entsprechenden Speicherstellen mit null initialisiert werden (stz = Store Zero), an einigen Stellen werden aber auch andere Werte geschrieben, so wird der Bildschirm mit „Ausgeschaltet, volle Helligkeit“ initialisiert, damit im Programm hier eine Ausgabe definiert werden kann, und er dann angeschaltet werden kann.


6.3 Entwickeltes Testprogramm

Spoiler Spoiler


Die Programme wurden in mehreren Zyklen entwickelt, mit dem festen Ziel, am Ende etwas „zum Zeigen“ zu haben. So wurde mit der Informationssuche begonnen, und als erstes ein einfaches Programm entwickelt, welches den Bildschirm einfärbt. Diese Funktion ist auch noch im Soundtest-Programm zu finden. Zu diesem Zeitpunkt lagen die beiden Init-Dateien schon vor, wodurch dieser Schritt sich recht einfach gestaltete. Das Programm tut nicht mehr, als den Bildschirm abzuschalten, die Farbe durch Ändern der entsprechenden Parameter zu wechseln, und den Bildschirm wieder einzuschalten.
Da über den Soundchip und dessen Programmierung am meisten Informationen vorlagen, war das der logische nächste Schritt. Dies gestaltete sich etwas schwieriger, da mit einem anderen Chip, dem Soundchip, kommuniziert werden musste. Da aber der größte Teil der SNES-Programmierung auf diesem Wege abläuft, war das unumgänglich. Der Ablauf war aus en Inormationen ersichtlich: Daten zuerst auf dem ROM, dies sollte im Initialisierungsteil geschehen, da auf dem SNES die Daten auch schon von Anfang an auf dem ROM sind. Dann müssen die Daten auf den RAM der CPU und von da weiter in den RAM des Soundchips. Weiterhin müssen die Header-Informationen vom ROM auf den RAM, und dort in ein kleines Programm verpackt werden, welches das Abspielen der Datei startet. Dieses Programm muss ebenfalls an den Soundchip gesendet werden und dort gestartet werden. Mit entsprechender Initialisierung wird die Musik dann als Endlosschleife abgespielt. Der Quellcode ist in Anhang C zu finden.
Der dritte Schritt war dann die Entwicklung eines kleinen Grafik-Programms. Hierzu lag ein rudimentäres Beispielprogramm vor, welches entsprechend modifiziert werden musste. Die Quelle des Beispielprogramms liegt uns leider nicht mehr vor. Ein Datensatz zum Anzeigen von Buchstaben war ebenfalls gefunden worden. Das Programm wurde in vier einzelnen schritten entwickelt: Anzeigen eines Buchstaben, eines Textes, eines fliessenden Textes, und als letztes, eines in einer Sinuskurve schwingenden Textes, als optisches „Highlight“. Der Aufbau ist immer ähnlich. Zunächst wird der SNES initialisiert, wobei hier noch zusätzliche unterprogramme hinzukommen, die den Grafik-RAM leeren und den Buchstaben-Datensatz hineinkopieren. Dann wird der anzuzeigende Text identifiziert und Zeilenweise angezeigt. Zuletzt wird noch der Bildschirm wieder angeschaltet, und je nach Programm das ganze auf dem Stand angehalten oder immer wieder neu gestartet. Bei einem einzelnen Buchstaben gestaltet sich das recht einfach: Der Buchstabe wird identifiziert und alle Pixel desselben angezeigt. Bei der Zeile ist das schon schwieriger, da jeder Buchstabe einzeln angezeigt werden muss. Bei dem Fliesstext wird nur ein Teil des Textes angezeigt, wobei der angezeigte Teil jeweils nach der Anzeige geändert wird, beim schwingenden Text muss die Anzeige noch mit der Sinuskurve verrechnet werden. Der Quelltext der letzten Variante, des schwingenden Textes, ist in Anhang D zu finden.


6.4 Kompilierung und Test

Spoiler Spoiler


Geschrieben werden die Programme, aufgrund ihrer Assembler-ähnlichen Programmiersprache, mit jedem denkbaren Texteditor. Es mag spezielle Programme geben, die das Schreiben eines SNES-Programmes erleichtern, doch zumindest waren solche für uns nicht auffindbar. Ein einmal geschriebenes Programm wird dann, sobald fertig und fehlerfrei, in eine Objektdatei kompiliert. Wir haben dazu das WLA DX Macro Assembler Package verwendet, welches neben Compiler und Linker noch eine Textausgabe der gefundenen Fehler anzeigt. Dies ist zum debuggen sehr hilfreich. Wenn die Objektdatei existiert, muss eine Link-Datei erstellt werden, in der alle Objektdateien des aktuellen ROM aufgeführt sind. Diese Datei ist, zumindest bei dem von uns genutzten Linker, notwendig, auch wenn nur eine Objektdatei existiert. Die Objektdatei und die Linkdatei werden dann dem Linker übergeben, der daraus eine smc-Datei macht, die von jedem üblichen SNES-Emulator gelesen werden kann. Wir haben hier den ZSNES-Emulator verwendet. Im Emulator lässt sich jetzt das entsprechende simulierte ROM auswählen und starten. Über die Ausgabe erhält man eine weitere Möglichkeit, den Code zu testen.
Signature from »KillBill_158«



Wer sich entschieden hat, etwas zu tun, und an nichts anderes denkt, überwindet alle Hindernisse.

This post has been edited 10 times, last edit by "KillBill_158" (Jun 19th 2012, 4:24pm)


1 guest thanked already.

KillBill_158

Supergenie

  • "KillBill_158" is male
  • "KillBill_158" started this thread

Posts: 1,967

Thanks: 136

  • Send private message

2

Tuesday, June 19th 2012, 3:39pm

7 Anhänge


7.1 Anhang A: header.inc

Spoiler Spoiler

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
;==LoRom==      	; We'll get to HiRom some other time.

.MEMORYMAP                 	; Begin describing the system architecture.
  SLOTSIZE $8000            	; The slot is $8000 bytes in size. More       ; details on slots later.
  DEFAULTSLOT 0           	; There's only 1 slot in SNES, there are more ; in other consoles.
  SLOT 0 $8000             	; Defines Slot 0's starting address.
.ENDME         	; End MemoryMap definition

.ROMBANKSIZE $8000          	; Every ROM bank is 32 KBytes in size
.ROMBANKS 8                 	; 2 Mbits - Tell WLA we want to use 8 ROM     ; Banks

.SNESHEADER
  ID "SNES"               	; 1-4 letter string, just leave it as "SNES"
  
  NAME "SNES Tile Demo       "  ; Program Title - can't be over 21 bytes,
  ;    "123456789012345678901"  ; use spaces for unused bytes of the name.

  SLOWROM
  LOROM

  CARTRIDGETYPE $00         	; $00 = ROM only, see WLA documentation for others
  ROMSIZE $08              	; $08 = 2 Mbits,  see WLA doc for more..
  SRAMSIZE $00             	; No SRAM         see WLA doc for more..
  COUNTRY $01              	; $01 = U.S.  $00 = Japan, that's all I know
  LICENSEECODE $00         	; Just use $00
  VERSION $00             	; $00 = 1.00, $01 = 1.01, etc.
.ENDSNES

.SNESNATIVEVECTOR          	; Define Native Mode interrupt vector table
  COP EmptyHandler
  BRK EmptyHandler
  ABORT EmptyHandler
  NMI VBlank
  IRQ EmptyHandler
.ENDNATIVEVECTOR

.SNESEMUVECTOR             	; Define Emulation Mode interrupt vector table
  COP EmptyHandler
  ABORT EmptyHandler
  NMI EmptyHandler
  RESET Start              	; where execution starts
  IRQBRK EmptyHandler
.ENDEMUVECTOR

.BANK 0 SLOT 0              	; Defines the ROM bank and the slot it is inserted in memory.
.ORG 0                     	; .ORG 0 is really $8000, because the slot starts at $8000
.SECTION "EmptyVectors" SEMIFREE

EmptyHandler:
       rti

.ENDS

.EMPTYFILL $00              	; fill unused areas with $00, opcode for BRK.  
                            	; BRK will crash the snes if executed.


7.2 Anhang B: Snes_Init.asm

Spoiler Spoiler

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
.MACRO Snes_Init
        sei             	; Disabled interrupts
        clc             	; clear carry to switch to native mode
        xce             	; Xchange carry & emulation bit. native mode
	rep     #$18    	; Binary mode (decimal mode off), X/Y 16 bit
        ldx     #$1FFF  	; set stack to $1FFF
        txs

        jsr Init
.ENDM

.bank 0
.section "Snes_Init" SEMIFREE

Init:
        sep     #$30    	; X,Y,A are 8 bit numbers
        lda     #$8F    	; screen off, full brightness
        sta     $2100   	; brightness + screen enable register 
        stz     $2101   	; Sprite register (size + address in VRAM) 
	stz     $2102   	; Sprite registers (address of sprite          ; memory [OAM])
        stz     $2103   	; same as above
        stz     $2105   	; Mode 0, = Graphic mode register
        stz     $2106   	; noplanes, no mosaic, = Mosaic register
        stz     $2107   	; Plane 0 map VRAM location
        stz     $2108   	; Plane 1 map VRAM location
        stz     $2109   	; Plane 2 map VRAM location
        stz     $210A   	; Plane 3 map VRAM location
        stz     $210B   	; Plane 0+1 Tile data location
        stz     $210C   	; Plane 2+3 Tile data location
        stz     $210D   	; Plane 0 scroll x (first 8 bits)
	stz     $210D   	; Plane 0 scroll x (last 3 bits) #$0 - #$07ff
        	stz     $210E   	; Plane 0 scroll y (first 8 bits)
        stz     $210E   	; Plane 0 scroll y (last 3 bits) #$0 - #$07ff
        stz     $210F  	; Plane 1 scroll x (first 8 bits)
        stz     $210F  	; Plane 1 scroll x (last 3 bits) #$0 - #$07ff
        stz     $2110   	; Plane 1 scroll y (first 8 bits)
        stz     $2110  	; Plane 1 scroll y (last 3 bits) #$0 - #$07ff
        stz     $2111   	; Plane 2 scroll x (first 8 bits)
        stz     $2111  	; Plane 2 scroll x (last 3 bits) #$0 - #$07ff
        stz     $2112  	; Plane 2 scroll y (first 8 bits)
        stz     $2112  	; Plane 2 scroll y (last 3 bits) #$0 - #$07ff
        stz     $2113   	; Plane 3 scroll x (first 8 bits)
        stz     $2113  	; Plane 3 scroll x (last 3 bits) #$0 - #$07ff
        stz     $2114  	; Plane 3 scroll y (first 8 bits)
        stz     $2114  	; Plane 3 scroll y (last 3 bits) #$0 - #$07ff
        lda     #$80   	; increase VRAM address after writing to $2119
        sta     $2115  	; VRAM address increment register
        stz     $2116  	; VRAM address low
        stz     $2117  	; VRAM address high
        stz     $211A  	; Initial Mode 7 setting register
        stz     $211B  	; Mode 7 matrix parameter A register (low)
        lda     #$01
        sta     $211B  	; Mode 7 matrix parameter A register (high)
        stz     $211C   	; Mode 7 matrix parameter B register (low)
        stz     $211C   	; Mode 7 matrix parameter B register (high)
        stz     $211D   	; Mode 7 matrix parameter C register (low)
        stz     $211D   	; Mode 7 matrix parameter C register (high)
        stz     $211E   	; Mode 7 matrix parameter D register (low)
        sta     $211E   	; Mode 7 matrix parameter D register (high)
        stz     $211F   	; Mode 7 center position X register (low)
        stz     $211F   	; Mode 7 center position X register (high)
        stz     $2120   	; Mode 7 center position Y register (low)
        stz     $2120   	; Mode 7 center position Y register (high)
        stz     $2121   	; Color number register ($0-ff)
        stz     $2123   	; BG1 & BG2 Window mask setting register
        stz     $2124   	; BG3 & BG4 Window mask setting register
        stz     $2125   	; OBJ & Color Window mask setting register
        stz     $2126   	; Window 1 left position register
        stz     $2127   	; Window 2 left position register
        stz     $2128   	; Window 3 left position register
        stz     $2129   	; Window 4 left position register
        stz     $212A   	; BG1, BG2, BG3, BG4 Window Logic register
        stz     $212B   	; OBJ, Color Window Logic Register              ; (or,and,xor,xnor)
        sta     $212C   	; Main Screen designation (planes, sprites     ; enable)
        stz     $212D   	; Sub Screen designation
        stz     $212E   	; Window mask for Main Screen
        stz     $212F   	; Window mask for Sub Screen
        lda     #$30
        sta     $2130   	; Color addition & screen addition init setting
        stz     $2131   	; Add/Sub sub designation for screen, sprite,  ; color
        lda     #$E0
        sta     $2132   	; color data for addition/subtraction
        stz     $2133   	; Screen setting (interlace x,y/enable SFX     ; data)	
        stz     $4200   	; Enable V-blank, interrupt, Joypad register
        lda     #$FF
        sta     $4201   	; Programmable I/O port
        stz     $4202   	; Multiplicand A
        stz     $4203   	; Multiplier B
        stz     $4204   	; Multiplier C
        stz     $4205   	; Multiplicand C
        stz     $4206   	; Divisor B
        stz     $4207   	; Horizontal Count Timer
        stz     $4208   	; Horizontal Count Timer MSB (most significant ; bit)	
        stz     $4209   	; Vertical Count Timer
        stz     $420A   	; Vertical Count Timer MSB
        stz     $420B   	; General DMA enable (bits 0-7)
        stz     $420C   	; Horizontal DMA (HDMA) enable (bits 0-7)
        stz     $420D   	; Access cycle designation (slow/fast rom)
        cli             	; Enable interrupts
        rts
.ends


7.3 Anhang C: Soundtest.asm

Spoiler Spoiler

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
; Dieses Programm spielt eine vorher definierte Sounddatei ab.
; Die Daten der Sounddatei werden dabei so behandelt, wie sie ein SNES auch ; behandeln würde.
 
.include "Header.inc"
.include "Snes_Init.asm"	; Einbindung der Initialisierungsdateien
 
.define BG1MoveH $7E1A25
.define BG1MoveV $7E1A26
.define BG2MoveH $7E1A27
.define BG2MoveV $7E1A28
.define BG3MoveH $7E1A29
.define BG3MoveV $7E1A2A	; Einige Definitionen von Werten, die laut     ; "Snes_Init.asm" gebraucht werden
 
VBlank:
   	rti	; Definition, die laut "Header.inc" gebraucht  ; wird
    
.define AUDIO_R0 $2140	; Definition der vier Soundports, die für die 
.define AUDIO_R1 $2141	; Kommunikation der SNES-CPU mit der Sound-CPU 
.define AUDIO_R2 $2142	; benötigt werden. Diese Ports sind über 
.define AUDIO_R3 $2143	; Speicherstellen im SNES-RAM abgebildet, die  ; auch von der Sound-CPU, allerdings mit       ; anderen Adressen, angesprochenwerden können. ; Sie Dienen als Ports in beide Richtungen.
 
.define XY_8BIT $10
.define A_8BIT  $20	; Einige Definitionen von Werten, die im       ; Weiteren gebraucht werden
 
.define musicSourceAddr $00fd
; Speicherplatz, der später für die            ; Kopiervorgänge benötigt wird. 
; Hier wird die Quelladresse der Musik           ; gespeichert und hochgezählt.

.define spcFile "OTTest.spc"	; Der Name der Musik-Quelldatei
.define spcFreeAddr $ffa0	; Adresse im RAM des Soundchips, bei der später ; die Init-Routine gespeichert wird
								
; Speicherbank für die erste Hälfte der Daten der Musik-Quelldatei. 
; Im Zuge der Definition werden die Daten gleich aus der Musikdatei in den ; simulierten ROM geschrieben.
.bank 1
.section "musicData1"
spcMemory1: .incbin spcFile skip $00100 read $8000 
		; Kopiervorgang von Datei zu ROM
.ends

; Speicherbank für die zweite Hälfte der Daten der Musik-Quelldatei. 
; Im Zuge der Definition werden die Daten gleich aus der Musikdatei in den ; simulierten ROM geschrieben.								
.bank 2
.section "musicData2"
spcMemory2: .incbin spcFile skip $08100 read $8000
		; Kopiervorgang von Datei zu ROM
.ends
								
; Speicherbank für den Programmcode auf der SNES-CPU
.bank 0
.section "MainCode"
								
dspData:  .incbin spcFile skip $10100 read $0080
; DSP-Daten der Musik-Quelldatei, werden direkt ; in die "MainCode"-Sektion des ROM geschrieben
 audioPC:  .incbin spcFile skip $00025 read $0002
 audioA:   .incbin spcFile skip $00027 read $0001
 audioX:   .incbin spcFile skip $00028 read $0001
 audioY:   .incbin spcFile skip $00029 read $0001
 audioPSW: .incbin spcFile skip $0002a read $0001
 audioSP:  .incbin spcFile skip $0002b read $0001
; Headerinformationen der Musik-Quelldatei,    ; werden direkt in die "MainCode"-Sektion des  ; ROM geschrieben
 
; Hauptprogramm
; Hier wird eine Subroutine aufgerufen, die sich um die Musik kümmert.     ; Danach wird der Hintergrund gefärbt und eine Endlosschleife gestartet.
Start:
	Snes_Init	; Aufruf der Initialisierung des Emulators
     jsr LoadSPC	; Aufruf des Musik-Unterprogramms
     
     ; Färben des Hintergrundes
     sep     #$20	; Akku auf 8 Bit Breite setzen
     lda     #%10000000	; Helligkeit wird auf 0% gesetzt
     sta     $2100
     lda     #%11100000	; Untere 8 Bit der Hintergrundfarbe laden (Hier ; grün)
     sta     $2122
     lda     #%00000000	; Obere 8 Bit der Hintergrundfarbe laden (Hier ; grün)
     sta     $2122
     lda     #%00001111	; Helligkeit wieder auf 100% setzen, damit die ; Farbe angezeigt wird
     sta     $2100
 
Forever:
     jmp Forever	; Endlosschleife
 
; Makro zum Kopieren eines Datenblocks
; Das Makro selbst speichert die übergebenen daten an entsprechenden       ; Stellen des Speichers und ruft ein Unterprogramm auf, das den              ; Kopiervorgang übernimmt.
.macro sendMusicBlockM	; Parameter: Quellsegment, Quelladresse,          ; Zieladresse, Länge
     
    	sep     #A_8BIT	; Akku auf 8 Bit Breite setzen
	lda     #\1	; Lade Quellsegment in den Akku (\1: Erster    ; Parameter)
    	sta     musicSourceAddr + 2
; Speichere Quellsegment an der vorgesehenen   ; Speicherstelle
    	rep     #A_8BIT	; Akku auf 16 Bit Breite setzen
    	lda     #\2	; Lade Quelladresse in den Akku (\2: Zweiter    ; Parameter)
    	sta     musicSourceAddr	; Speichere Quelladresse an der vorgesehenen   ; Speicherstelle
	rep     #XY_8BIT	; X-Register und Y-Register auf 16 Bit Breite   ; setzen
   	ldx     #\3	; Speichere Zieladresse in X-Register (\3:     ; Dritter Parameter)
    	ldy     #\4	; Speichere Länge in Y-Register (\4: Vierter   ; Parameter)
    	jsr     CopyBlockToSPC	; Aufruf des Unterprogramms für den            ; Kopiervorgang
 .endm
 
; Unterprogramm, welches für das eigentliche Abspielen der Musik zuständig ; ist. Hier werden die Programme zum kopieren der Musik und zur Ausführung ; des Abspielvorgangs aufgerufen.
LoadSPC:
	jsr     CopySPCMemoryToRam	
; Subroutine, mittels der die Sounddaten aus   ; dem ROM in den RAM kopiert werden
    	stz     $4200   
    	sei	; Deaktiviert Interrupts und Controllereingaben
    	sendMusicBlockM $7f $0002 $0002 $ffbe
		; Kopiert die Daten vom RAM in den Speicher der ; Sound-CPU
	jsr     MakeSPCInitCode	; Schreibt die Initialisierungsroutine für den ; Soundchip anhand der Headerinformationen
    	sendMusicBlockM $7f $0000 spcFreeAddr $0016
; Speichert den Initcode des Soundprogramms an ; einer freien Speicherstelle im Soundchip-RAM
    	jsr     InitDSP	; Sendet die DSP-Daten der Sounddatei an die   ; Sound-CPU
	rep     #XY_8BIT	; X-Register und Y-Register auf 16 Bit Breite   ; setzen
    	ldx     #spcFreeAddr	; Lade die Speicheradresse des Initcodes der   ; Sounddatei im Soundchip-RAM in das X-Register
    	jsr     StartSPCExec	; Startet die Ausführung der Sound-Schleife
    	cli             
    	sep     #A_8BIT	; Akku auf 8 Bit
    	lda     #$80				
    	sta     $4200	; Reaktiviert interrupts und Controllereingaben
    	rts
 
; Subroutine, mittels der die Sounddaten aus dem ROM in den RAM kopiert    ; werden, vom Datenende an Rückwärts
CopySPCMemoryToRam:
	Rep     #XY_8BIT	; X,Y auf 16 Bit
	ldx.w   #$7fff	; X-Register als Counter auf Anfangswert       ; initialisieren (Rückwärts, also höchster Wert ; beginnt)
CopyLoop:
    	lda.l   spcMemory1,x	; Lade den Inhalt der ersten Speicherbank des   ; ROM an der durch das X-Register vorgegebenen ; Position in den Akku
     sta.l   $7f0000,x	; Speichere den Inhalt des Akku an die         ; Speicherstelle 7f0000 Hex plus den Inhalt des ; X-Register
     lda.l   spcMemory2,x	; Lade den Inhalt der ersten Speicherbank des   ; ROM an der durch das X-Register vorgegebenen ; Position in den Akku
     sta.l   $7f8000,x	; Speichere den Inhalt des Akku an die         ; Speicherstelle 7f8000 Hex plus den Inhalt des ; X-Register
     dex	; verringere den Wert im X-Register um 1
     bpl     CopyLoop	; Springe zu CopyLoop, wenn der Wert im X-     ; Register positiv ist (bpl: Branch if PLus)
     rts
 
; Sendet die DSP-Daten der Sounddatei an die Sound-CPU
InitDSP:
    	rep    #XY_8BIT        	; X,Y auf 16 Bit
    	ldx    #$0000	; X-Register auf 0 setzen
InitLoop:
    	sep    #A_8BIT	; Akku auf 8 Bit
    	txa                    	; Kopiere den Inhalt des X-Registers in den    ; Akku (Hier: Das untere Byte von X)
    	sta    $7f0100         	; Speichere den Inhalt des Akku an die         ; Speicherstelle 7f0100 Hex
    	lda.l  dspData,x       	; Lade den Akku mit dem Wert des Bytes der DSP-; Daten, der vom X-Register angegeben wird
    	sta    $7f0101        	; Speichere den Inhalt des Akku an die         ; Speicherstelle 7f0101 Hex
    	phx                    	; Rette den Inhalt des X-Registers auf den     ; Stack
; Rufe das Block-Kopier-Makro auf, dass die Speicherstellen 7f0100 und ; 7f0101 in den RAM der Sound-CPU schreiben soll.
    	sendMusicBlockM $7f $0100 $00f2 $0002
 
    	rep    #XY_8BIT            
    	plx	; Schreibe den Inhalt von X vom Stack wieder in ; das X-Register
    	inx	; Erhöhe X um 1
   	cpx    #$0080	; Vergleiche X mit 80 Hex
    	bne    InitLoop	; Wenn X geringer, springe zu InitLoop
    	rts
								
; Schreibt die Initialisierungsroutine für den Soundchip anhand der        ; Headerinformationen
MakeSPCInitCode:
	; Es müssen folgende Daten geschrieben werden:
    	; 00-Byte nach 00.
    	; 01-Byte nach 01.
    	; Wert für s nach s.
    	; PSW-Wert auf den Stack.
    	; Wert für a nach a.
    	; Wert für x nach x.
    	; Wert für y nach y.
    	; Lade PSW-Wert vom Stack.
    	; Springe zur Position des Programm-Counters.
 
    	sep     #A_8BIT
 
    	; Push [01]-Wert auf den Stack.
    	lda.l   $7f0001
    	pha
 
    	; Push [00]-Wert auf den Stack.
    	lda.l   $7f0000
    	pha
								
    	; Schreibe Code fürs setzen des [00]-Wertes.
    	lda     #$8f	; mov dp,#imm
    	sta.l   $7f0000
   	pla
    	sta.l   $7f0001
    	lda     #$00
    	sta.l   $7f0002
 
    	; Schreibe Code fürs setzen des [01]-Wertes.
    	lda     #$8f	; mov dp,#imm
    	sta.l   $7f0003
    	pla
    	sta.l   $7f0004
    	lda     #$01
    	sta.l   $7f0005
 
    	; Schreibe Code fürs setzen des s-Wertes.
    	lda     #$cd	; mov x,#imm
    	sta.l   $7f0006
    	lda.l   audioSP
    	sta.l   $7f0007
    	lda     #$bd	; mov sp,x
    	sta.l   $7f0008
 
    	; Schreibe Code fürs pushen von PSW auf den Stack.
    	lda     #$cd	; mov x,#imm
    	sta.l   $7f0009
    	lda.l   audioPSW
    	sta.l   $7f000a
    	lda     #$4d	; push x
    	sta.l   $7f000b
 	
    	; Schreibe Code fürs setzen des Akku-Wertes.
    	lda     #$e8	; mov a,#imm
    	sta.l   $7f000c
    	lda.l   audioA
    	sta.l   $7f000d
 	
    	; Schreibe Code fürs setzen des x-Wertes.
    	lda     #$cd	; mov x,#imm
    	sta.l   $7f000e
    	lda.l   audioX
    	sta.l   $7f000f
 	
	; Schreibe Code fürs setzen des y-Wertes.
    	lda     #$8d	; mov y,#imm
    	sta.l   $7f0010
    	lda.l   audioY
    	sta.l   $7f0011
 
	; Schreibe Code fürs holen von PSW vom Stack.
    	lda     #$8e	; pop psw
    	sta.l   $7f0012
 	
	; Schreibe Code fürs springen.
    	lda     #$5f	; jmp labs
    	sta.l   $7f0013
    	rep     #A_8BIT
    	lda.l   audioPC
    	sep     #A_8BIT
    	sta.l   $7f0014
    	xba
    	sta.l   $7f0015
    	rts
 
; Unterprogramm für den Kopiervorgang
; Dieses Unterprogramm ist für die Kommunikation mit dem Soundchip und das ; Verschieben der Daten vom SNES-RAM in den Sound-RAM zuständig.
; Es verschiebt die Daten anhand der übergebenen Adressen: 
;	Die Quelladresse ist in musicSourceAddr gespeichert
;	Die Zieladresse findet sich im X-Register
;	Die Länge des zu kopierenden Blocks ist im Y-Register gespichert
CopyBlockToSPC: 
	; Warte darauf, dass die Sound-CPU bereit ist.
    	sep     #A_8BIT	; Akku auf 8 Bit
    	lda     #$aa	; Lade den Wert aa Hex in den Akku
WaitLoop1:
    	cmp     AUDIO_R0	; Vergleiche den Wert im Akku mit dem Wert am  ; ersten Soundport
	bne     WaitLoop1	; Sollte dieser Wert nicht anliegen, warte, bis ; er anliegt

   	stx     AUDIO_R2	; Speichere die Zieladresse im Port 3
 
    	phy	; Schreibe den Wert von Y auf den Stack
    	plx	; und lade ihn in X
 
; Start des Kopiervorganges durch senden eines Befehlscodes an die     ; Sound-CPU.
    	lda     #$01				
    	sta     AUDIO_R1	; Lade den Wert 01h in den zweiten Soundport
    	lda     #$cc
    	sta     AUDIO_R0	; Lade den Wert cch in den ersten Soundport
WaitLoop2:
	cmp     AUDIO_R0	; Vergleiche den Wert im Akku mit dem Wert am  ; ersten Soundport
    	bne     WaitLoop2	; Sollte dieser Wert nicht anliegen, warte, bis ; er anliegt

    	ldy     #$0000	; Initialisiere das Y-Register mit 0 als         ; Counter
 
CopyBlockToSPC_loop:
	Xba	; Tausche die Bytes des Akku
    	lda     [musicSourceAddr],y
; Lade den Akku mit dem y-ten zu übertragenden ; Byte
    	xba	; Tausche die Bytes des Akku, so dass das      ; Adressbyte im High Byte des Akkus liegt
	tya		; Lade den Inhalt von Y in A (das untere Byte  ; von Y)
 
   	rep     #A_8BIT	; Akku auf 16 Bit
	sta     AUDIO_R0	; Sende den Inhalt des Akku an den ersten 	    ; Soundport
    	sep     #A_8BIT	; Akku auf 8 Bit
 
WaitLoop3:
	cmp     AUDIO_R0	; Vergleiche den Wert im Akku mit dem Wert am  ; ersten Soundport
    	bne     WaitLoop3	; Sollte dieser Wert nicht anliegen, warte, bis ; er anliegt
 
    	iny	; Zähler hochzählen
    	dex	; Anzahl noch zu sendender Bytes runterzählen
    	bne     CopyBlockToSPC_loop
; Wenn noch Bytes zu senden, springe zu        ; CopyBlockToSPC_loop
 
	ldx     #$ffc9	; Lade den Wert der Startadresse der IPL ROM    ; Routine in das X-Register
    	stx     AUDIO_R2	; Sende den wert an den dritten Soundport
 
    	xba
    	lda     #0	; Lösche das High Byte des Akku
    	xba
 
    	clc
    	adc     #$2	; Stoppe den Counter
 
    	rep     #A_8BIT	; Akku auf 16 Bit
    	sta     AUDIO_R0	; Sende den Inhalt des Akku an den ersten      ; Soundport
    	sep     #A_8BIT	; Akku auf 8 Bit
 
WaitLoop4:
	cmp     AUDIO_R0	; Vergleiche den Wert im Akku mit dem Wert am  ; ersten Soundport
    	bne     WaitLoop4	; Sollte dieser Wert nicht anliegen, warte, bis ; er anliegt
    	rts

; Startet die Ausführung der Sound-Schleife
; Die Startadresse des Initcodes der Sounddatei befindet sich im X-Register
StartSPCExec:
	; Warte darauf, dass die Sound-CPU bereit ist.
    	sep     #A_8BIT	; Akku auf 8 Bit
    	lda     #$aa	; Lade den Wert aa Hex in den Akku
WaitLoop5:
	cmp     AUDIO_R0	; Vergleiche den Wert im Akku mit dem Wert am   ; ersten Soundport
    	bne     WaitLoop5	; Sollte dieser Wert nicht anliegen, warte, bis ; er anliegt
     
    	stx     AUDIO_R2	; Sende die Startadresse an den dritten        ; Soundport
 
; Start der Programmausführung durch senden eines Befehlscodes an die   ; Sound-CPU.
    	lda     #$00				
    	sta     AUDIO_R1	; Lade den Wert 00h in den zweiten Soundport
    	lda     #$cc
    	sta     AUDIO_R0	; Lade den Wert cch in den ersten Soundport
WaitLoop6:
	cmp     AUDIO_R0	; Vergleiche den Wert im Akku mit dem Wert am  ; ersten Soundport
    	bne     WaitLoop6	; Sollte dieser Wert nicht anliegen, warte, bis ; er anliegt
    	rts
 
.ends


7.4 Anhang D: Flex.asm

Spoiler Spoiler

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
; Dieses Programm zeigt einen Text auf dem Bildschirm an, der über den     ; Bildschirm scrollt und sich dabei dreht. Der Text wird endlos angezeigt, ; wiederholt sich also.

.include "Header.inc"			
.include "Snes_Init.asm"	; Einbindung der Initialisierungsdateien

VBlank:
  	RTI		; Definition, die laut "Header.inc" gebraucht  ; wird

.bank 0
.section "MainCode"	; Der Code des Programms befindet sich in der  ; Speicherbank 0 des ROM

; Die Startroutine schreibt einige Initialisierungen, um die Textanzeige zu ; ermöglichen. Dann werden einige Unterprogramme aufgerufen, die den RAM   ; entsprechend vorbereiten und die Buchstaben und Farben kopieren.
; Gleichzeitig werden verschiedene Zähler definiert, die für die           ; Textanzeige benötigt werden. Zuletzt wird eine Endlosschleife gestartet, ; die die Textanzeige am laufen hält.
Start:  
	Snes_Init	; Aufruf der Initialisierung des Emulators
	rep     #$10	; X, Y-Register auf 16 Bite Breite
	sep     #$20	; Akkumulator auf 8 Bit Breite
	lda	#$00			
	sta	$2105	; Setze Bildschirm auf Modus 0, 8*8 Bit Tiles
	lda	#$75	
	sta	$2107	; Hintergrund 1 Init-Einstellungen
	lda	#$00
	sta	$210b	; Hintergrund 1 Tile Init-Einstellungen
	lda	#$01				
	sta	$212c	; Hintergrund 1 aktiviert
	stz	$1000	; Startwert der Tile-Initialisierung
	stz	$1001	; Speichervariable für Tile-Initialisierung
	jsr	Copy_Gfx	; Funktion zum kopieren der Grafikdaten in RAM
	jsr	Copy_colors	; Funktion zum kopieren der Farbdaten in den   ; Farb-RAM
	jsr	Make_tiles	; Leeren des Screens und Tile-Initialisierung
	jsr	Clear_ram	; Leeren des VRAM

	ldx	#$0000
	stx	$1002	; Counter für die Zeilen eines zu zeichnenden  ; Buchtaben
	ldx	#$0000
	stx	$1004	; Counter für Buchstaben, die zu zeichnen sind

	ldx	#$0000
	stx	$1006	; Grafik-Offset eines Buchstaben. Gibt die     ; Position der entsprechenden Grafik im VRAM an

	ldx	#$0000
	stx	$1008	; Hier wird der Text-Offset für die Flex-      ; Funktion gespeichert

	ldx	#$0000
	stx	$100a	; Der aktuelle Offset der SINE-Werte 
	
	ldx	#$0000
	stx	$100c	; Speicherwert für den SINE-Offset
	
	ldx	#$0000
	stx	$100e	; Speicherwert für den SINE-Offset

	ldx	#$0000
	stx	$1010	; Scrollposition innerhalb eines Buchstaben.

	ldx	#$0000
	stx	$1012	; Scrollposition in Buchstaben im Text
	
	lda	#$00
	sta	$10
	lda	#$80			
	sta	$11			
	lda	#$7e		
	sta	$12	; Speichert den Wert 7e8000h in 10-12h, so dass ; mit [$10] $7e8000 adressiert werden kann

	lda	#$0f
	sta	$2100	; Bildschirm an, höchste Helligkeit
Waitloop:
	jsr	WaitVb	; Starte die WaitVb-Routine, um sicher zu sein, ; das neue Daten gesendet werden können
	jsr	Routines	; Starte die Berechnung und Übertragung neuer  ; Daten
	bra	Waitloop	; Springe wieder zu Waitloop, um den Prozess   ; erneut zu starten

; Routines ist eine Sammelfunktion, in der der DMA-Prozess vom Speicher in ; den VRAM angestossen wird. Weiterhin werden hier die Funktionen, die für ; die Anzeige des Textes benötigt werden, aufgerufen.
Routines:
	rep	#$10	; Setze X,Y-Register auf 16 Bit
	sep	#$20	; Setze Akku auf 8 Bit

; Start der allgemeinen DMA Routine zum Kopieren der Grafikdaten in die ; Ausgabe. Start eines DMA-Prozesses, der die Daten, die angezeigt      ; werden sollen, vom RAM des SNES in den VRAM schreibt.
	lda	#$00
	sta	$4330	; initialisiere Befehlsregister des vierten    ; DMA-Controllers		
	lda	#$18
	sta	$4331	; Setze Zieladresse auf VRAM
	lda	#$00
	sta	$4332
	lda	#$80		
	sta	$4333
	lda	#$7e
	sta	$4334	; Setzt Quelladresse auf 7E8000h
	ldx	#$0800
	stx	$4335	; Anzahl der zu übertragenden Bytes auf 800h
	lda	#$00
	sta	$2115	; Setze VRAM Autoinkrement auf 0
	ldx	#$0000
	stx	$2116	; Setzt Zieladresse im VRAM auf 0h
	lda	#$08		
	sta	$420b	; Startet DMA-Übertragungsprozess mit dem      ; vierten DMA-Controller
	lda	#$80	
	sta	$2115	; Autoinkrement Adresse im Grafik-RAM um 80h

	lda	$1010	; Lade aktuellen Scrolloffset im Buchstaben
	sta	$210d	; Scrolle den Bildschirm um diesen Offset
	stz 	$210d	; Setze den Scrolloffset auf null, um flackern ; zu verhindern
	jsr	Scroll	; Die Scrollroutine setzt den Scrolloffset neu, ; so das der Text weiter scrollt. Weiterhin    ; schreibt sie die Buchstaben, die angezeigt   ; werden sollen, in den Speicher.
	jsr	Flex	; Die Flex-Routine lässt die Buchstaben in     ; einer Sinuskurve schwingen und ist           ; gleichzeitig dafür zuständig, das die         ; Buchstaben zeilenweise in den Speicherbereich ; kopiert werden, aus dem sie später die DMA-   ; Routine in den VRAM kopiert.
	rts


; Die Scrollroutine ist dazu da, den Text in den Speicher zu schreiben, der ; aktuell angezeigt werden soll. Gleichzeitig berechnet sie, welcher Text  ; angezeigt wird, wenn der Text über den Bildschirm scrollt.
Scroll:
	lda	$1010	; Lade aktuellen Scrolloffset im Buchstaben
	clc
	adc	#$01	; Erhöhe um 1
	sta	$1010	; Speichere den erhöhten Offset
	cmp	#$08	; Prüfe, ob über einen Buchstaben gescrollt    ; wurde
	bcs	scrolltexts	; Wenn ja, springe zu scrolltexts
	rts
scrolltexts:
	stz	$1010	; Setze aktuellen Scrolloffset im Buchstaben   ; auf null
	ldy	$1012	; Lade Scrolloffset im Text ins Y-Register
	ldx	#$0000	; Lade das X-Register mit null als Counter für ; den Text, der auf dem Bildschirm ausgegeben  ; wird
copyscroll:
	lda	TEXT,y	; Lade den ersten anzuzeigenden Buchstaben in  ; den Akku
	sta	$7e7000,x	; Speichere den Buchstaben im RAM an der Stelle ; 7e7000h + X
	iny		; Zähle den Counter für den Text hoch
	cpy #$0040	; Prüfe, ob der Text zuende ist (der Text hat  ; momentan 32 Zeichen)
	beq endtext	; Wenn ja, springe zu endtext
moveon:
	inx		; Zähle die Counter für die Anzeige hoch
	cpx	#$0020	; Prüfe, ob die Anzeige schon voll ist (der    ; Bildschirm ist 16 Zeichen breit)
	bne	copyscroll	; Wenn noch nicht alle Zeichen ausgegeben      ; wurden, starte die Funktion neu
	ldy	$1012	; Hole aktuellen Text-Offset
	iny		; Erhöhe ihn um 1
	cpy #$0040	; Prüfe, ob der Text zuende ist
	beq endtext2	; Wenn ja, springe zu endtext
moveon2:
	sty	$1012	; Speichere den erhöhten Offset
	rts
endtext:
	ldy	#$0000	; Lade den Counter für den Text neu
	bra	moveon	
endtext2:
	ldy	#$0000	; Lade den Counter für den Text neu
	bra	moveon2	


; Die Flex-Routine lässt die Buchstaben in einer Sinuskurve schwingen und  ; ist gleichzeitig dafür zuständig, dass die Buchstaben zeilenweise in den ; Speicherbereich kopiert werden, aus dem sie später die DMA-Routine in den ; VRAM kopiert.
Flex:
	ldy	$100a	; Lese den aktuellen Absolut-SINE-Offset in Y  ; ein
	sty	$100e	; Speichere ihn, zur Verwendung für die        ; Erstellung aller Buchstaben
Flex1:
	ldy	$100e	; Lese den SINE-Offset der Zeile in Y ein
	sty	$100c	; Speichere ihn, zur Verwendung für die        ; Erstellung eines Buchstaben
	ldx	$1008	; Lese den aktuellen Text-Offset in X ein
	rep	#$30	; Setze Akku auf 16 Bit
	lda	$7e7000,x	; Lese nächsten anzuzeigenden Buchstaben
	and	#$003f	; Konvertiere das Format von ASCII zu C64-     ; Format und Lösche das High Byte
	asl 	a	; Nun wird der Wert vier mal mit 2 malgenommen 
	asl 	a	; (um 1 nach links geschoben) um seine 
	asl 	a	; Entsprechung im Charset zu finden, da jeder 
	asl 	a	; Buchstabe im Charset 16 Byte groß ist.
	sta	$1006	; Speichere den Wert
	tax		; Schreibe den Wert vom Akku ins X-Register
Flexdraw:
	ldy	$100c	; Lese den SINE-Offset für den aktuellen          ; Buchstaben in Y ein
	rep	#$30	; Setze Akku auf 16 Bit
	lda	SINE,y	; Lese SINE-daten an der Stelle y in den Akku
	and	#$00ff	; Lösche das High Byte
	tay		; Schreibe Akku ins Y-Register
	sep	#$20	; Setze Akku auf 8 Bit
	lda	$7ea000,x	; Lese das aktuelle Byte der Grafik des            ; aktuellen Buchstaben ein
	sta	[$10],y	; Speichere es an der Stelle, die der SINE-       ; Offset vorgibt. [$10] ist Speicherindirekt,  ; gespeichert wird also an der Stelle im        ; Speicher, die in der Speicherstelle 10h      ; steht.
	inx		; Hole das nächste Byte
	inc	$100c	; Erhöhe den SINE-Offset des Zeichens
	inc	$1002	; Erhöhe den Zeilencounter des aktuellen       ; Buchstaben
	lda	$1002	; Lade den Zeilencounter
	cmp	#$10	; Prüfe, ob 16 Zeilen gezeichnet wurden
	bne	Flexdraw	; Wenn nicht, springe zu Flexdraw
	
	stz	$1002	; Setze den Zeilencounter auf null
	inc	$1004	; Erhöhe den Buchstabencounter
	lda	$1004	; Lade den Buchstabencounter
	cmp	#$20	; Prüfe, ob 32 Buchstaben gezeichnet wurden
	beq	enddraw	; Wenn ja, springe zu enddraw
	rep	#$30	; Setze Akku auf 16 Bit
	lda	$10	; Lade den wert der Adresse 10h
	clc		; Lösche das Carry-Flag
	adc	#$0040	; Erhöhe die Adresse im Akku um 40h, also um    ; ein Zeichen. Jede Spalte ist 64 Byte hoch,   ; das ist aus der SINE-Tabelle ersichtlich. 64 ; dezimal sind 40h, und somit muss für die       ; nächste Spalte der Wert um 40h erhöht werden.
	sta	$10	; Speichere den Wert wieder in 10h
	sep	#$20	; Setze Akku auf 8 Bit
	
	lda	$100e	; Lade den SINE-Offset der Zeile in den Akku
	clc		; Lösche das Carry-Flag
	adc	#$fe	; Verringere den SINE-Offset um 2
	sta	$100e	; Speichere den Zeilenoffset wieder
	inc	$1008	; Erhöhe den Text-Offset um 1, um das nächste  ; Zeichen zu lesen
	bra	Flex1	; springe zu Flex1
enddraw:
	lda	#$00		
	sta	$10			
	lda	#$80		
	sta	$11	; Lade den Wert 8000h in 10-11h. 12h ändert    ; sich nicht
	stz	$1008	; Setze den Text-Offset auf null zurück
	stz	$1004	; Setze den Buchstabencounter auf null zurück
	inc	$100a	
	inc	$100a	; Erhöhe den Absolut-SINE-Offset um 2
	rts


; Die WaitVb (Virtual Blank) Routine wartet, bis der Bildschirm mit neuen   ; Daten versorgt werden kann.
WaitVb:	
	lda	$4210	; Hier wird das NMI-Flag zurückgesetzt, indem  ; es gelesen wird. Dadurch wird ein NMI-Prozess ; angestossen, an dessen Ende das Flag wieder   ; gesetzt wird. Dieser Prozess ist             ; erforderlich, um die Anzeige neu schreiben zu ; können.
	bpl WaitVb	; Das NMI-Flag soll high sein. Sollte das nicht ; der Fall sein, 
			; springt bpl zu WaitVb.
	rts


; Diese Funktion leert den VRAM und schreibt das Buchstaben-Charset hinein, ; damit später die Buchstaben anhand dieses Charsets auf dem Bildschim     ; angezeigt werden können.
Copy_Gfx:
	ldx	#$0000		
Clearvr:
	stx	$2116	; Wähle VRAM-Adresse durch das X-Register
	stz	$2118		
	stz	$2119	; Lösche die Daten des VRAM an der Stelle
	inx		; Erhöhe das X-Register
	cpx	#$0000	; Prüfe, ob X wieder null erreicht hat
	bne	Clearvr	; Ist der RAM noch nicht zuende, springe zu    ; Clearvr

	ldx	#$0000	; Lade X mit null
	txy		; Kopiere null auch nach Y
Chardouble:			
	lda	Charset,y	; Lese das Y-te Byte des Charset on den Akku
	sta	$7ea000,x	; Speichere das Byte im RAM, an der Stelle     ; 7ea000h + X
	inx		; Erhöhe X um 1
	sta	$7ea000,x	; Speichere dasselbe Byte im RAM um eine Stelle ; im RAM weiter. Dadurch wird jeder Buchstabe   ; doppelt so hoch
	inx		; Erhöhe X um 1
	iny		; Erhöhe Y um 1
	cpy	#$0200	; Prüfe ob alle Bytes aller Buchstaben gelesen ; wurden
	bne	Chardouble	; Wenn noch nicht alle Bytes gelesen wurden,   ; springe zu Chardouble
	rts
	

; Diese Funktion kopiert die verwendeten Farben für Hintergrund und Schrift ; in den Farb-RAM Die Farben sind 16 Bit lang und im 0rrrrrgggggbbbbb-     ; Format gespeichert (0: nicht relevant, r: rot, g: grün, b: blau)
Copy_colors:
	stz	$2121	; Adresse 00h im Farb-RAM
	lda	#$FD	; High Byte des Hintergrundes
	sta	$2122
	lda	#$FF	; Low Byte des Hintergrundes
	sta	$2122
	lda	#$7C	; High Byte der Textfarbe
	sta	$2122
	lda	#$00	; Low Byte der Textfarbe
	sta	$2122
	rts


; Diese Funktion löscht die genutzten Hintergründe, indem sie sie mit      ; leeren Tiles füllt. Danach werden neue Tiles definiert, die später mit    ; den Werten für die Buchstaben gefüllt werden sollen.
Make_tiles:
	ldx	#$7400			
	stx	$2116	; Wähle die Adresse 7400h im VRAM aus          ; (Hintergrund 1)
			; Auf diesem Hintergrund wird d. Text angezeigt
	ldx	#$0000	; Initialisiere X als Counter
clearscreen:
	lda	#$00		
	sta	$2118		
	lda	#$01
	sta	$2119	; Lösche den Hintergrund, indem leere Tiles     ; genutzt werden
	inx		; Erhöhe X
	cpx	#$0400	; Prüfe, ob Alle Tiles des Hintergrundes        ; geleert wurden
	bne	clearscreen	; Wenn nicht, springe zu clearscreen
	ldx	#$7800		
	stx	$2116	; Wähle die Adresse 7800h im VRAM aus          ; (Hintergrund 2). Dieser Hintergrund ist nicht ; zu sehen, ausser am rechten Rand, wenn der   ; erste Hintergrund verschoben wird.
	ldx	#$0000	; Initialisiere X als Counter
clearscreen2:
	lda	#$00
	sta	$2118		
	lda	#$01		
	sta	$2119	; Lösche den Hintergrund, indem leere Tiles    ; genutzt werden
	inx		; Erhöhe X
	cpx	#$0400	; Prüfe, ob Alle Tiles des Hintergrundes       ; geleert wurden
	bne	clearscreen2	; Wenn nicht, springe zu clearscreen2

; Dieser Teil initialisiert die Tiles, auf denen später der Text       ; dargestellt wird. Die Tiles haben einen Inhalt, der für das weitere   ; nicht relevant ist, und wie folgt aussieht:
	;    00h   08h   10h ... (32 Spalten Breite)
	;    01h   09h   11h ...
	;    02h   0Ah   12h ...
	;    03h   0Bh   13h ...
	;    04h   0Ch   14h ...
	;    05h   0Dh   15h ...
	;    06h   0Eh   16h ...
	;    07h   0Fh   17h ...
	
	ldx	#$7540		
	stx	$2116	; Wähle die Adresse 7540h im VRAM aus
	ldx	#$0000	; Initialisiere X als Spaltenzähler
drawchar:
	lda	$1000	; Lade den Wert des Zeilenzählers (Erste Zeile, ; erste Spalte) in den Akku
	sta	$1001	; Speichere den Akku zur späteren Verwendung
drawflexpattern:
	lda	$1001	; Lade das aktuelle Byte
	sta	$2118	; Schreibe es in den VRAM
	stz	$2119	; Keine Farbpalette dazu auswählen
	lda	$1001	; Lade das aktuelle Byte nochmal
	clc
	adc	#$08	; Erhöhe das Byte um 8, um die nächste Zeile zu ; erhalten. Die Zählreihenfolge ist vertikal,  ; deswegen müssen 7 Byte übersprungen werden,  ; um bei einer Spaltenhöhe von 8 Byte in       ; dieselbe Zeile der nächsten Spalte zu         ; gelangen
	sta	$1001	; Aktuelles Byte rückspeichern
	inx		; Spaltenzähler erhöhen
	cpx	#$0020	; Prüfe, ob alle 32 Spalten bearbeitet wurden
	bne	drawflexpattern
			; Wenn nicht, springe zu drawflexpattern
	ldx	#$0000	; Setze den Spaltenzähler wieder zurück
	inc	$1000	; Erhöhe den Zeilenzähler um 1
	lda	$1000	; Lade aktuelles Byte (Nächste Zeile, erste    ; Spalte)
	cmp	#$08	; Prüfe, ob alle Zeilen bearbeitet wurden
	bne	drawchar	; Wenn nicht, springe zu drawchar
	rts


; Diese Funktion löscht alle relevanten Bereiche im RAM. Der Bereich       ; 7e8000h - 7e8800h ist für die Grafikdaten des anzuzeigenden Textes,
; der Bereich 7e7000h - 7e7020h speichert die Buchstaben in Rohform.
Clear_ram:
	ldx	#$0000	; Lade X mit null
clearram:
	lda	#$00	; Lade Akku mit null
	sta	$7e8000,x	; Speichere null an der Stelle 7e8000h + X
	inx		; Erhöhe X
	cpx	#$0800	; Prüfe, ob der komplette Bereich geleert wurde
	bne	clearram	; Wenn nicht, springe zu clearram

	ldx	#$0000	; Setze X wieder auf null
clearscrolltext:
	lda	#$20	; Lade den Akku mit 20h (Der Wert steht für ein ; Leerzeichen)
	sta	$7e7000,x	; Schreibe den Wert in die Speicherstelle      ; 7e7000 + X
	inx		; Erhöhe X
	cpx	#$0040	; Prüfe ob alle Werte, in denen Text           ; gespeichert wird, überschrieben wurden
	bne	clearscrolltext	; Wenn nicht, springe zu clearscrolltext
	rts


; Dies sind die Werte, die die Sinuskurve bilden, welcher der Text folgt.
SINE:
 .db  32,32,33,34,35,35,36,37,38,38,39,40,41,41,42,43,44,44,45,46
 .db  46,47,48,48,49,50,50,51,51,52,53,53,54,54,55,55,56,56,57,57
 .db  58,58,59,59,59,60,60,60,61,61,61,61,62,62,62,62,62,63,63,63
 .db  63,63,63,63,63,63,63,63,63,63,63,63,62,62,62,62,62,61,61,61
 .db  61,60,60,60,59,59,59,58,58,57,57,56,56,55,55,54,54,53,53,52
 .db  51,51,50,50,49,48,48,47,46,46,45,44,44,43,42,41,41,40,39,38
 .db  38,37,36,35,35,34,33,32,32,31,30,29,28,28,27,26,25,25,24,23
 .db  22,22,21,20,19,19,18,17,17,16,15,15,14,13,13,12,12,11,10,10
 .db   9, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2
 .db   1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 .db   1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7
 .db   7, 8, 8, 9, 9,10,10,11,12,12,13,13,14,15,15,16,17,17,18,19
 .db  19,20,21,22,22,23,24,25,25,26,27,28,28,29,30,31


; Dies sind die Buchstaben, die später auf dem Bildschirm angezeigt werden. 
; Das Charset wurde mit dem Cyber Font-Editor V1.4 erstellt, das aber      ; leider nicht kostenlos zu bekommen ist.
Charset:
;==========================================================================
; Cyber Font-Editor V1.4  Rel. by Frantic (c) 1991-1992 Sanity Productions 
;==========================================================================
	.db	$0	
	.db	$3c,$42,$99,$a1,$a1,$99,$42,$00	;' '
	.db	$3e,$60,$c6,$fe,$c6,$c6,$66,$00	;'!'
	.db	$fc,$06,$ce,$fc,$c6,$ce,$fc,$00	;'"'
	.db	$3e,$60,$c0,$c0,$c0,$c6,$7c,$00	;'#'
	.db	$fc,$06,$c6,$c6,$cc,$dc,$f0,$00	;'$'
	.db	$fe,$00,$c0,$f8,$c0,$e6,$7c,$00	;'%'
	.db	$fe,$00,$c0,$f8,$c0,$c0,$60,$00	;'&'
	.db	$7c,$e6,$c0,$ce,$c6,$ce,$7c,$00	;'''
	.db	$c6,$06,$c6,$fe,$c6,$c6,$66,$00	;'('
	.db	$fc,$00,$30,$30,$30,$30,$7c,$00	;')'
	.db	$7e,$00,$18,$18,$98,$d8,$70,$00	;'*'
	.db	$c6,$0c,$d8,$f0,$d8,$cc,$46,$00	;'+'
	.db	$c0,$00,$c0,$c0,$c0,$d8,$f6,$00	;','
	.db	$26,$70,$fe,$d6,$d6,$c6,$66,$00	;'-'
	.db	$66,$e0,$f6,$fe,$ce,$c6,$66,$00	;'.'
	.db	$7c,$e6,$c6,$c6,$c6,$ce,$7c,$00	;'/'
	.db	$fc,$06,$c6,$fc,$c0,$c0,$60,$00	;'0'
	.db	$7c,$e6,$c6,$c6,$c6,$ce,$76,$00	;'1'
	.db	$fc,$06,$c6,$fc,$d8,$cc,$66,$00	;'2'
	.db	$7c,$e6,$c0,$7c,$06,$ce,$7c,$00	;'3'
	.db	$fc,$00,$30,$30,$30,$30,$18,$00	;'4'
	.db	$c6,$c0,$c6,$c6,$c6,$6e,$3e,$00	;'5'
	.db	$c6,$c0,$c6,$c6,$66,$36,$1c,$00	;'6'
	.db	$66,$c0,$c6,$d6,$fe,$76,$32,$00	;'7'
	.db	$66,$e0,$7c,$18,$7c,$ee,$66,$00	;'8'
	.db	$c6,$c0,$c6,$6c,$38,$38,$38,$00	;'9'
	.db	$7e,$46,$0c,$18,$30,$66,$7c,$00	;':'
	.db	$00,$00,$00,$00,$00,$00,$00,$00	;';'
	.db	$00,$00,$00,$00,$00,$00,$00,$00	;'<'
	.db	$00,$00,$00,$00,$00,$00,$00,$00	;'='
	.db	$00,$00,$00,$00,$00,$00,$00,$00	;'>'
	.db	$00,$00,$00,$00,$00,$00,$00,$00	;'?'
	.db	$00,$00,$00,$00,$00,$00,$00,$00	;'@'
	.db	$18,$18,$18,$18,$18,$00,$0c,$00	;'A'
	.db	$6c,$6c,$36,$00,$00,$00,$00,$00	;'B'
	.db	$00,$6c,$fe,$6c,$6c,$fe,$6c,$00	;'C'
	.db	$00,$00,$00,$00,$00,$00,$00,$00	;'D'
	.db	$00,$00,$00,$00,$00,$00,$00,$00	;'E'
	.db	$00,$00,$00,$00,$00,$00,$00,$00	;'F'
	.db	$0c,$0c,$18,$00,$00,$00,$00,$00	;'G'
	.db	$30,$60,$60,$c0,$60,$60,$30,$00	;'H'
	.db	$18,$0c,$0c,$06,$0c,$0c,$18,$00	;'I'
	.db	$10,$54,$38,$fe,$38,$54,$10,$00	;'J'
	.db	$00,$10,$10,$7c,$10,$10,$00,$00	;'K'
	.db	$00,$00,$00,$00,$00,$18,$30,$00	;'L'
	.db	$00,$00,$00,$7c,$00,$00,$00,$00	;'M'
	.db	$00,$00,$00,$00,$00,$30,$30,$00	;'N'
	.db	$00,$06,$0c,$18,$30,$60,$c0,$00	;'O'
	.db	$7c,$e6,$c6,$c6,$c6,$ce,$7c,$00	;'P'
	.db	$70,$c0,$30,$30,$30,$30,$38,$00	;'Q'
	.db	$3c,$60,$06,$1c,$30,$66,$7c,$00	;'R'
	.db	$fc,$00,$06,$3c,$06,$c6,$7c,$00	;'S'
	.db	$1c,$20,$6c,$cc,$fe,$0c,$0e,$00	;'T'
	.db	$fe,$00,$c0,$fc,$06,$ce,$7c,$00	;'U'
	.db	$3c,$66,$c0,$fc,$c6,$ce,$7c,$00	;'V'
	.db	$7e,$00,$06,$06,$0c,$0c,$0c,$00	;'W'
	.db	$7c,$e0,$c6,$7c,$c6,$ce,$7c,$00	;'X'
	.db	$7c,$e0,$c6,$7e,$06,$ce,$7c,$00	;'Y'
	.db	$00,$00,$00,$18,$00,$18,$00,$00	;'Z'
	.db	$00,$00,$00,$18,$00,$18,$30,$00	;'['
	.db	$18,$30,$70,$e0,$70,$30,$18,$00	;'\'
	.db	$00,$00,$3c,$00,$3c,$00,$00,$00	;']'
	.db	$30,$18,$1c,$0e,$1c,$18,$30,$00	;'^'
	.db	$7c,$c6,$66,$0c,$18,$00,$18,$00	;'_'

; Dieser Text wird auf dem Bildschirm angezeigt. Es kann jeder beliebige   ; Text angezeigt werden, solange die Zeichen im Charset vorhanden sind, und ; die Länge in der Scrollfumktion entsprechend eingetragen ist.
TEXT:
	.db	"   DIES IST EIN SCROLLENDER TEXT"
	.db 	". UND DAS TOLLSTE IST, ER DREHT "
	
.ends


Großer DANK geht an dieser Stelle an Prof. Dr. Thomas Risse und André Killing, Oliver Hanslian, Roy Norbart die dieses Dokument erstellt haben.
Ich stelle es hier als Mirror zur Verfügung. MfG KillBill_158
Signature from »KillBill_158«



Wer sich entschieden hat, etwas zu tun, und an nichts anderes denkt, überwindet alle Hindernisse.

This post has been edited 4 times, last edit by "KillBill_158" (Jun 20th 2012, 1:57pm)


1 registered user and 1 guest thanked already.

Users who thanked for this post:

CloudMcRip (19.02.2015)

LostTemplar

Far East of Eden Zero

Posts: 59

  • Send private message

3

Wednesday, June 20th 2012, 1:03pm

Quelle: http://www.weblearn.hs-bremen.de/risse/RST/WS07/SNES/

Hast du den Autor um Erlaubnis gebeten?

KillBill_158

Supergenie

  • "KillBill_158" is male
  • "KillBill_158" started this thread

Posts: 1,967

Thanks: 136

  • Send private message

4

Wednesday, June 20th 2012, 1:45pm

Wollte ich noch reinmachen heute eigentlich aber bei uns auf Arbeit geht es heute übel ab. Werde ich mal gleich machen. Danke für den Link kann man immer gebrauchen ;)

EDIT: es ist getan!
Signature from »KillBill_158«



Wer sich entschieden hat, etwas zu tun, und an nichts anderes denkt, überwindet alle Hindernisse.

This post has been edited 1 times, last edit by "KillBill_158" (Jun 20th 2012, 1:51pm)


earth phoenix

Schön vorsichtig

  • "earth phoenix" is male

Posts: 162

Thanks: 3

  • Send private message

5

Wednesday, June 20th 2012, 9:53pm

saucool. kannte ich noch nicht...

MasterPhW

Creme de la Creme

Posts: 142

Thanks: 2

  • Send private message

6

Wednesday, June 20th 2012, 11:19pm

Wollte ich noch reinmachen heute eigentlich aber bei uns auf Arbeit geht es heute übel ab. Werde ich mal gleich machen. Danke für den Link kann man immer gebrauchen ;)

EDIT: es ist getan!

Sollte man meines Erachtens als erstes machen. Also um Erlaubnis fragen. Denn das ist nix, was innerhalb von 5 Minuten zusammengeschrieben wurde und du weißt die Lizenz nicht, unter der es veröffentlicht wurde.
Signature from »MasterPhW«
Netbook: Asus EeePC 1015PEM | Intel Atom Dual Core N550@1,5GHz | Win7 Starter
Main: Intel Core i7 860@3.8Ghz | 8GB DDR3-1333 | ATI XFX HD 5750 | ATI HD Audio | 256GB SSD & 3TB + 4TB external | Win7 Pro X64 SP1
Old: AMD Athlon64 X2 4200+@2.5Ghz | MSI KbT Neo2-F V2 | 2GB Corsair Value | Radeon HD 3850 512MB | Creative SB Audigy LS | 2TB | Win7 Pro X64 SP1


earth phoenix

Schön vorsichtig

  • "earth phoenix" is male

Posts: 162

Thanks: 3

  • Send private message

7

Thursday, June 21st 2012, 7:21am

sieht nach semesterarbeit aus... dumm nur, dass die autoren ihre matrikelnummern mit da draufgeschrieben haben... egal. verlinken darf man die, 1:1 wiedergeben glaube ich weniger... die rechte haben afaik die autoren (wobei sich die unis idr ein nutzungsrecht entstandener software o.ä. zusichern lassen). von daher: am besten die jungs (vorname.nachname@hs-bremen.de) mal freundlich anschreiben. normalerweise sind die glücklich, wenn sie wahrgenommen werden...

KillBill_158

Supergenie

  • "KillBill_158" is male
  • "KillBill_158" started this thread

Posts: 1,967

Thanks: 136

  • Send private message

8

Thursday, June 21st 2012, 7:42am

Jo, dann werde ich mal die Tasten schwingen heute und nachfragen .... dachte halt auch das sie glücklich sind weil ihre Arbeit dadurch mehr Beachtung findet ....
Signature from »KillBill_158«



Wer sich entschieden hat, etwas zu tun, und an nichts anderes denkt, überwindet alle Hindernisse.

SinaP

SNESEdit

  • "SinaP" is male

Posts: 236

Thanks: 30

  • Send private message

9

Thursday, June 21st 2012, 12:27pm

Du kannst es auch ganz rausschmeißen und neu Scheiben ... als Author mit verweisen auf die Quellen.

Das sieht dann schon professioneller aus als nur raus und rein Kopieren.
Die meisten die das Lesen (jetztiges aussehen) gefällt es bestimmt weil es 'tolle Diagrame und Bilder' hat. Ansonsten werden sie wohl nicht viel Achnung davon haben.

Die Assembler sachen findest Du auch so von freien Authoren mit passendem (selbst geschriebenen) Assembler. Das wird wohl eher das sein was die meisten Wissen wollen.

KillBill_158

Supergenie

  • "KillBill_158" is male
  • "KillBill_158" started this thread

Posts: 1,967

Thanks: 136

  • Send private message

10

Thursday, June 21st 2012, 1:13pm

Ich hab nicht nur raus und rein Kopiert teils hab ich schon drin rum geschrieben auch die Bilder sind alle von mir neu erzeugt worden ... das mit den freien Autoren muss ich noch mal schauen ;) Danke für den Tipp
Signature from »KillBill_158«



Wer sich entschieden hat, etwas zu tun, und an nichts anderes denkt, überwindet alle Hindernisse.

LostTemplar

Far East of Eden Zero

Posts: 59

  • Send private message

11

Thursday, June 21st 2012, 1:36pm

Es geht halt um's Moralische - wo kein Kläger, da kein Richter, und ich denke nicht, dass hier jemand etwas unternähme - aber ROM Hacking an sich ist ja schon in der Grauzone, wenn man das so sagen kann, im akademischen Bereich jedoch (wozu dieses Dokument ja eigentlich zählt) ist Plagiarismus fast schon eine Todsünde (man denke nur an Guttenberg).

Ich fände es im Übrigen ohnehin mal eine coole Idee, wenn man eine Einführung in Computerarchitektur mit dem SNES (und/oder anderen) Konsolen aufziehen würde. Zumindest die Grundlagen stimmen ja selbst heute noch mit den modernen Maschinen überein.

Rate this thread