Hilfe bei grundsätzliche Herangehensweisen des ASM-Codens

Hier könnt ihr Euch mit Gleichgesinnten austauschen bei der Entwicklung eines neuen Spiels oder eines neuen Stücks Hardware... oder einfach nur über Coding und Spieleentwicklung allgemein plaudern.

Moderatoren: ikari_01, d4s

snoer
SNES-Programmer
SNES-Programmer
Beiträge: 249
Registriert: 27. Januar 2013, 13:10
+Positive Tradingpoints+: 5 von 5

Hilfe bei grundsätzliche Herangehensweisen des ASM-Codens

Beitrag von snoer » 31. Januar 2013, 01:06

Beweggründe für diesen Thread
versteckter Inhalt:
Ich habe diesen Thread erstellt, weil mir immer mal wieder Gedanken dazu kommen wie ich Aufgaben programmiertechnisch sinnvoll bewältigen kann, und über die ich mich einfach hin und wieder austauschen muss, weil ich nicht beurteilen kann, ob die Gedanken totaler Irrsinn oder genau richtig sind. Mir fehlt einfach die Programmiererfahrung, und manchmal muss man das Rad ja nicht neu erfinden und andere haben sich diese oder ähnliche Fragen schon lange beantwortet.
Interne Fragestellung #1
Modularisierung und Bibliotheken

Ich frage mich wie man es schaffen kann, den Arbeitsaufwand den man beim asm coden ja zwangsläufig hat, zu reduzieren.
Dabei kommt mir der Begriff Modularisierung in den Kopf.
Ist es nicht an und für sich sinnvoll sich Bibliotheken für alle "common tasks" zu schreiben?
hmm ich meine..
es sollte doch so laufen, dass ich möglichst viel Code den ich schreibe für andere Spiele wiederverwenden kann, also macht es nicht Sinn sich quasi Bibliotheken für irgendwie alles Mögliche anzulegen?

Ich denke da an so grundlegende Dinge wie eine compare funktion..
(obsolet)wirf 2 argumente rein, vergleich sie und gib mir einen return wert..
EDIT: 65816 Reference -> CPX

oder schon sowas wie, lade den Wert aus Register X in Register Y..
oder.. ja.. oderOderRerReRrRrRRrrrr

Und.. wäre das ganze am ende weniger Perfomant?
Im Grunde genommen sowas wie ein Framework.. oder.. eine art Scriptsprache
..oder sollte ich einfach mal als aller erstes das Manual vom 65816 durchlesen,
weil es das alles schon gibt? oder spricht irgendwie irgendwas sonst dagegen?
wer jetzt nur denkt :bang: der sei gebeten den spoiler ganz oben zu lesen,
in dem ich kurz beschrieben habe, welche Funktion dieser Thread für mich haben soll :]

Benutzeravatar
lytron
Code Bro
Code Bro
Beiträge: 2664
Registriert: 12. August 2012, 20:18
+Positive Tradingpoints+: 12 von 12
Wohnort: ハノーファー区
Kontaktdaten:

Re: Hilfe bei grundsätzliche Herangehensweisen des ASM-Coden

Beitrag von lytron » 31. Januar 2013, 06:46

Für komplexere Sachen ist es unerlässlich, wiederkehrende Elemente auszulagern (in Subroutinen; da ich weiß, dass du wie ich mit bazz' Tutorials aufgewachsen bist, empfehle ich dir, möglichst schnell dich von bazz' Makros zu trennen, speziell diesem DMA-Makro).

Allerdings sind diese Auslagerungen etwas größerer Art als dein Beispiel; für einen Vergleich braucht man eigentlich nur zwei Zeilen (LDA Werteins, CMP Wertzwei), da macht diese Auslagerung nur wenig Sinn; eher Sinn mächte es, wenn du etwas schröbest wie: Ich speichere die Ursprungsadressen von einer Tilemap und zugehörigem Tileset in diese Register, und die nachfolgende Subroutine macht automatisch einen DMA-Transfer in den VRAM. Oder man vereinfacht sich so die Kommunikation mit dem Soundchip.

