Debuggen

Die „Wanzen“ finden…

C64 Studio & AMCE

Wir kennen jetzt alle Assemblerbefehle und Adressierungsarten des C64. Die meisten Befehle haben wir auch in Beispielprogrammen verwendet.

Das Wichtigste ist es nun weiterzumachen!

Nur durch üben lernt man wirklich etwas. Auch das Fehlermachen, diese zu suchen und zu korrigieren gehört dazu und ist von unschätzbarem Wert. Die Fehlersuche wird im Englischem übrigens „debuggen“ genannt, dieser Begriff wird euch in diesem Zusammenhang häufig begegnen.
Wenn eine Routine einfach nicht funktionieren will und ihr euch reinhängt, um das gewünschte Ergebnis zu erreichen, dann werdet ihr dabei eine Menge mehr lernen, als durchs Lesen eines Textes und probieren von Beispielen per Copy & Paste.

Mut zum Fehler

Fehler werden euch immer wieder begegnen.
Schauen wir uns daher zum Abschluß des Assembler Grundlagen Tutorials exemplarisch mal die am häufigsten vorkommenden an und klären, wie man diese findet.

Wie ihr gleich sehen werdet, gestaltet sich die Fehlersuche am PC mit dem C64 Studio oder einem Emulator sehr einfach. Das C64 Studio bietet dabei die wirklich leichteste Möglichkeit zur Fehlersuche. Setzt ihr andere Assembler wie z. B. ACME ein, dann solltet ihr das Programm im Emulator untersuchen. Wir schauen uns hier beides einmal an, beim Emulator gehe ich von WinVICE aus.

Falls ihr weder das C64 Studio noch ACME einsetzt, kann es auch sein, dass es neue Fehler bei der Erstellung gibt oder andere gar nicht auftreten, da der Assembler den Quellcode anders umsetzt.

Direkt am C64 ist es schon schwieriger, Fehlern auf die Spur zu kommen. Dort empfiehlt sich der Einsatz eines Freezer-Moduls (z. B. das Action Replay oder The Final Cartridge), für die Fehlersuche. Dies wird hier aber kein Thema sein.

Syntax-Fehler

Am einfachsten zu finden sind wohl die Syntax-Fehler bei der Eingabe. Schreibt ihr versehentlich z. B. lad #$5a statt lda #$5a oder verwendet eine für den Befehl nicht mögliche Adressierung wie z. B. sty $d020,x, dann werdet ihr spätestens beim Erstellen des Programmes vom Assembler darauf hingewiesen. Dies machen alle Assembler, daher sind diese Fehler sehr einfach zu beheben.

Testprogramm zum Üben der Fehlersuche

Die folgenden Tippfehler sind da schon etwas fieser. Sie erzeugen nicht immer eine Meldung des Assemblers, können einen aber schon einiges an Kopfzerbrechen bereiten.
Übernehmt das folgende Programm bitte 1:1!
Ich beziehe mich im Verlauf des Beitrages auf die jeweiligen Zeilnnummern und wenn ihr diese beibehaltet, wird es etwas leichter für euch.

!!! Alles voller Fehler !!!

Auf den ersten Blick sieht das Programm ganz OK aus. Es sollte eigentlich nur 40 mal den Buchstaben C in der ersten Bildschirmzeile ausgeben.

Wir möchten einfach 40x den Buchstaben „C“ in der ersten Zeile ausgeben.

Später sollten wir es korrigieren können, ohne das wir das Programm einmal starten müssen. Da wir aber noch am Anfang unserer Programmiererlaufbahn auf dem C64 sind, nutzen wir die uns gegebenen Möglichkeiten zur Fehlersuche komplett aus.

Erste Fehlermeldungen

Erstellt jetzt das Programm. Wie erwartet, erhaltet ihr einige Fehlermeldungen, dies ist hier ja unser aktuelles Thema. Welche Fehlermeldungen ihr nun seht, hängt vom jeweiligem Assembler ab.

Beim C64 Studio erhaltet ihr z. B. folgende Liste:

Meldungen im C64 Studio

ACME wirft euch zunächst nur diese beiden Meldung aus:

Fangen wir also an, unser Programm zu debuggen.

Zeile: 2

Die Assembler beschweren sich, dass in dieser Zeile kein Wert angegeben wurde bzw. bei ACME zusätzlich, dass ungültige Zeichen am Ende der Zeile stehen.

Die Lösung ist recht simpel:
Ihr dürft bei einer Variablen / Konstanten kein # verwenden. Außerdem fehlt das $ als Kennzeichen, dass es sich um eine Hex-Zahl handelt.

Ändern wir die Zuweisung in der Zeile also in: ZP_COPYPOS = $fb

Erstellt jetzt das Programm erneut.

Beim C64 Studio gibt es aktuell keinen Fehler mehr, obwohl einer angebracht wäre. ACME zeigt euch aber folgendes an:

Zeile: 14

