Corti
14.04.2008, 07:57
Flexible Speicherverwaltung im RPG-Maker / Stackprinzip
Frage: Worum gehts in diesem Topic?
Um die Übernahme/Anwendung des Stack-Pinzips in der Speicherverwaltung auf den RPG-Maker.
In einigen Aspekten ( Scriptcodestruktur & Speicheradressierung) hat der Maker starke Ähnlichkeiten mit Assembler,
so wird z.B. im Maker eine Varibale über ihre Nummer im Variablenblock angesprochen, ähnlich der direkten Speicheradressierung
im RAM-Baustein eines Microcontrollers.
Speicherverwaltung? Stackprinzip? Wozu?
Jede in irgend einem Script des Makers verwendete Variable benötigt einen Speicherplatz. Aufgrund der Architektur des Varibalenblocks
besetzen die Variablen einer Hilfsfunktion, die zB den Goldstand im Spiel als Pictures anzeigt, in diesem Fall die Varibalen zur Zerlegung in Einer-, Zehner- Hunderterstellen auch dann Speicherplatz wenn sie nicht benötigt werden.
Die hier genannten Hilfsvariablen werden nur zur Anzeige des Zahlenpictures benötigt, danach braucht man sie nicht mehr und bei der nächsten Verwendung des Call Events werden sie sowieso neu errechnet.
Warum also nicht die Variablen "freigeben" solange sie nicht benötigt werden, oder zumindest den Platz im Variablenblock mehrfach nutzen können.
Warum nicht einfach alle Hilfsvariablen an den selben Ort legen?
Stichworte: rekursiver Aufruf, Verschachtelung
Was machen wir in diesem Topic?
Ich demonstriere das Stackprinzip anhand eines simplen Beispiels, das in diesem Form in der Praxis, Sprich im Maker wunderbar funktioniert.
Das Beispiel ist simpel gehalten um verständlich zu sein. Wer es verstanden hat wird es auch für seine zwecke nutzen können.
Es ist keine "Nachklicken für Kinder" Anleitung, die Inhalte richten sich an Fortgeschrittene.
Das Beispielproject:
Wir haben eine Map & ein Männchen.
Wenn wir das Männchen ansprechen bekommen wir die Aufforderung nacheinander 2 Zahlen und eine Rechenoperation einzugeben.
Die Eingabe soll über ein CommonEvent erfolgen, dass vom Eventscript des Männchen aufgerufen wird.
Die Verarbeitung der 2 Zahlen und der Operation erfolgt in einem 2. CommonEvent.
Hier mal als Blockdiagramm:
http://www.imgimg.de/uploads/Fensterblock53ef1ab3jpg.jpg
Vorbereitung:
Wir reservieren 3 Variablen.
SP - der Stackpointer
SP_H - ein Hilfspointer, nötig weil der Maker keine PointerOffsets verrechnen kann
Tempsave - Zwischenspeicher, entspricht dem Akkumulator einer CPU
Was ist ein Stack/Stackpointer?
Der Stack bezeichnet einen Speicherbereich in dem Speicherplatz vergeben werden kann.
Der Stackpointer ist ein Zeiger auf die unterste Speicheradresse die besetzt ist, man kann also davon ausgehen, dass man den auf dem Platz unterhalb des Stackpointers befindlichen Speicher verwenden kann ohne etwas zu überschreiben.
Der Stackpointer muss zu anfang natürlich erst einmal positioniert werden.
http://www.imgimg.de/uploads/Sp0165f6a11cjpg.jpg
So sieht das dann im Variablenblock des Makers aus.
Das Script:
Dies ist der Scriptcodes des Rechenscripts. Ich habe die Abschnitte & Zeilen nummeriert und werden sie weiter unten kommentieren und mit Speicherabbildern illustrieren.
Wenn ich mich weiter unten auf den Scriptcode beziehe meine ich diese Grafik.
http://www.imgimg.de/uploads/Bild152b85b0e4jpg.jpg
Abschnitt 1:
Dieser Abschnitt besteht aus 3 identischen Einzelschritten.
//Einlesen Zahl1:
Der Stackpointer wird um 1 Feld nach unten verschoben indem man ihn +1 addiert.
Damit zeigt er auf einen leeren Variablenplatz.
Wir lesen die erste Zahl in den Zwischenspeicher ein und verschieben sie an die Stelle
auf die der Stackpointer gerade zeigt.
Diese Stelle ist nun der Speicherort für "Zahl 1" mit der wir nachher rechnen werden.
Dasselbe passiert mit den Eingaben für die Operation (1=Addieren , 2 & Rest = subtrahieren ) und die 2. Zahl.
Nachdem dies passiert ist bekommen wir folgendes Speicherabbild:
Unterhalb des Ursprungsortes des Stackpointers wurde Platz gemacht für unsere 3 Varibalen. Am Ende der Operation zeigt der Stackpointer
auf den letzten belegten Variablenplatz.
http://www.imgimg.de/uploads/Sp0249f4e5dfjpg.jpg
Abschnitt 2:
Nach dem Einlesen der 3 Variablen können diese nun verarbeitet werden, betrachten wir dazu also einmal das Common Event zur Verrechnung.
v_Rechnen(Z1,Z2,OP)
Ich benutze das Common Event hier wie eine Funktion in Assembler.
Die Funtion benötigt 3 Parameter und gibt einen Wert zurück, die Parameterübergabe und der Rückgabewert müssen allerdings per Hand" gemacht werden.
Ein Funktionsaufruf wird gemacht indem man oben auf dem Stack Platz für den Rückgabewert schafft und die Paramater direkt danach anordnet.
Das ist zwar keine "richtige" Parameterübergabe, aber irgendwie doch, denn innerhalb des aufgerufenen Commen Events kann man dann mit Sicherheit davon ausgehen, dass sich die benötigten Parameter auf bestimmten Plätzen relativ zur Position des Stackpointers befinden.
Die Compiler jeder Hochsprache wie C++ verfahren genau so wenn sie den C-Code in Assembler übersetzen.
Reservieren des Rückgabewertes:
http://www.imgimg.de/uploads/Sp03c715d96fjpg.jpg
Der Platz für den Rückgabewert wird reserviert.
Abschnitt 3:
Anschliessend werden die Inhalte der Parameter gelesen und in richtiger Reihenfolge auf dem Stack positioniert.
http://www.imgimg.de/uploads/Sp041c661980jpg.jpg
Warum kopiert man die Variablen nochmal? Die stehen doch direkt davor schon einmal!
Das mag sein, man kann auch die Funktion so umschreiben dass es mit dem schon positionierten Daten klappt, aber nicht immer liegen die Parameter in der selben Reihenfolge vor. Um also sicher zu gehen, dass egal in welchem Kontext das CE aufgerufen wird immer die parameter richtig übernommen, kopiert man sie noch einmal neu auf den Stack.
Abschnitt 4: CallEvent Rechnen!
Das CE wird aufgerufen. Innerhalb des "Rechnen"-CE wird der Stackpointer nochmals um einen Platz nach unten verschoben um Platz für das Rechenergebnis zu schaffen. Anschliessend wird die Rechenoperation durchgeführt, hierzu werden die Parameter über den Stackhilfspointer (SP_H) relativ zum Stackpointer angesprochen.
http://www.imgimg.de/uploads/Sp05e7092bcejpg.jpg
So sieht es dann im Speicher aus.
Abschnitt 5: Wert zurückgeben, Spuren beseitigen
Nachdem die Rechnung nun ausgeführt wurde speichert man den Inhalt des Speichrplatzes auf den Stackpointer gerade zeigt an den Ort für den Rückgabewert, in diesem Fall 4 Plätze oberhalb des Stackpointers, den wir durch Rechnen mit dem Hilfspointer erreichen.
Anschliessend wird der Stackpointer um 4 reduziert.
Damit geben wir den Speicherplatz der 3 Parameter und des Ergebnisses wieder frei
Das Commen Event "Rechnen" endet hier~
http://www.imgimg.de/uploads/Sp06ca110cf8jpg.jpg
Abschnitt 6: Ausgabe & Speicher freiräumen.
Nun da alles getan ist fehlt noch die Ausgabe in einer Textbox.
Dazu wird der Inhalt des Feldes auf das SP zeigt in Tempsave kopiert und dieses dann ausgegeben.
Anschliessend wird SP um 4 verringert, damit zeigt der Stackpointer wieder auf den Ursprungswert
http://www.imgimg.de/uploads/Sp08007bd855jpg.jpg
Hurray~ flexible Speichervergabe =D
Fazit:
Vorteile:
- Rekursive Aufrufe möglich
- weniger gleichzeitiger Speicherbedarf,effizientere Nutzung
Nachteile:
- Parallele Prozesse & Stack verträgt sich nicht da man innerhalb des Makerscript keine Interrupts besitzt, keine Rücksprungpunkte etc.
- Debugging wird schwieriger da man kein bleibendes Speicherabbild mehr hat und die Benennung der Variablen zwecks Übersicht fehlt, je nach Verzweigungstiefe der Aufrufe kanns schwer werden die Daten im F9-Modus überhaupt zu finden.
Das wars soweit
mfg~ der Corti
Frage: Worum gehts in diesem Topic?
Um die Übernahme/Anwendung des Stack-Pinzips in der Speicherverwaltung auf den RPG-Maker.
In einigen Aspekten ( Scriptcodestruktur & Speicheradressierung) hat der Maker starke Ähnlichkeiten mit Assembler,
so wird z.B. im Maker eine Varibale über ihre Nummer im Variablenblock angesprochen, ähnlich der direkten Speicheradressierung
im RAM-Baustein eines Microcontrollers.
Speicherverwaltung? Stackprinzip? Wozu?
Jede in irgend einem Script des Makers verwendete Variable benötigt einen Speicherplatz. Aufgrund der Architektur des Varibalenblocks
besetzen die Variablen einer Hilfsfunktion, die zB den Goldstand im Spiel als Pictures anzeigt, in diesem Fall die Varibalen zur Zerlegung in Einer-, Zehner- Hunderterstellen auch dann Speicherplatz wenn sie nicht benötigt werden.
Die hier genannten Hilfsvariablen werden nur zur Anzeige des Zahlenpictures benötigt, danach braucht man sie nicht mehr und bei der nächsten Verwendung des Call Events werden sie sowieso neu errechnet.
Warum also nicht die Variablen "freigeben" solange sie nicht benötigt werden, oder zumindest den Platz im Variablenblock mehrfach nutzen können.
Warum nicht einfach alle Hilfsvariablen an den selben Ort legen?
Stichworte: rekursiver Aufruf, Verschachtelung
Was machen wir in diesem Topic?
Ich demonstriere das Stackprinzip anhand eines simplen Beispiels, das in diesem Form in der Praxis, Sprich im Maker wunderbar funktioniert.
Das Beispiel ist simpel gehalten um verständlich zu sein. Wer es verstanden hat wird es auch für seine zwecke nutzen können.
Es ist keine "Nachklicken für Kinder" Anleitung, die Inhalte richten sich an Fortgeschrittene.
Das Beispielproject:
Wir haben eine Map & ein Männchen.
Wenn wir das Männchen ansprechen bekommen wir die Aufforderung nacheinander 2 Zahlen und eine Rechenoperation einzugeben.
Die Eingabe soll über ein CommonEvent erfolgen, dass vom Eventscript des Männchen aufgerufen wird.
Die Verarbeitung der 2 Zahlen und der Operation erfolgt in einem 2. CommonEvent.
Hier mal als Blockdiagramm:
http://www.imgimg.de/uploads/Fensterblock53ef1ab3jpg.jpg
Vorbereitung:
Wir reservieren 3 Variablen.
SP - der Stackpointer
SP_H - ein Hilfspointer, nötig weil der Maker keine PointerOffsets verrechnen kann
Tempsave - Zwischenspeicher, entspricht dem Akkumulator einer CPU
Was ist ein Stack/Stackpointer?
Der Stack bezeichnet einen Speicherbereich in dem Speicherplatz vergeben werden kann.
Der Stackpointer ist ein Zeiger auf die unterste Speicheradresse die besetzt ist, man kann also davon ausgehen, dass man den auf dem Platz unterhalb des Stackpointers befindlichen Speicher verwenden kann ohne etwas zu überschreiben.
Der Stackpointer muss zu anfang natürlich erst einmal positioniert werden.
http://www.imgimg.de/uploads/Sp0165f6a11cjpg.jpg
So sieht das dann im Variablenblock des Makers aus.
Das Script:
Dies ist der Scriptcodes des Rechenscripts. Ich habe die Abschnitte & Zeilen nummeriert und werden sie weiter unten kommentieren und mit Speicherabbildern illustrieren.
Wenn ich mich weiter unten auf den Scriptcode beziehe meine ich diese Grafik.
http://www.imgimg.de/uploads/Bild152b85b0e4jpg.jpg
Abschnitt 1:
Dieser Abschnitt besteht aus 3 identischen Einzelschritten.
//Einlesen Zahl1:
Der Stackpointer wird um 1 Feld nach unten verschoben indem man ihn +1 addiert.
Damit zeigt er auf einen leeren Variablenplatz.
Wir lesen die erste Zahl in den Zwischenspeicher ein und verschieben sie an die Stelle
auf die der Stackpointer gerade zeigt.
Diese Stelle ist nun der Speicherort für "Zahl 1" mit der wir nachher rechnen werden.
Dasselbe passiert mit den Eingaben für die Operation (1=Addieren , 2 & Rest = subtrahieren ) und die 2. Zahl.
Nachdem dies passiert ist bekommen wir folgendes Speicherabbild:
Unterhalb des Ursprungsortes des Stackpointers wurde Platz gemacht für unsere 3 Varibalen. Am Ende der Operation zeigt der Stackpointer
auf den letzten belegten Variablenplatz.
http://www.imgimg.de/uploads/Sp0249f4e5dfjpg.jpg
Abschnitt 2:
Nach dem Einlesen der 3 Variablen können diese nun verarbeitet werden, betrachten wir dazu also einmal das Common Event zur Verrechnung.
v_Rechnen(Z1,Z2,OP)
Ich benutze das Common Event hier wie eine Funktion in Assembler.
Die Funtion benötigt 3 Parameter und gibt einen Wert zurück, die Parameterübergabe und der Rückgabewert müssen allerdings per Hand" gemacht werden.
Ein Funktionsaufruf wird gemacht indem man oben auf dem Stack Platz für den Rückgabewert schafft und die Paramater direkt danach anordnet.
Das ist zwar keine "richtige" Parameterübergabe, aber irgendwie doch, denn innerhalb des aufgerufenen Commen Events kann man dann mit Sicherheit davon ausgehen, dass sich die benötigten Parameter auf bestimmten Plätzen relativ zur Position des Stackpointers befinden.
Die Compiler jeder Hochsprache wie C++ verfahren genau so wenn sie den C-Code in Assembler übersetzen.
Reservieren des Rückgabewertes:
http://www.imgimg.de/uploads/Sp03c715d96fjpg.jpg
Der Platz für den Rückgabewert wird reserviert.
Abschnitt 3:
Anschliessend werden die Inhalte der Parameter gelesen und in richtiger Reihenfolge auf dem Stack positioniert.
http://www.imgimg.de/uploads/Sp041c661980jpg.jpg
Warum kopiert man die Variablen nochmal? Die stehen doch direkt davor schon einmal!
Das mag sein, man kann auch die Funktion so umschreiben dass es mit dem schon positionierten Daten klappt, aber nicht immer liegen die Parameter in der selben Reihenfolge vor. Um also sicher zu gehen, dass egal in welchem Kontext das CE aufgerufen wird immer die parameter richtig übernommen, kopiert man sie noch einmal neu auf den Stack.
Abschnitt 4: CallEvent Rechnen!
Das CE wird aufgerufen. Innerhalb des "Rechnen"-CE wird der Stackpointer nochmals um einen Platz nach unten verschoben um Platz für das Rechenergebnis zu schaffen. Anschliessend wird die Rechenoperation durchgeführt, hierzu werden die Parameter über den Stackhilfspointer (SP_H) relativ zum Stackpointer angesprochen.
http://www.imgimg.de/uploads/Sp05e7092bcejpg.jpg
So sieht es dann im Speicher aus.
Abschnitt 5: Wert zurückgeben, Spuren beseitigen
Nachdem die Rechnung nun ausgeführt wurde speichert man den Inhalt des Speichrplatzes auf den Stackpointer gerade zeigt an den Ort für den Rückgabewert, in diesem Fall 4 Plätze oberhalb des Stackpointers, den wir durch Rechnen mit dem Hilfspointer erreichen.
Anschliessend wird der Stackpointer um 4 reduziert.
Damit geben wir den Speicherplatz der 3 Parameter und des Ergebnisses wieder frei
Das Commen Event "Rechnen" endet hier~
http://www.imgimg.de/uploads/Sp06ca110cf8jpg.jpg
Abschnitt 6: Ausgabe & Speicher freiräumen.
Nun da alles getan ist fehlt noch die Ausgabe in einer Textbox.
Dazu wird der Inhalt des Feldes auf das SP zeigt in Tempsave kopiert und dieses dann ausgegeben.
Anschliessend wird SP um 4 verringert, damit zeigt der Stackpointer wieder auf den Ursprungswert
http://www.imgimg.de/uploads/Sp08007bd855jpg.jpg
Hurray~ flexible Speichervergabe =D
Fazit:
Vorteile:
- Rekursive Aufrufe möglich
- weniger gleichzeitiger Speicherbedarf,effizientere Nutzung
Nachteile:
- Parallele Prozesse & Stack verträgt sich nicht da man innerhalb des Makerscript keine Interrupts besitzt, keine Rücksprungpunkte etc.
- Debugging wird schwieriger da man kein bleibendes Speicherabbild mehr hat und die Benennung der Variablen zwecks Übersicht fehlt, je nach Verzweigungstiefe der Aufrufe kanns schwer werden die Daten im F9-Modus überhaupt zu finden.
Das wars soweit
mfg~ der Corti