Ich habe eigentlich auch daran gedacht, meinen Code möglichst so zu schreiben, dass er auseinandergenommen werden und anderweitig eingesetzt werden kann. Das mittelprächtige Ergebnis sieht man bei meinem Textbox-Projekt. An dieses Ding wage ich mich seit einem guten Vierteljahr nicht ran, um es in ein anderes Projekt zu implementieren, weil es so kacke einzubinden ist. ;)
Bisher habe ich immer alles lieber noch einmal neu geschrieben, aber aus den einfachen Gründen, weil ich es einfacher fand, neu anzufangen, als mich in Altes, Verworrenes reinzufuchsen, um zu gucken, was ich entnehmen muss, damit es läuft. Deshalb wäre es sicherlich vernünftig, wenn man eine Engine fertig hat, die fürs Recycling zwischenzuspeichern, bevor man sie mit Inhalt füllt. ;)
pantalytron: ルトロンはくそのディスアセンブラだよ!
Perikles hat geschrieben:Man muss sich das mal reinziehen: die Idee ist scheiße, die theoretische Ausarbeitung ist scheiße, die praktische Umsetzung ist scheiße und der so entstehende Anspruch noch beschissener.

snoer
SNES-Programmer
SNES-Programmer
Beiträge: 249
Registriert: 27. Januar 2013, 13:10
+Positive Tradingpoints+: 5 von 5

Re: Hilfe bei grundsätzliche Herangehensweisen des ASM-Coden

Beitrag von snoer » 31. Januar 2013, 15:09

leider fehlt mir, trotz grundlegender Kenntnisse, Programmiererfahrung auch in den höheren Sprachen, aber die kriegen es ja auch hin sich libs zu schreiben.

In meiner naiven Vorstellung würde man, um beim Beispiel deiner Textbox zu bleiben, das ganze ja so einrichten, dass man einfache Funktionen zur verfügung stellt und die dann ausreichend dokumentiert, sodass man das Modul garnicht mehr anfassen muss.
import und fertig. dannach benutzt man nurnoch die mitgelieferte funktionalität

sodass ich, wenn ich eine Textbox haben will sowas schreibe wie

Code: Alles auswählen

libTB_getTextBox positionType, height, width, pointerAdressOfText
die lib übernimmt dann den gesammten Rest.
Sie hätte quasi ihr eigenes upload to vram Geraffel.
Man bräuchte dann wohl noch ne weitere Funktion über die sich das vram Geraffel "kalibrieren" ließe.

..ja das sind so Gedanken die ich dazu habe.
Ich frage mich nur ob es am Ende performant ist in eine ASM Sprache solche Funtkionalität zu implementieren, oder ob es das Programm unnötig aufbläst. Wobei ich denke, dass die Größe des Programms heutzutage eh keine große Rolle mehr spielt, oder lieg ich da falsch?


Genauso träume ich ja von sowas wie einem ascii interpreter, damit das mit dem ganzen Text nicht so dermaßen umständlich ist.. (wobei man da eventuell sinnvoller einen parser in python oderso schreibt)
Ich meine..

Es muss doch möglich sein, dass man seine Dialog texte (so ganz sexy) in json oderso ablegt, und die datei dann wiederum unter zuhilfename einer Funktion direkt in sein spiel einbindet. so..

Code: Alles auswählen

libASCII_import dialoge/meineDialoge.txt, offsetInRomToStoreTheData
libTB_getTextBox positionType, height, width, offsetInRomToStoreTheData
..wobei offsetInRomToStoreTheData ein speicherbereich auf dem rom wäre und libASCII beim importieren der Textdatei den Text natürlich wunderbar komprimiert, undzwar genau so,
dass libTBA_getTextBox mit dem "Datei Format" was anfangen kann..


so.. jetzt hab ich mir genug warme Gedanken gemacht.
Ich weiss halt in Wirklichkeit nicht, ob das was ich mir da so ausdenke auch nur im Ansatz realistisch ist.. aber was solls :) Spaß machts jedenfalls sich sowas auszudenken.
Was meinst du dazu? oder Sonstjemand der das beurteilen kann?