Wir wollen hier das dritte Zeichen (die Zählung beginnt bei 0!), also das C, aus der !byte-Zeile am Programmende laden. Unserem Befehl lautet aber: Lade den Wert des Labels l1+2 in den Akku, da wir eine Raute # verwenden. Das Label l1 hat einen Wert größer als 255, es liegt ja hinter $0801. Wir versuchen hier also $081d in den Akku zu holen. Da der Akku nur 8-Bit groß ist, kommt es zur Fehlermeldung. Löscht die Raute, so dass nur noch lda l1+2 stehen bleibt. Als aufmerksame Leser, habt ihr bestimmt ein weiteres Problem mit dieser Zeile entdeckt, um dieses kümmern wir uns aber später.

Wenn ihr das Programm jetzt erstellt, dann gibt es keine Fehler mehr vom Assembler, aber unser Programm zeigt auch keinerlei Ausgaben.

Schön wäre es doch, wenn man jetzt einfach nach jeder Zeile kontrollieren könnte, was gerade so in den Speicherstellen und Registern steht. Genau dies können wir mit dem C64 Studio oder einem Emulator bequem machen. Dieses schrittweise durchgehen, nennt man „tracen“. Da sich das Tracen mit dem C64 Studio und VICE unterscheidet, trennen sich hier jetzt die Wege. Zunächst kümmern wir uns um das C64 Studio, weiter unten machen wir dann dasselbe nochmal mit VICE.

Falls ihr kein C64 Studio verwendet, empfehle ich euch trotzdem mal einen Blick auf den folgenden Abschnitt zu werfen, damit ihr seht, was ihr verpasst.

Tracen mit dem C64 Studio

Um ein Programm schrittweise abzuarbeiten, benötigen wir erstmal einen Breakpoint (Haltepunkt). Damit legen wir fest, an welcher Stelle wir uns unser Programm per Einzelschritt, genauer ansehen möchten. Wir wollen direkt mit der ersten Anweisung lda #>SCREENRAM beginnen.
Klickt daher im C64 Studio einfach auf die Zeilennummer 9.

Einen Breakpoint setzen.

Wie ihr seht, könnt ihr durch einen erneuten Klick auf die Zeilennummern, den Breakpoint auch wieder löschen. Ihr müsst euch nicht auf einen Breakpoint beschränken! Ihr könnt beliebig viele davon setzen.

Kontrolliert noch, ob die Toolleiste für den Debugger sichtbar ist…

Toolleiste fürs Debuggen anzeigen.

Damit das C64 Studio nun überhaupt auf die Breakpoints reagiert, dürft ihr das Programm nicht mehr einfach mit F5 bzw. „Build and run“ starten. Klickt stattdessen auf Debuggen in der Toolleiste oder drückt STRG+F5.

Was alles passiert, wenn man das Debuggen startet.

Sobald ihr mit dem Debuggen beginnt, passiert eine ganze Menge:

  • Das C64 Studio erstellt ggf. das Programm.
  • VICE wird vom C64 Studio mit unserem Programm gestartet. Dabei übernimmt das C64 Studio teilweise die Steuerung von VICE. Ihr solltet daher das Debuggen nur über das C64 Studio beenden und nicht einfach VICE schließen, sonst stürzt es ab!
  • Die Buttons auf der zuvor eingeblendeten Toolleiste werden aktiviert.
    Ihre Funktionen (von links nach rechts) sind:

    1. Start
      Das Programm normal weiterlaufen lassen. Beim nächsten Breakpoint, wird aber wieder unterbrochen.
    2. Pause
      Falls das Programm aktuell normal läuft, könnt ihr es über den 2. Button manuell unterbrechen.
    3. Debuggen beenden
      Dadurch wird auch VICE geschlossen! Wie bereits erwähnt, solltet ihr das Debuggen immer über diese Funktion abschließen.
    4. Ins Unterprogramm spingen
      Stosst ihr auf einen jsr-Befehl, könnt ihr euch entscheiden, ob ihr dem Sprung folgen wollt. Falls ja, klickt auf den 4. Button und ihr traced auch das Unterprogramm. Die Funktion kann auch mit F11 ausgelöst werden.
    5. Unterprogramm normal ausführen
      Möchtet ihr euch die Routine, zu der jsr verzweigt, nicht genauer ansehen, dann klickt auf diesen Button. Die Subroutine wird dann ganz normal, ohne Unterbrechungen ausgeführt und ihr gelangt direkt zum nächsten Befehl, der hinter dem ursprünglichen jsr steht. Solltet ihr im Unterprogramm ebenfalls Breakpunkte gesetzt haben, dann wird dort natürlich wieder angehalten. Ihr könnt dafür euch einfach F10 drücken.
    6. Unterprogramm verlassen
      Befindet ihr euch in einem Unterprogramm, habt aber genug gesehen, dann könnt ihr mit dem sechsten Button das Programm weiterlaufen lassen, bis der dazu gehörige rts-Befehl ausgeführt wurde. Klickt ihr in unserm Beispiel auf diesen Button, dann landet ihr übrigens in einer ROM-Routine, da unser Programm von dort aufgerufen wurde. Ihr könnt also sogar durchs ROM tracen.
  • Es öffnet sich ein Fenster mit den Registern.
    Das Registerfenster

    Hier können wir gleich genau nachvollziehen, was für Register die einzelnen Befehle beeinflussen und welchen Wert sie haben. Änderungen werden übrigens rot hervorgehoben. Hier noch eine kurze Erklärung, der einzelnen Felder. Bei den ersten fünf Feldern, wird euch der Inhalt immer hexadezimal und dezimal angezeigt.

    • X – Inhalt des X-Registers
    • Y – Inhalt des Y-Registers
    • A – Inhalt des Akkumulators
    • PC – Inhalt des Programcounters, hier seht ihr also, in welcher Speicherstelle sich der jeweilige Befehl befindet.
    • SP – Inhalt des Stackpointers
    • Status – Hier könnt ihr erkennen, welche Bits im Statusregister aktuell gesetzt bzw. gelöscht sind.
    • LIN – In welcher Rasterzeile befinden wir uns.
    • CYC – Wieviele Taktzyklen sind in der aktuellen Rasterzeile vergangen.
      LIN und CYC werden euch jetzt wahrscheinlich noch nicht viel sagen. Sobald ihr euch mit dem Rasterzeilen-Interrupt beschäftigt, werdet ihr diese Angaben verstehen.
    • $01 – Wie ihr seit Kleine Hardwarekunde bereits wisst, kann man über die Speicherstelle $01 ROMs ein- und ausblenden. Da diese Adresse sehr wichtig ist, wird sie euch hier immer direkt angezeigt.
  • Unser Programm läuft bis zum Breakpoint und hält direkt nach der BASIC-Zeile an.
