Parameterübergabe über die Zeropage

Es gibt 55 Antworten in diesem Thema, welches 5.417 mal aufgerufen wurde. Der letzte Beitrag (7. März 2024 um 19:13) ist von JeeK.

  • Hallo!

    Ich hab hier in einem älteren Thread gelesen dass zur Parameterübergabe an Unterprogramme auch oft (vor allem bei mehreren Parametern) Stellen in der Zeropage genutzt werden wenn man mit den Registern nicht mehr auskommt.

    Für mich als Assembler-Anfänger stellt sich hier jedoch die Frage: Welche Adressen kann ich hier gefahrlos für diesen Zweck nutzen?

    Die Zeropage enthält ja ziemlich viele wichtige und essentielle Informationen und selbst wenn ich Zeropage-Adressen verwende die auf meinem System nicht genutzt werden heißt das ja nicht dass das auf anderen Systemen auch der Fall ist.

    Dann würde mein Programm auf anderen Systemen ja eventuell nicht einwandfrei laufen.

    Ich muss da aktuell ziemlich umdenken da ich von der Assembler-Programmierung am PC komme und von dort natürlich die Parameterübergabe über den Stack gewohnt bin.

    Doch gerade diese Methode wurde in dem oben genannten Thread nicht als die optimale Methode in Bezug auf 6502 Assembler dargestellt und dass sie eher nur für Spezialfälle (Rekursion, Reentranz) verwendet wird.

    Und ich kann das auch nachvollziehen weil man verhältnismäßig viel Code für das Parameterhandling schreiben muss.

    lg, Markus

    Hier der Link zum Github - Repo des Assembler-Kurses: Bitte melde dich an, um diesen Link zu sehen.

  • selbst wenn ich Zeropage-Adressen verwende die auf meinem System nicht genutzt werden heißt das ja nicht dass das auf anderen Systemen auch der Fall ist.

    Ich glaube, da wird einfach immer davon ausgegangen, dass man quasi auf dem System "alleine" ist. Es gibt ein paar Addressen, bei denen "unbenutzt" dabei steht. Diese kann man erstmal prinzipiell nutzen. Auch der Kassettenpuffer wird gerne verwendet. Am Ende des Bildschirmbereichs ist auch noch etwas ungenutzter Platz.

    Allerdings kann es tatsächlich passieren, dass man noch irgendwas anderes im Hintergrund laufen hat, was einen Konflikt verursacht. Ich bin jetzt auf den 8-Bittern auch noch Anfänger und kenne das vom PC etwas mehr. Dort wird ja für die Übergabe der Stack genutzt. Ich sehe jetzt keinen Grund, das hier nicht auch zu machen, wenn man auf Nummer sicher gehen will.

  • Die Klassiker sind die Adressen 2, 251, 252, 253 und 254. Stack ist wirklich unpraktisch, weil man auf den nicht wirklich geschickt beliebig zugreifen kann (und wenn man die Parameter runter-popped, muss man sie ja meist auch wieder irgendwo zwischenspeichern). 64erGrufti hat recht: meist gilt bei Assembly-Programmen nach mir die Sintflut und man reißt sich einfach alles unter den Nagel, was man nicht anderweitig braucht (für I/O etc).

    Ich schau meist einfach hier: Bitte melde dich an, um diesen Link zu sehen. und entscheide aus dem Bauch raus.

    Und im Zweifelsfall muss man natürlich nicht die Zeropage nehmen, sondern kann auch beliebige Adressen woanders verwenden, solange es nicht furchtbar zeitkritisch ist.

    ────────────────────────────────────────────────────────────
    Bitte melde dich an, um diesen Link zu sehen. - Bitte melde dich an, um diesen Link zu sehen.
    ────────────────────────────────────────────────────────────

  • wenn man die Parameter runter-popped, muss man sie ja meist auch wieder irgendwo zwischenspeichern

    Das funktioniert am PC aber problemlos und ist dort gängige Technik. Warum sollte das hier problematischer sein?

    Wenn ich slebst jetzt mal kein Interruptprogramm schreibe, dann sollte ich selbst über die Register quasi die Kontrolle haben, d.h. vom Programmablauf selbst wissen, was, wann, in welchem Register steht. Wenn ich nun Werte auf den Stack schiebe, kann ich sie im Unterprogramm wieder runter holen und sollte auch in keinen Konflikt rein laufen. Wird ein Interrupt ausgelöst, sollte das Programm dort eh alle Register auf den Stack schieben und vor Beenden wieder runter holen, d.h. bei der Rückkehr aus dem Interruptprogramm sollte eh der vorherige Zustand wieder hergestellt sein. Ich muss mir halt nur vorher schon überlegen, in welcher Reihenfolge ich die Werte auf den Stack schiebe, bzw. in welcher Reihenfolge ich diese brauche.

  • Stimmt, die Abwicklung über den Stack ist wirklich recht umständlich, hab gestern mal versucht drei Parameter über den Stack an ein Unterprogramm zu übergeben, da kommt recht schnell einiges an Code zusammen und der Zugriff ist dann auch nicht so einfach wie am PC, dort friert man ja üblicherweise den Inhalt des SP-Registers im BP-Register ein und greift dann einfach per Index auf die Parameter zu, z.B. mit mov ax, [bp+4]

    Erschwerend kommt noch hinzu dass man ja nur den Inhalt des Akkumulators und des Statusregisters direkt per Maschinenbefehl auf den Stack bringen bzw. von dort wieder holen kann. Da ist der Weg über die Zeropage oder irgendeine eigene Struktur sicher einfacher.

    Hier der Link zum Github - Repo des Assembler-Kurses: Bitte melde dich an, um diesen Link zu sehen.

  • Ich muss mir halt nur vorher schon überlegen, in welcher Reihenfolge ich die Werte auf den Stack schiebe, bzw. in welcher Reihenfolge ich diese brauche.

    Man kann die Problematik natürlich auf den Caller abschieben, aber dann muss der halt wiederum schauen, wo er die Werte zwischenspeichert. Nach meiner persönlichen Erfahrung ist das (vor allem wegen der geringen Zahl der Register) auf dem 6502 ein umständliches Rumgehampel.

    ────────────────────────────────────────────────────────────
    Bitte melde dich an, um diesen Link zu sehen. - Bitte melde dich an, um diesen Link zu sehen.
    ────────────────────────────────────────────────────────────

  • Die Klassiker sind die Adressen 2, 251, 252, 253 und 254. Stack ist wirklich unpraktisch, weil man auf den nicht wirklich geschickt beliebig zugreifen kann (und wenn man die Parameter runter-popped, muss man sie ja meist auch wieder irgendwo zwischenspeichern).

    Stimmt, die Abwicklung über den Stack ist wirklich recht umständlich, hab gestern mal versucht drei Parameter über den Stack an ein Unterprogramm zu übergeben, da kommt recht schnell einiges an Code zusammen und der Zugriff ist dann auch nicht so einfach wie am PC, dort friert man ja üblicherweise den Inhalt des SP-Registers im BP-Register ein und greift dann einfach per Index auf die Parameter zu, z.B. mit mov ax, [bp+4]

    Erschwerend kommt noch hinzu dass man ja nur den Inhalt des Akkumulators und des Statusregisters direkt per Maschinenbefehl auf den Stack bringen bzw. von dort wieder holen kann. Da ist der Weg über die Zeropage oder irgendeine eigene Struktur sicher einfacher.

    Hier der Link zum Github - Repo des Assembler-Kurses: Bitte melde dich an, um diesen Link zu sehen.

  • Da ist der Weg über die Zeropage oder irgendeine eigene Struktur sicher einfacher.

    Kla, das sicherlich. Aber wenn Du konfliktfrei arbeiten willst, was ja Deine ursprüngliche Anfrage war, kannst Du das nicht über die Zeropage machen. Oder Du musst innerhalb Deines Programms einfach ein paar Bytes dafür reservieren. Dann kannst Du es komfortabel und sicher gestalten.

  • Ich glaub zusammenfassend kann man sagen dass man das einfach von Fall zu Fall individuell entscheiden muss, je nachdem worauf es ankommt (Geschwindigkeit, Sicherheit usw.)

    Hier der Link zum Github - Repo des Assembler-Kurses: Bitte melde dich an, um diesen Link zu sehen.

  • Das Schöne an Maschinensprache ist: Die Kiste gehört komplett Dir - wenn Du es willst. D.h., man kann grundsätzlich alle Arbeitsspeicheradressen nach Belieben benutzen, außer die, die hardwareseitig schon in irgendeiner Weise Verwendung finden, und selbst von denen lassen sich viele noch auf andere Weise nutzen, nämlich vom Stack. Die CPU hat ja extra Befehle bzw. Indizierungsarten zur Benutzung der Zeropage vorgesehen, und da diese effizient sind, ist es ratsam, sie ausgiebig zu verwenden.

    Wenn man BASIC und Maschinensprache kombinieren will, ist es anders. Dann hängt es davon ab, welche ROM-Routinen überhaupt zum Einsatz kommen. Das fängt schon mal beim Service-Interrupt an. Pauschal kann man also nicht sagen, welche Zeropage-Adressen bei Benutzung des BASIC frei oder sicher sind. Viele Adressen kann man aber temporär nutzen. Die werden erst dann wieder überschrieben, wenn eine entsprechende BASIC-Routine zum Einsatz kommt, die ebenfalls diese Adressen verwendet. So kann man z.B. in der Regel die Fließkomma-Register problemlos vorübergehend verwenden, werden aber durch den Gebrauch von BASIC-Berechnungen wieder überschrieben. Wenn man nur danach gehen würde, welche Register tatsächlich dauerhaft von BASIC unberührt bleiben, dann ist die Auswahl sehr gering und im C64-Wiki zur Zeropage dokumentiert.

  • Hätte hier abschließend noch ein kleines Rechenbeispiel und bin mir nicht sicher ob ich das richtig verstanden habe.

    Es geht um die genauen Abläufe wenn ein JSR ausgeführt wird. Das Programm ist sinnbefreit, es geht rein um den Ablauf beim Aufruf des Unterprogramms und dann den Rücksprung zum Aufrufer mit RTS.

    1500 A2 00 LDX Bitte melde dich an, um diesen Link zu sehen.

    1502 A9 01 LDA Bitte melde dich an, um diesen Link zu sehen.

    1504 20 00 30 JSR $3000

    1507 RTS

    Die Rücksprungadresse ist ja eigentlich $1507.

    Laut der Beschreibung auf der Seite Bitte melde dich an, um diesen Link zu sehen. wird die Adresse des JSR-Opcodes $1504 +2 auf den Stack gelegt, also die um 1 verminderte Rücksprungadresse $1507-1 = $1506, und zwar zuerst das Hi-Byte $15 und dann das Lo-Byte $06.

    Hier das Unterprogramm:

    3000 INX

    3001 INX

    3002 INX

    3003 INX

    3004 RTS

    Der Befehl RTS holt sich die Adresse dann wieder vom Stack, addiert 1 drauf und trägt $1507 im Program Counter ein wodurch es beim RTS an der Adresse $1507 weitergeht mit der Programmausführung.

    Hab ich das richtig verstanden?

    Hier der Link zum Github - Repo des Assembler-Kurses: Bitte melde dich an, um diesen Link zu sehen.

    Einmal editiert, zuletzt von markusk2020 (20. Februar 2024 um 10:17)

  • Der Kassettenpuffer bietet sich auch an, ebenso kann man unter Umständen den Input-Puffer ab $0200 benutzen. Möglichkeiten gibt es viele.

    Früher waren 64k unglaublich viel, heute reicht es nicht mal mehr für "Hello, world!".

  • Hab ich das richtig verstanden?

    Das ist richtig!

    ────────────────────────────────────────────────────────────
    Bitte melde dich an, um diesen Link zu sehen. - Bitte melde dich an, um diesen Link zu sehen.
    ────────────────────────────────────────────────────────────

  • Das Schöne an Maschinensprache ist: Die Kiste gehört komplett Dir - wenn Du es willst. D.h., man kann grundsätzlich alle Arbeitsspeicheradressen nach Belieben benutzen, außer die, die hardwareseitig schon in irgendeiner Weise Verwendung finden

    Das kommt eben drauf an, ob man den Anwender unbedingt zwingen will, einen jungfräulichen Computer zu benutzen. Klar, kann man machen, aber das führt auch gerne mal zu Frust.

    Wenn da jemand ein Steckmodul (Basic-Erweiterung, Fastloader, etc.) drin hat, weißt Du halt nicht mehr unbedingt, was Du da benutzen kannst. Ich habe schon öfters Programme gehabt, wo ich mein Steckmodul mit Schnelllader raus ziehen und langsam laden musste. Ich finde sowas als Nutzer nicht erfreulich. Klar, für den Programmierer war das einfach, der Schuß kann aber auch mal ganz schnell nach hinten los gehen, wenn man die Leute frustriert. Ein Programm, was dann von den Leuten durch andere erstezt wird, weil es da weniger Komplikationen gibt, hat man dann umsonst geschrieben.

    Hab ich das richtig verstanden?

    Ja, ich denke schon. So habe ich es zumindest auch verstanden.

    muffi

    Zitat

    Früher waren 64k unglaublich viel, heute reicht es nicht mal mehr für "Hello, world!".

    Da hast Du wohl noch nicht Bitte melde dich an, um diesen Link zu sehen. gesehen

  • Wenn da jemand ein Steckmodul (Basic-Erweiterung, Fastloader, etc.) drin hat, weißt Du halt nicht mehr unbedingt, was Du da benutzen kannst.

    Das kann abgeschaltet werden.

    Ich habe schon öfters Programme gehabt, wo ich mein Steckmodul mit Schnelllader raus ziehen und langsam laden musste.

    Dann ist es aber schlecht programmiert. Entweder, man nutzt bewusst das vorhandene Schnelllademodul, oder man baut einen eigenen Schnelllader ein. Wenn denn überhaupt nachgeladen werden muss. Davon ging ich nicht unbedingt aus. Wenn man eine Einzeldatei, meinetwegen 200 Blocks, mit Speeder einlädt, dann kann man danach im C64 machen, was man will. Auch ergeben sich keine Probleme, wenn man selbst für Modul programmiert.

    Es gibt unzählige Module. Wenn man die alle als aktive Module berücksichtigen wollte, dürfte man gar nichts in der Zeropage nutzen, auch nicht die "ab Werk" dauerhaft freien Register, die Claus oben schon aufzählte. Dann gäbe es die großen Spiele und sämtlichen Demos erst gar nicht. Man hat sich m.E. eigentlich darauf geeinigt, einen originalen C64 voraussetzen zu dürfen. Viele Puristen fordern dies sogar als Grundlage.

  • Das kann abgeschaltet werden.

    Was kann abgeschaltet werden?

    Dann ist es aber schlecht programmiert. Entweder, man nutzt bewusst das vorhandene Schnelllademodul, oder man baut einen eigenen Schnelllader ein

    Es geht dabei nicht unbedingt ums Nachladen. Es gibt Programme, die laufen dann einfach nicht.

    Man hat sich m.E. eigentlich darauf geeinigt, einen originalen C64 voraussetzen zu dürfen. Viele Puristen fordern dies sogar als Grundlage.

    Und genau das ist das Problem. Das fängt schon bei einem alternativen Kernel an. Mit so einem "Egoismus" kommen nämlich dann solche Probleme zustande (gut, ich nehme an, dass es hier nicht die Zeropage ist).

    Dass man sich "darauf geeinigt" hat, würde ich weniger sagen. Eher man hat sich "daran gewöhnt".

  • Was kann abgeschaltet werden?

    das Modul

    Es gibt Programme, die laufen dann einfach nicht.

    Das ist aber kein Beleg dafür, dass das so sein muss. Meine ursprgl. Aussage war, dass man mit Assembler den C64 übernehmen und dann auch die ZP frei verwenden kann.

    Wer ein nicht umschaltbares alternatives Kernel fährt, ist im Prinzip selbst schuld, wenn deswegen ein Programm nicht läuft. Die meisten Programme setzen die original verbauten ROMs voraus. Selbst die Sprungvektoren, die ursprünglich für Kompatibilität sorgen sollten, werden von den meisten schon längst nicht mehr genutzt, weil man davon ausging, dass sich an den originalen ROMS nichts mehr ändert. Ob das i.O. ist, ist eine andere Frage, aber es ist so. Allerdings spielt das bzgl. meiner Aussage eigtl. auch keine Rolle. Denn ich sagte ja, man kann den C64 übernehmen, und das heißt, dass mich auch die verbauten ROMs nicht interessieren müssen. Die werden dann nämlich auch ausgeblendet. CPU, SID, VIC und CIAs dürfen als unveränderlich betrachtet werden; ansonsten ist es kein C64/C128.

    Es gibt ein paar Addressen, bei denen "unbenutzt" dabei steht.

    Dieses "unbenutzt" bezieht sich aber auch nur auf BASIC und Kernal, nicht auf etwaige eingesteckte aktive Module. Dann dürfte man diese Register genauso wenig benutzen wie alle anderen. Aber so ist es eben nicht; vielmehr kann man alles abschalten und die ZP komplett leerfegen, von den ersten beiden Adressen abgesehen. Frag mal die Demoprogrammierer; da wird nichts dem Zufall überlassen.

  • Wofür sollte man denn ein Modul während eines Spieles brauchen? Freezen geht eigtl. immer. Ansonsten fällt mir nur Schnellladen ein. Aber genau das ist oft gar nicht gewollt, weil man dann z.B. Übergangsbilder beim Laden nicht zu sehen bekommt, wenn der VIC abgeschaltet wird. Manche Spiele lassen auch die Musik laufen beim Laden, zeigen Animationen etc., z.B. Hawkeye. Es wird aber kein Demoprogrammierer oder Entwickler eines größeren Spieles auf die Zeropage, die für die CPU und die Programmierung essenziell ist, verzichten wollen, nur damit alle möglichen Module aktiv bleiben dürfen.