EDIT:
Am ende frage ich mich grad.. hmm ..wenn dieses ganze processing vom SNES überommen werden muss, dann entstehen wahrscheinlich sowas wie Ladezeiten..
Um das zu umgehen, wäre es vielleicht möglich, das ganze doch als eine art Python script zu machen, das am ende eine .inc ausspuckt in der der ganze text komprimiert drin liegt.
im grunde genommen sowas wie pcx2snes bloß für textdateien.. da muss ich noch ein bisschen vor mich hin rubberducken ehe ich mir eine Meinung dazu gebildet hab was von all dem geschriebenen schlau ist.

Benutzeravatar
lytron
Code Bro
Code Bro
Beiträge: 2664
Registriert: 12. August 2012, 20:18
+Positive Tradingpoints+: 12 von 12
Wohnort: ハノーファー区
Kontaktdaten:

Re: Hilfe bei grundsätzliche Herangehensweisen des ASM-Coden

Beitrag von lytron » 31. Januar 2013, 16:11

Grundsätzlich war mein Textbox-Programm auch genau für eine solche Implementierung gedacht, und viele Funktionen funktionierten bereits so, wie du es hier beschrieben hast.

Für den VRAM-Transfer z. B. könnte man wiederum eine Subroutine schreiben, die von der Textbox-Subroutine aufgerufen wird, das ließe sich ja zum Glück auf einige Ebenen verschachteln (soweit, wie es der Platz im Stack für die Rücksprung-Adressen zulässt).

Wegen der ASCII-Zeichen: Genau soetwas hatte ich mal in einer ROM gefunden, die ich disassembled hatte (Cobra Girls).

Der Trick dahinter ist sehr einfach:
Man schreibt die Zeichenkette als ASCII-Zeichen, und die Tiles sind entsprechend dem ASCII-Zeichensatz angepasst, also Tile Nummer $41 ist ein großes A, da der ASCII-Code dafür ebenfalls ein $41 ist usw.

Dann könntest du in deiner ROM schreiben:

Code: Alles auswählen

Textdata:
.DB "Hello World!"
und er würde das entsprechend übernehmen. Bei meinem Textbox-Dingens geht das nicht/nicht ohne größere Ummodelleien. U. a. da die Schrittweite zwischen zwei Buchstaben 2 und nicht 1 ist (weil ein Buchstabe aus zwei Tiles besteht und nicht einem).
Allerdings gibbet ja für mein Textbox-Geschreibsel ein Minitool zum Umwandeln. ;)

Und, was du indirekt schon angesprochen hast:

Ob am Ende eher der Prozessor oder der Datenspeicher zu schonen ist, weiß ich selbst nicht. Ich schätze, da beides zu schnell an seine Grenzen kommt (solange man nicht die MSU benutzt, aber die güldet eh nich), ist das immer zusammenhangsbezogen, was man eher schonen sollte. Ich glaube allerdings, dass ich grundsätzlich dazu neige, die Kapazität des Prozessors zu unterschätzen. ;)
pantalytron: ルトロンはくそのディスアセンブラだよ!
Perikles hat geschrieben:Man muss sich das mal reinziehen: die Idee ist scheiße, die theoretische Ausarbeitung ist scheiße, die praktische Umsetzung ist scheiße und der so entstehende Anspruch noch beschissener.

d4s
Moderator
Moderator
Beiträge: 345
Registriert: 13. Juni 2006, 16:11
+Positive Tradingpoints+: 1 von 1
Kontaktdaten:

Re: Hilfe bei grundsätzliche Herangehensweisen des ASM-Coden

Beitrag von d4s » 31. Januar 2013, 22:14

Ich werde mal so grundsätzlich wie möglich antworten.

Eine fundamentale Sache, die so selten explizit erwähnt wird, ich aber für essentiell halte, insbesondere, wenn man von höheren Sprachen kommt:

Es gibt kaum eine Art, unsicherer als in Assembler auf einem System wie dem SNES zu programmieren.

In Assembler zu programmieren bedeutet, untyped und unchecked zu programmieren.
Im Fall des SNES kommt noch hinzu, dass die CPU-Architektur nicht mal Memory Protection bietet.
Das bedeutet, dass es absolut kein Abfangen und keine Behandlung von irgendeiner Art von Programmierfehlern gibt.