Zeile 9

Wir befinden uns jetzt also in der ersten Programmzeile und zwar bevor diese ausgeführt wurde.

Schauen wir uns mal an, was in dieser Zeile geschieht:
Wir möchten das LSB der Adresse, die in der Variablen SCREENRAM gespeichert ist, in den Akku holen. Da wir dort $03ff eingetragen haben, sollte also $ff in den Akku geladen werden. Solltet ihr beim Tracen mal nicht mehr wissen, was in Variablen steht, dann stellt einfach den Mauszeiger darauf. Es wird euch dann ein Tooltip angezeigt.

Werft zunächst einen Blick auf die Register, sie sollten wie oben gezeigt aussehen und drückt anschließend F10 bzw. klickt auf den Button in der Toolleiste, um die Zeile auszuführen.

Die erste Zeile tracen.

Wie wir an der roten Farbe erkennen, hat sich der Akku geändert. Dort steht jetzt eine $03 drin. Moment mal! $03? Das ist aber nicht das erwartete LSB! Es ist das MSB. Genau, wir haben uns bei den #< #> vertan.

Beendet das Debuggen und ändert dann die Zeilen 9 und 11:

Traced dann erneut über Zeile 9 und ihr werdet sehen, dass dieses Mal der richtige Wert im Akku landet. Außerdem sollte euch auffallen, dass jetzt auch das N-Flag gesetzt wurde, weil wir nun $ff statt $03 im Akku haben. Bevor ihr Zeile 10 ausführt, stellt den Mauzeiger mal auf das ZP_COPYPOS hinter dem sta. Ihr seht, dass es sich um die Speicherstelle $fb handelt und ihren aktuell Inhalt, hier $00. Führt dann Zeile 10 aus und kontrolliert erneut ZP_COPYPOS, jetzt solltet ihr sehen, dass dort $ff, unser LSB, steht. Jetzt führt noch Zeile 11 aus und kontrolliert den Akku. Jup, das passt: $03.

Soweit, so gut.
Zeile 12

Sobald ihr bei Zeile 12 ankommt, solltet ihr stutzig werden. Der Debugger hilft uns hier nicht direkt, aber da wir uns jede Zeile vor Ausführung ansehen, fällt uns direkt ein weiterer Fehler auf.

Na, habt ihr es gesehen? Wir überschreiben das LSB von eben, mit dem MSB, da wir ein +1 vergessen haben.

Korrigiert also Zeile 12 in…

Da wir nun die ersten Probleme behoben haben, können wir den bisherigen Breakpoint löschen und einen neuen für die Zeile 13 setzen.

Zeile 13

Debuggt das Programm jetzt, mit dem neuen Breakpoint in dieser Zeile. Wir möchten ja eine komplette Zeile mit dem Buchstaben C füllen. Da eine Zeile 40 Zeichen fassen kann, wollen wir diesen Wert ins X-Register laden. Eigentlich sollte euch direkt der Fehler ins Auge springen, wir tracen aber trotzdem. Führt also Zeile 13 aus.