Ein Resultat ist, dass viele Fehler unentdeckt bleiben und das Debuggen der gefundenen Fehler ungleich schwieriger ist als in anderen Sprachen.
Ein anderes Resultat ist, dass es mit steigender Programmkomplexität ungewöhnlich schnell sehr einfach wird, sich "in eine Ecke" zu programmieren:
Monolithische Programmblöcke, die schwer zu warten, erweitern und auf andere Projekte zu portieren sind und lauter versteckte Nebenwirkungen haben, sind eher die Regel als die Ausnahme, wenn man nicht von Anfang an mit Bedacht vorgeht.

Dieser Gefahr sind sich die meisten Anfänger nicht bewusst und das ist auch mit ein Grund dafür, warum es im Homebrew-Bereich zwar einige 1-Level Demos oder Spiele auf niedrigem NES-Niveau gibt, aber wenig, was an Komplexität darüber hinausgeht.
Dort liegt ungefähr die Grenze, zu der man mit mehr oder wenigem planlosem Hacken ohne größere Probleme hinkommt.
Viele (ich selbst anfangs eingeschlossen) überschätzen sich nach ersten Erfolgen, bauen eine kleine Demo zusammen, und wenn es dann daran geht, die Teile zu einem größeren Ganzen zusammenzubauen oder weitere Features hinzuzufügen, folgt Hack auf Hack.
Danach Hacks, um um die bestehenden Hacks herumzuarbeiten.
Irgendwann macht sich dann Ernüchterung breit.
Theoretisch kann man weitermachen (bei vielen veröffentlichten, offiziellen Spielen scheint das der Fall gewesen zu sein), aber man kämpft mehr und mehr gegen den bestehenden eigenen Programmcode, als mit ihm zu arbeiten.
An dieser Stelle hören die meisten auf.


Was mir sehr geholfen hat, war ein geplantes und strukturiertes Grundgerüst:
-Runtime-Fehlerbehandlung für alle Probleme, die ich erkennen (oder vermuten) kann (Stack under/overflow, memory corruption, unerwartete Interrupts, Gültigkeitsbereiche von Funktionsparametern, dynamische Sprungziele etc.).
-kleines Objektsystem, das die Gruppierung von Funktionen und Daten in logische, abgetrennte Blöcke ermöglicht, mir die Erzeugung und Verwaltung von Objekten während der Laufzeit abnimmt und standardisierte Aufrufregeln hat.
-dynamische Verwaltung von Ressourcen (DMA-Kanäle, Video-RAM, Farbpaletten etc.).
-Unit-Tests (auch, wenn dort oft die Faulheit siegt und ich lange nicht so viele schreibe, wie ich sollte)

Auf dieser Basis fällt es mir verhältnismässig leicht, flexibel und modular meine Codebasis zu vergrößern und Projekte zu skalieren, ohne dass sie unübersichtlich/unwartbar werden.
Nachteilig ist der zusätzliche Overhead des dynamischen Runtime-Krams, die Vorteile machen das für mich aber deutlich wieder wett, und ganz am Ende eines Projektes kann man immer noch mit statischen Hacks die Performance optimieren, andersrum geht das dagegen nur sehr schlecht.

Verglichen mit modernen High Level Frameworks ist meine Codebasis natürlich immer noch Low-Level-Gehacke, durch das wahrscheinlich kaum jemand ausser mir durchsteigt, aber es ist ein Schritt in die richtige Richtung.
snoer hat geschrieben:leider fehlt mir, trotz grundlegender Kenntnisse, Programmiererfahrung auch in den höheren Sprachen, aber die kriegen es ja auch hin sich libs zu schreiben.
Neben dem bereits genannten Problem des Fehlens jeglicher grundsätzlichen Struktur, Calling Conventions, Memory Management, standardisierter Typen etc. ist ein großes Problem, dass die SNES CPU relativ langsam ist.
Jedes Stück Flexiblität und Modularität geht i.d.R. auch mit Performance-Verlust einher.

Klar kann man ein generelles Framework zum Managen von animierten Sprites, Kollisionsabfrage usw. schreiben.
Spätestens wenn man aber ein Shmup schreibt und mehr als 3 Schüsse gleichzeitig auf dem Bildschirm haben will, wird man aber z.B. kaum um einen extrem spezialisierten und auf das jeweilige Spiel zugeschnittenen Bullet-Manager herumkommen.