Ihr solltet sehen, dass das X-Register weiterhin den Wert $00 hat. Der Grund ist wieder recht einfach, kann aber zu echt schwer zu entdeckenden Fehlern führen. Wir nutzen ldx $40, damit laden wir aber nicht 40 ins X-Register, sondern den Inhalt der Speicherstelle $40. Aktuell steht dort eine 0 es hätte aber jeder beliebige Wert sein können, der dort zufinden ist. Sogar unsere gewünschte 40.

Wir ändern dies also in:

Wir kommen gleich nochmal zur Zeile 13 zurück, machen aber mit der nächsten weiter.

Zeile 14

Auch hier brauchen wir eigentlich nicht zu tracen. Macht es dennoch einmal. Im Akku sollte der Wert $88 stehen. Könnt ihr euch erklären, weshalb?
Sobald ihr auf diese Zeile stosst, sollte euch auffallen, dass wir das falsche Label verwenden. Unsere Zeichen stehen bei l11 und nicht l1. Um ähnliche Fehler zu vermeiden, empfiehlt es sich sprechende Labelnamen, statt so kryptischer wie hier zu nehmen.
Seht ihr nun, warum $88 im Akku steht? Da wir noch nichts korrigiert haben, addieren wir aktuell noch l1+2. Dort steht das dey. Ein Blick auf die Mnemonics verrät euch, dass dieses den OpCode $88 hat.

Ändert jetzt Zeile 14…

Zeile 16

Wir verzichten vorerst aufs Tracen. Werft einen Blick auf die Zeilen 13, 16 und 17. Fällt euch etwas auf? Beachtet auch die Kommentare. Davon gibt es hier zwar übertrieben viele, sie können aber bei der Fehlersuche durchaus hilfreich sein. Wir schreiben in Zeile 13, dass wir den Schleifenzähler mit 40 füllen und verwenden das X-Register. Hier in Zeile 16 und in der nächsten nehmen wir aber das Y-Register!

Wir müssen also nochmal Zeile 13 korrigieren und auch dort das Y-Register verwenden.

Lasst das Programm jetzt mal normal laufen. Mist, es ist immer noch nichts zu sehen – gibt es wirklich keine Änderung?

Es tut sich etwas.

Doch oben rechts in der Ecke hat sich etwas geändert.

Da es aber immer noch nicht das gewünschte Ergebnis ist, machen wir erstmal weiter.

Zeile 18

Löscht den bisherigen Breakpunkt und setzt einen auf Zeile 16 von eben. Beginnt dann zu tracen. Wir wollen jetzt mal unsere Schleife durchgehen. Führt einfach die Zeilen 16 bis 18 aus.

Die Schleife endet zu früh.

Ooops, wir fliegen schon nach einem Durchlauf raus. Beim Tracen könnt ihr beobachten, wie sich nach dem dey der Inhalt des Y-Registers von 40 auf 39 ändert. Anschließend prüfen wir mit beq, ob das Schleifenende erreicht ist. Wir prüfen also, ob etwas gleich ist. Aber wo ist die Prüfung bzw. womit vergleichen wir? Wie wir gelernt haben, kontrolliert beq, ob das Z-Flag = 1 ist. Ein Blick auf die Register zeigt, dass dies nicht der Fall ist, daher wird auch nicht gesprungen. Das Zero-Flag wird immer gesetzt, wenn die letzte Operation 0 war. Vor dem beq wurde dey ausgeführt und das hat, wie gesehen, zu 39 im Y-Register geführt. Sobald das Y-Register 0 wird, würde auch das Zero-Flag gesetzt werden. Wir brauchen also keine extra Prüfung, wir müssen nur unsere Sprungbedingung ändern. Welche wäre angebracht? Ihr müsst das Verhalten nur umkehren.

Ändert Zeile 18 in…

Startet das Programm normal und ihr werdet sehen, dass nun die ganze erste Zeile mit einer Linie gefüllt ist. Dies ist schwer zu erkennen, stellt zur Not einfach mal den Cursor in die erste Zeile.

Wo sind unsere Buchstaben?
Zeile 21

Der letzte Fehler ist nun evtl. etwas schwer zu verstehen. Mit der !byte-Zeile wollen wir uns die Buchstaben A, B und C als Text im Speicher ablegen. Und dann das C für die Ausgabe verwendet. Offensichtlich klappt es nicht wirklich. Traced nochmal Zeile 14 und schaut, welcher Wert im Akku landet. Es sollte $63 bzw. 99 sein. Schaut ihr im PETSCII-Zeichensatz nach, dann seht dort für $63 tatsächlich das C. Allerdings wird im Artikel ebenfalls erwähnt, warum das hier evtl. nicht klappt.
Falls ihr jetzt denkt, klar – da muss !text statt !byte stehen, probiert es aus und seht, dass es nichts ändert. Wir geben die Zeichen nicht über eine Kernal-Routine aus, sondern schreiben direkt in den Bildschirmspeicher. Daher müssen wir die Zeichen aus dem Char-ROM nehmen! Schaut ihr dort nach, entdeckt ihr, dass an Stelle 99 unser aktuell auf dem Bildschirm ausgegebenes Zeichen zufinden ist. Wie bringen wir jetzt das C64 Studio dazu, den richtigen Code für das C zuspeichern? Geben wir Zeichen, direkt im Bildschirmspeicher (engl. Screen-RAM) aus, dann sollten wir !scr, statt !byte oder !text verwenden.

Ändert abschließend also Zeile 21 in:

Damit ist es vollbracht und unser Programm läuft, wie vorgesehen!

Ein Fehler ist uns übrigens durchgerutscht! Diesen entdecken wir gleich, sobald wir das Programm mit VICE untersuchen. Daher solltet ihr euch auch diesen Abschnitt einmal ansehen. Es kann außerdem nichts schaden, eine weitere Möglichkeiten zur Fehlersuche zu entdecken.
Ansonsten, springt direkt zum Schluß.

Tracen mit VICE

Ohne C64 Studio, empfehle ich euch, die Fehlersuche per Emulator. Da im Emulator alles simuliert abläuft, habt ihr in der Regel direkten Zugriff auf alle Speicherstellen und Register. Ich zeige hier mal mit WinVICE, wie man ein Programm untersuchen kann.

Ich gehe davon aus, dass ihr oben die Fehler beseitigt habt, die euch der Assembler ausgeworfen hat und nun das Programm läuft, aber nichts anzeigt.

Der Monitor

VICE bietet uns mit dem Montior die Möglichkeit, Speicherstellen und Register einzusehen, sowie Änderungen am Programm vorzunehmen. Um den Monitor zu öffnen, wählt ihn im Menü aus oder drückt die entsprechende Tastenkombination, hier ALT+M.

Den Monitor starten.

Darauf hin öffnet sich ein neues Fenster mit dem VICE Monitor.

Disassemblieren

Sollte das Monitorfenster noch leer sein, öffnet das Konsolenfenster. Im Konsolenfenster könnt ihr Befehle eingeben, die jeweils mit ENTER bestätigt werden müssen. Wir wollen auch direkt den ersten Befehl verwenden und einen Blick auf unser Programm werfen.

Gebt dazu im Konsolenfenster d 801 ein.

Konsolenfenster öffnen und unser Programm im Speicher anzeigen.

Mit d 801 (der Monitor interpretiert alle Zahlen als hexadezimal) lassen wir uns den Speicherbereich ab $0801 disassembliert anzeigen, d. h. der Monitor interpretiert die Bytes ab der angegebenen Adresse als Programm und zeigt die entsprechenden Mnemonics an.

Ihr solltet folgende Anzeige erhalten:

Werfen wir mal einem Blick auf die erste Zeile:

.C: zeigt an, dass ihr euch den Computer anschaut. Ihr könnt mit dem Monitor nämlich auch andere Geräte beauskunften, z. B. die Floppy.
0801 ist die Startadresse der Zeile. Hier unser Programmstart, den wir ja mit *=$0801 festgelegt haben.
0C 08 E2 Es folgen max. drei Bytes, die ab der Zeilenstartadresse gefunden wurde. Es sind max. drei Bytes, da dies auch die max. Länge eines Befehls beim C64 ist.
NOOP $E208 abschließend folgt das gefundene Mnemonic. Wundert euch nicht über die Anzeige, hier wird mit $0c ein illigaler OpCode gefunden. Ich will jetzt nicht ins Detail gehen, warum die Anzeige ist, wie sie ist, nehmt es einfach hin.

Das Monitorfenster könnt ihr, wie jedes andere Fenster schließen oder durch die Eingabe von x im Kommandofenster. Wenn ihr es erneut öffnet, bleiben eure letzten Ausgaben erhalten. Schließt ihr VICE komplett, dann sind diese natürlich futsch.

Zeile 6

Der Anfang unseres Programm sieht im Monitor ja echt merkwürdig aus, oder?
Denkt daran, dass wir eine BASIC-Startzeile verwenden und wenn ihr die Anzeige von 0801 - 080d damit vergleicht, sollte euch auffallen, dass diese, bis auf ein Byte, identisch sind.

Wir haben uns bei der Adresse für den nächsten BASIC-Befehl verrechnet, $080b ist falsch! Dieser Fehler wurde glücklicherweise beim Laden vom C64 automatisch korrigiert und hat somit keine Auswirkung. Die korrekte Adresse lautet $080c. Wie ihr seht, kann der Monitor uns das denken nicht abnehmen. Wir müssen die Anzeige richtig interpretieren. Im obigen Abschnitt sehen wir also nicht die angezeigten Assemblerbefehle, sondern eine BASIC-Zeile. Die Bytes werden vom BASIC-Interpreter ausgewertet und haben daher eine ganz andere Bedeutung. Davon kann der Monitor aber nichts wissen.

Auch wenn unser Fehler ohne Folgen blieb, sollten wir ihn korrigieren:

Zeile 9 & 11