Die bestehenden Libraries beschränken sich daher eher auf allgemeine Systemfunktionalität.
Als Beispiel kann man pvsneslib nennen, ein bisschen mehr Kram gibts hier.

snoer hat geschrieben: Ich frage mich nur ob es am Ende performant ist in eine ASM Sprache solche Funtkionalität zu implementieren, oder ob es das Programm unnötig aufbläst. Wobei ich denke, dass die Größe des Programms heutzutage eh keine große Rolle mehr spielt, oder lieg ich da falsch?

Genauso träume ich ja von sowas wie einem ascii interpreter, damit das mit dem ganzen Text nicht so dermaßen umständlich ist.. (wobei man da eventuell sinnvoller einen parser in python oderso schreibt)
Ich denke, ROM-Größe ist heute praktisch irrelevant.
Textparser, wie auch sonstige Conversion Tools für Grafiken, Sound, Level etc. sind natürlich ausgesprochen sinnvoll, oft unumgänglich.

Ich persönlich hacke mir meine sämtlichen Hilfsprogramme für solche Zwecke, die entweder direkt Binärdaten, oder inkludierbare Asm-Sourcefiles ausspucken (je nach Einsatzbereich) auch alle in python zusammen und bin damit bisher sehr gut gefahren.
In meinem letzten veröffentlichen Projekt (Super Road Blaster) sind die Textstrings aufgrund der geringen Menge noch händisch geschriebene asm-includes, in meiner aktuellen Codebasis hab ich aber bereits einen Textparser mit multi-language-support und so.
snoer hat geschrieben: Am ende frage ich mich grad.. hmm ..wenn dieses ganze processing vom SNES überommen werden muss, dann entstehen wahrscheinlich sowas wie Ladezeiten..
Grundsätzlich muss man natürlich sagen: Berechne so viel du kannst vor und include es statisch, anstatt es vom SNES zur Laufzeit berechnen zu lassen.
Ladezeiten haben lustigerweise viele SNES-Spiele, sie dauern aber selten länger als ein oder zwei Sekunden und lassen sich deswegen noch ganz gut kaschieren, sodass die meisten Leute sie nicht mal bemerken.
Klassisches Beispiel: Der schwarze "Mario start!"-Bildschirm vor jedem Level in Super Mario World. ;)
Ich persönlich verzichte immer auf Kompression, wenn es nicht unbedingt notwendig ist, weil ROM-Größe heutzutage wie gesagt praktisch irrelevant ist.

snoer
SNES-Programmer
SNES-Programmer
Beiträge: 249
Registriert: 27. Januar 2013, 13:10
+Positive Tradingpoints+: 5 von 5

Re: Hilfe bei grundsätzliche Herangehensweisen des ASM-Coden

Beitrag von snoer » 1. Februar 2013, 20:45

-Runtime-Fehlerbehandlung für alle Probleme, die ich erkennen (oder vermuten) kann (Stack under/overflow, memory corruption, unerwartete Interrupts, Gültigkeitsbereiche von Funktionsparametern, dynamische Sprungziele etc.).
-kleines Objektsystem, das die Gruppierung von Funktionen und Daten in logische, abgetrennte Blöcke ermöglicht, mir die Erzeugung und Verwaltung von Objekten während der Laufzeit abnimmt und standardisierte Aufrufregeln hat.
-dynamische Verwaltung von Ressourcen (DMA-Kanäle, Video-RAM, Farbpaletten etc.).
-Unit-Tests (auch, wenn dort oft die Faulheit siegt und ich lange nicht so viele schreibe, wie ich sollte)
danke vor allem für dies, abgesehen davon, für die ganze antwort als solche.
sehr hilfreich!

Benutzeravatar
Farbauti
SNES-Programmer
SNES-Programmer
Beiträge: 128
Registriert: 14. August 2011, 23:00
+Positive Tradingpoints+: 0 von 0
Wohnort: Berlin

Re: Hilfe bei grundsätzliche Herangehensweisen des ASM-Coden

Beitrag von Farbauti » 3. Februar 2013, 00:53