Da wir uns gerade so schön unser Programm im Monitor ansehen, schauen wir doch mal etwas genauer hin. Werfen wir direkt einen Blick auf die erste Anweisung in unserem Quellcode: lda #>SCREENRAM ;LSB in den Akku. Im Monitor sehen wir normalerweise keine Label- oder Variablennamen. Man kann diese zwar an VICE übermitteln, dazu muss der Assembler diese exportieren können, aber das ist hier nicht der Fall. Wir müssen also ohne auskommen. Auch die Kommentare fehlen uns, daher ist es immer gut, wenn man den Quellcode mit der disassemblierten Anzeige im Monitor vergleicht.

Schauen wir uns also mal die erste Assemblerzeile (Zeilen-Nr. 9) genauer an.

Im Monitor finden wir den ersten Befehl direkt hinter unserem BASIC-Start, also ab $080e.

Wie wir am Kommentar erkennen, wollen wir das LSB der Adresse, die in SCREENRAM gespeichert ist (hier $03ff), in den Akku laden. Im Monitor entdecken wir aber ein lda #$03.

Werft jetzt im Monitor auch noch einen Blick auf auf die übernächste Zeile, in der wir das MSB laden wollen.

Dort findet ihr ein lda #$ff! Die Bytes $03 und $ff ergeben ja unsere Adresse $03ff, wir haben uns aber bei der LSB / MSB Reihenfolge vertan. $ff ist das LSB und $03 das MSB!

Ändert also die betroffenen Zeilen:

Schließt jetzt das Monitorfenster mit x und anschließend VICE. Erstellt dann das geänderte Programm, startet es wieder mit VICE und lasst es euch im Monitor anzeigen.

Jetzt sollten LSB und MSB korrekt geladen werden:

Breakpoints

Um ein Programm schrittweise abzuarbeiten, benötigen wir erstmal einen Breakpoint (Haltepunkt). Damit legen wir fest, an welcher Stelle wir uns unser Programm per Einzelschritt, genauer ansehen möchten.

Ihr solltet euch aktuell im Konsolenfenster des VICE-Monitors befinden. Wir möchten jetzt unser Programm ab der ersten Assemblerzeile lda #>SCREENRAM ;LSB in den Akku schrittweise untersuchen. Also brauchen wir einen Breakpoint für die Adresse $080e.

Gebt break 80e ein, um einen Breakpoint zu setzen. Ein einzelnes break oder kürzer bk, listet euch alle aktiven Breakpoints auf. Um einen Breakpoint wieder zu löschen, benötigt ihr den delete-Befehl und die laufende Nr. aus der Liste der Breakpoints, also z. B. delete 1. Auch dies geht kürzer per del.

Breakpoints setzen und löschen

Ihr solltet jetzt einen Breakpoint auf der Adresse $080e haben!

Sobald VICE auf einen Breakpoint stößt, wird das Programm angehalten und es öffnet sich automatisch der Monitor.

Schließt jetzt den Monitor und startet unser Programm dann einfach per RUN. Es öffnet sich direkt wieder der Monitor, aber die Anzeige ist etwas anders.

Der Breakpoint wurde erreicht.

Für uns sind jetzt nur die letzten Zeilen von Interesse:

#1 ist die Nummer des Breakpoints, die wir auch nach break, in der Liste der Breakpoints finden.
(Stop on exec 080e) hier seht ihr warum Stop on exec und wo 080e angehalten wurde.
298 ist die Rasterzeile in der wir uns befinden, im Monitor auch häufig mit LIN bezeichnet.
039 zeigt an, wie viele Taktzyklen CYC in der aktuellen Rasterzeile vergangen sind.
Diese beiden Werte werden bei euch abweichen, sie änderen sich mit jedem Start. Ihren Sinn versteht ihr, sobald ihr euch mit dem Rasterzeileninterrupt beschäftigt.

Den Anfang der Zeile .C:080e A9 FF LDA #$FF kennen wir ja schon, neu ist der Rest dahinter.

A: Akkuinhalt
X: Inhalt des X-Registers
Y: Inhalt des Y-Registers
SP: Adresse, auf die der Stackpointer zeigt
N.-B.... gesetzte Flags im Statusregister, im üblichen Format NV-BDIZC
8628141 Sobald VICE startet, beginnt eine Stoppuhr zu laufen. Hier könnt ihr ablesen, wieviele Taktzyklen bisher vergangen sind. Daher steht hier auch immer etwas anderes.

Denkt immer daran, dass der akutell angezeigte Befehl (hier das lda #$ff) noch nicht ausgeführt wurde! Die Register zeigen also den Zustand vor der Ausführung des Befehls!

Dies ist jetzt wieder unsere Befehlszeile, in der wir neue Eingaben tätigen können. Wie ihr seht, steht vorne die Adresse unseres Breakpoints.

Möchtet ihr die Register etwas hübscher angezeigt bekommen oder sie erneut anzeigen lassen, dann gebt einfach den Befehl registers oder kürzer r ein:

So, wir wollen jetzt nur die aktuelle Zeile mit dem lda #>SCREENRAM bzw. im Monitor lda #$ff ausführen. Gebt dazu den Monitorbefehl step oder z ein.

Nach dem step hat sich die Registeranzeige geändert. Wie ihr seht, befindet sich nun $ff im Akku. Der nächste Befehl wäre das Speichern auf der Zeropage sta $fb. Gebt jetzt erneut step ein, um auch den nächsten Befehl auszuführen.

Es wird die nächste Zeile lda #$03 angezeigt. Die Register haben sich durch das sta $fb natürlich nicht geändert. Lasst uns dann mit m fb kontrollieren, was in der Zeropage steht.

Steppen und Memorydump.

Mit mem oder m könnt ihr euch den Inhalt des Speichers ab der angegebenen Adresse als Dump anzeigen lassen. Für uns ist die Adresse $fb auf der Zeropage interessant. Dort hat das sta $fb ja den Inhalt des Akkus abgelegt.

Wie ihr seht, steht direkt in der ersten Zeile >C:00fb ff, im ersten Byte unser $ff.

Zeile 12

Führt nun auch die nächsten beiden Schritte lda #$03 und sta $fb aus, dabei sollte euch jetzt auffallen, dass wir einen Fehler im Programm haben. Falls ihr es nicht gesehen habt, gebt nochmal m fb ein.
An der Adress $fb steht jetzt nur noch ein $03!

Wir haben hier also fälschlich zweimal etwas an der selben Adresse gespeichert.

Wir müssen unseren Source ändern, das MSB muss natürlich in die nächste Speicherstelle…

Zeile 13

Erstellt das Programm dann neu und öffnet es wieder mit VICE. Setzt den Breakpoint dieses Mal auf die obige Zeile. Startet dann das Programm und schaut euch nochmal an, was in der Zeropage an der Adresse $fb steht.

Jetzt stimmt unsere Adresse auf der Zeropage: >C:00fb ff 03.

Gebt nun step ein und kontrolliert das X-Register.

Im X-Register sollte eigentlich 40 stehen, da wir die Schleife so oft ausführen wollen. Wir haben hier schon wieder einen Tippfehler. Mit dem aktuellen Befehl, laden den Inhalt der Speicherstelle $40 und nicht die Zahl 40 ins X-Register.

Ändert daher Zeile 13 in…

Diese Zeile wird uns gleich nochmal beschäftigen, wir machen aber erstmal weiter…

Zeile 14

Dieser Fehler ist ein recht fieser. Wenn er euch nicht schon beim Kodieren aufgefallen ist, ist er nicht so einfach zufinden. Im VICE Monitor seht ihr in als lda $081d. Schaut doch mal mit d 81d, was sich dort befindet und vergleicht es mit unserem Quellcode.

Dort steht also das dey unserer Schleife. Wie wir beim Vergleich mit dem Sourcecode sehen, kommen unsere Zeichen aber direkt hinter dem rts, also ab Adresse $0821. Wir haben also das falsche Label verwendet. Unsere Zeichen stehen bei l11 und nicht l1. Um ähnliche Fehler zu vermeiden, empfiehlt es sich sprechende Labelnamen, statt so kryptischer wie hier, zu nehmen.

Ändert Zeile 14 in…

Zeile 16 oder doch 13?

Wir verzichten vorerst aufs Tracen. Bei Problemen empfiehlt es sich immer einfach nochmal einen genauen Blick auf den Quellcode zu werfen. Schaut euch daher die Zeilen 13, 16 und 17 nochmal ganz genau an. Fällt euch etwas auf? Beachtet auch die Kommentare. Davon gibt es hier zwar übertrieben viele, sie können aber bei der Fehlersuche durchaus hilfreich sein.

Wir schreiben in Zeile 13, dass wir den Schleifenzähler mit 40 füllen und verwenden das X-Register. Hier in Zeile 16 und in der nächsten nehmen wir aber das Y-Register!

Wir müssen also nochmal Zeile 13 korrigieren und auch dort das Y-Register verwenden.

Wenn ihr alle Korrekturen, bis hier vorgenommen habt und das Programm frisch erstellt, dann sollte euch bei einem erneuten Start endlich eine erste Änderung auffallen.

Es tut sich etwas.

Wir sind zwar noch nicht am Ziel, haben aber endlich eine Ausgabe.

Zeile 18

Wir wollen jetzt ein letztes Mal tracen. Ich solltet die Ausgabe, wie auf dem Bild oben sehen. Legt euch dann im Monitor einen Breakpoint für die Zeile sta (ZP_COPYPOS),y ;Zeichen ausgeben an.
Startet das Programm und stept durch die Schleife.
Wir haben hier nur einen Schleifendurchlauf und landen direkt beim rts.

Bei .C:081b schreiben wir unser Zeichen in den Bildschirmspeicher, das sorgt für die im Bild weiter oben rot markierte Änderungen. Dann verringern wir mit dey unseren Schleifenzähler im Y-Register. Dies seht ihr auch im Monitor Y: ändert sich von $28 auf $27, denkt daran dass dort Hex-Werte stehen, dezimal sind es 40 und 39.
Anschließend prüfen wir mit beq, ob das Schleifenende erreicht ist. Wir prüfen also, ob etwas gleich ist. Aber wo ist die Prüfung bzw. womit vergleichen wir?
Wie wir gelernt haben, kontrolliert beq, ob das Z-Flag = 1 ist. Ein Blick auf die Register zeigt, dass dies nicht der Fall ist, daher wird auch nicht gesprungen. Das Zero-Flag wird immer gesetzt, wenn die letzte Operation 0 war. Vor dem beq wurde dey ausgeführt und das hat, wie gesehen, zu $27 im Y-Register geführt. Sobald das Y-Register 0 wird, würde auch das Zero-Flag gesetzt werden. Wir brauchen also keine extra Prüfung, wir müssen nur unsere Sprungbedingung ändern.

Welche wäre angebracht? Ihr müsst das Verhalten nur umkehren.

Ändert Zeile 18 in…

Startet das Programm nach der Korrektur und ihr werdet sehen, dass nun die ganze erste Zeile mit einer Linie gefüllt ist. Dies ist schwer zu erkennen, stellt zur Not einfach mal den Cursor in die erste Zeile.

Wo sind unsere Buchstaben?
Zeile 21

Der letzte Fehler ist nun evtl. etwas schwer zu verstehen. Mit der !byte-Zeile wollen wir uns die Buchstaben A, B und C als Text im Speicher ablegen. Und dann das C für die Ausgabe verwendet. Offensichtlich klappt es nicht wirklich. Traced nochmal Zeile 14 und schaut, welcher Wert im Akku landet. Es sollte $63 bzw. 99 sein. Schaut ihr im PETSCII-Zeichensatz nach, dann seht dort für $63 tatsächlich das C. Allerdings wird im Artikel ebenfalls erwähnt, warum das hier evtl. nicht klappt.
Falls ihr jetzt denkt, klar – da muss !text statt !byte stehen, probiert es aus und seht, dass es nichts ändert. Wir geben die Zeichen nicht über eine Kernal-Routine aus, sondern schreiben direkt in den Bildschirmspeicher. Daher müssen wir die Zeichen aus dem Char-ROM nehmen! Schaut ihr dort nach, entdeckt ihr, dass an Stelle 99 unser aktuell auf dem Bildschirm ausgegebenes Zeichen zufinden ist. Wie bringen wir jetzt ACME dazu, den richtigen Code für das C zuspeichern? Geben wir Zeichen direkt im Bildschirmspeicher (engl. Screen-RAM) aus, dann sollten wir !scr, statt !byte oder !text verwenden.

Ändert abschließend also Zeile 21 in:

Damit läuft unser Programm endlich.

Seit ihr auf den Geschmack gekommen und möchtet mehr mit dem VICE Monitor machen, dann schaut euch einmal Ein kleiner Crack an.

Der Vorhang fällt

Hier konntet ihr nun sehen, wie man Fehler aufspüren kann. Sobald ihr mit Interrupts anfangt steigt die Fehlergefahr erheblich an. Diese sind dann auch nicht mehr so einfach wie die obigen zu finden. Auch Logikfehler sind manchmal schwer aufzuspüren.

Um solchen Fehlern vorzubeugen und die Fehlersuche zu erleichtern, solltet ihr versuchen das Programm möglichst strukturiert und vernünftig kommentiert einzugeben. Gebt ihr die Programme auf einem PC ein, dann stellt dies kein Problem dar. Direkt auf dem C64 müsst ihr natürlich mit dem verfügbaren Speicher sparsam umgehen. Auch die Verwendung von Variablen und Labeln ist hilfreich, allerdings sollten die Namen aussagekräftig und eindeutig sein. Ansonsten gilt auch hier, üben, üben, üben…


Damit schließe ich das Assembler Grundlagen Tutorial und hätte noch eine Bitte:

Eure Meinung ist mir sehr wichtig!
Es wäre daher schön, wenn ihr die anonyme Bewertungsmöglichkeit bzw. das ebenfalls anonym verwendbare Feedback (s. links) benutzt um mir zu helfen in Zukunft bessere Texte zu fabrizieren.
Bewertet die Beiträge oder teilt mir eure Meinungen und Wünsche in einem kleinen Text mit. Ein evtl. berechtigtes „Schrott!!“ hilft dabei natürlich nicht soviel, wie ein kurzer Text, warum ihr das so empfindet.

Ich danke für euer Interesse und hoffe ihr schaut demnächst wieder vorbei.
Dann werden wir unsere Kenntnisse durch ein komplettes Programm in einem neuen Tutorial vertiefen.

Stay tuned


Schrott!!Naja...Geht so...Ganz gut...SUPER! (11 Bewertungen | Ø 4,73 von 5 | 94,55%)

Loading...


ZurückWeiter

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Protected by WP Anti Spam