Ich denke die Notwendigkeit von Libs beim Snes (Konsolen generell) ist eher zweifelhaft.
Klar solche Sachen wie Tasteabfrage oder andere solchen Lowlevel Tasks kann man auslagern. Die großen Sachen
wie das rendern des Bildes, Animationen, Kollisionsabfrage etc. wurden aber warscheinlich nicht in Libs ausgelagert weil sich die einzelnen Spiele dafür zu stark unterscheiden.
Versteh mich jetzt aber nicht falsch, nur weil man diese Sachen nicht für andere Spiele nutzen kann/will sollte man sich trotzdem die Mühe machen den Quelltext logisch zu trennen.
Eine Datei die deinen ganzen Source enthält wäre wohl unmöglich zu warten oder zu bugfixen.

Generell sollte man meiner Meinung nach bei der Programmierung, im Allgemeinen, auf bestimmte Vorgehensweisen, sogenannte Best practice, vetrauen.
Ich kann dir für sowas die Seite http://www.clean-code-developer.de/ empfehlen. Du wirst definitiv nicht alles von dort umsetzen können/müssen aber einige der Sachen kannst du definitv auch beim asm Programmieren anwenden.

@d4s:
Kannst du mir mal bitte erklären wie du deineUnit-Test durchführst?
Ist das einfach nur eine ROM in der du deine Funktionen testest oder wie ist da deine herrangehensweise?

d4s
Moderator
Moderator
Beiträge: 345
Registriert: 13. Juni 2006, 16:11
+Positive Tradingpoints+: 1 von 1
Kontaktdaten:

Re: Hilfe bei grundsätzliche Herangehensweisen des ASM-Coden

Beitrag von d4s » 8. Februar 2013, 18:01

Farbauti hat geschrieben: @d4s:
Kannst du mir mal bitte erklären wie du deineUnit-Test durchführst?
Ist das einfach nur eine ROM in der du deine Funktionen testest oder wie ist da deine herrangehensweise?
Die Tests sind in einem Code-Modul im ROM zusammengefasst und werden, solange ich das Spiel entwickle, bei jedem Start des ROMs nach Initialisierung der Hardware komplett durchlaufen.
Schlägt ein Test fehl(weil die Rückgabeparameter der aufgerufenen Funktion nicht den Erwartungen entsprechen), wird mit Fehlermeldung abgebrochen und die Nummer des fehlgeschlagenen Tests angezeigt.

Grob gesagt also etwas so simples wie eine Reihe von Funktionsaufrufen in vom Gesamtsystem abkoppelbaren Systemmodulen zum Systemstart, bei denen die Rückgabeparameter der Aufrufe mit erwarteten Ergebnissen vergleichen werden.
Wahrscheinlich weniger schön, als man das von aktuellen Systemen kennt, aber die Idee ist identisch.

Ein Beispiel für ein zu testendes Code-Modul ist das Allocation Management für den Videospeicher.
Nach Inintialisierung werden eine Reihe von verschiedenen grossen Speicherblöcken allocated und deallocated, unter anderem wird auch versucht, mehr Speicher zu allocaten als physisch möglich.
Nach jedem dieser Testschritte werden die Rückgabewerte der zu testenden Funktion des Allocation Managers überprüft (Hier: allocation id, Startadresse des alloziierten Speicherblocks, Errorflag).

Primärer Nutzen für mich ist, dass beim Entwickeln Regressionen vor allen Dingen im Kernsystem potentiell schneller auffallen können.

Benutzeravatar
ikari_01
snesfreaks.com-Team
snesfreaks.com-Team
Beiträge: 441
Registriert: 15. Juni 2010, 23:19
+Positive Tradingpoints+: 6 von 6
Wohnort: Wunstorf

Re: Hilfe bei grundsätzliche Herangehensweisen des ASM-Coden

Beitrag von ikari_01 » 18. Februar 2015, 17:50

*thread aus dem Grab zieh*
Wie verhinderst du dabei eigentlich Fragmentierung? Verschiebst du den Rest VRAM-Inhalt, wenn etwas freigegeben wird, wodurch ein "Loch" entstünde, und passt alle Referenzen an? Sonst kann es ja ziemlich schnell einen Schweizer Käse geben und man kriegt nichts mehr allokiert. :ugly:
sd2snes news: https://sd2snes.de

Antworten