Beiträge von Mike im Thema „Styleguide für Assembler“

    Ist das verständlich?

    Evtl. hattest Du mit dem Programmfragment mal die Absicht, eine Basen-Konversion (je nach Einsprungpunkt für binär, oktal, dezimal und hexadezimal) zu fahren, die Division als wiederholte Subtraktion ausgeführt.

    Allerdings funktioniert der Code oben sicher nicht so, wie geschrieben. Die Subtraktion ist nicht kommutativ. Vermutlich fehlt auch noch ein "sta zp" am Ende, trotzdem würde es nicht funktionieren. Ich stell' mal ganz keck die Frage: ist der Code aus einem bestehenden Programm oder hattest Du dir den grad aus den Fingern gesogen?

    das clc von neulich gehört gefälligst _nicht_ an den Anfang, auch wenn es eine hübsche Einrückung erzeugt.

    Um irgendeine Einrückung geht's mir da überhaupt nicht.

    Ich sehe die zwei Schritte (also: jeweils LDA/ADC/STA) der 16-Bit-Addition als eine Einheit an, die systematisch auch zur 32-Bit-Addition ausgebaut werden könnte. Damit bereitet das CLC den Gesamtprozeß vor und wird vor die gesamte Anordnung gestellt. Daß das alles über den Akku läuft, ist mir fast egal. Der Akku ist für mich da nur Erfüllungsgehilfe. Ich hab' da eher die Sicht der Zeropage als 256 8-Bit-Register, 128 16-Bit-Register oder 64 32-Bit-Register.

    Wenn ich also jede Zeile als langes Befehlswort sehe und deine enge Bindung des CLC an das erste ADC honorieren wollte, müßte das wie folgt aussehen:

    Code
    LDA zp  :CLC:ADC #lo:STA zp
    LDA zp+1:NOP:ADC #hi:STA zp+1

    oder

    Code
    LDA zp  :CLC:ADC #lo:STA zp
    LDA zp+1:   :ADC #hi:STA zp+1

    Und da sag' ich ganz einfach mal: Nö.

    Wenn ich richtig loslege, kommt auch schon mal so ein Code raus (ist für VC-20):

    Diese Routine schreibt eine 40 Zeichen lange Zeichenkette als ganzes in eine 160x192 Bitmap. Die Zeichen sind 4x8 Pixel groß, damit das aufgeht. Damit nicht zuerst das linke Zeichen gedruckt wird (wobei die Nibbles der rechten Hälfte erhalten werden müßten) und dann erst das rechte Zeichen (wobei die Nibbles der linken Hälfte erhalten werden müßten...!), druckt die Routine in der Schleife "TDraw_01" immer zwei Zeichen auf einmal! In "TCharset" stehen die Zeichendefinitionen so drin, daß die Zeichen im rechten und linken Nibble enthalten sind. So entfällt an dieser Stelle aufwendiges Shiften.

    Der größere Teil der "TDraw_00"-Schleife betreibt Adreß-Arithmetik um die Basis-Pointer "text_left" und "text_right" in den Zeichensatz zu berechnen. Ein Byte wird beim Shiften im Akku gehalten, das braucht dann nur 2 Zyklen statt 5 auf der Zeropage.

    Dadurch, daß die Routine immer gleich eine ganze Textzeile printet, müssen sich die Daten nicht durch 1-Zeichen-Programmierinterface (wie eben bei CHROUT) quetschen. Sie schafft fast 6000 Zeichen/Sekunde und wird in einem scrollenden ASCII-Text-Viewer eingesetzt!

    Code
    clc
    lda	var
    adc	#xx
    sta	var
    bcc	.skip
    inc	var + 1
    .skip:

    Das ist eine übliche Variante, um bei Konstanten zwischen 0..255 die Addition zu verkürzen und zu beschleunigen.

    Also, der hintere Teil der Routine (BCC/INC) braucht 3 Zyklen, wenn kein Übertrag war, und sonst 2+6 = 8 Zyklen.

    In Fällen, wo nicht unbedingt klar ist, ob immer eine 8-Bit-Konstante ausreicht, oder wenn z.B. nicht erwünscht ist, daß der Code *unterschiedliche* Laufzeiten hat, bin ich mir auch nicht zu schade, das so anzuschreiben:

    Code
    CLC
    LDA var  :ADC #xx:STA var
    LDA var+1:ADC #0 :STA var+1

    Spart außerdem noch eins der "generischen" Labels (hier: "skip") und natürlich läßt sich das Konstrukt für eine 32-Bit-Addition verallgemeinern. Und da sieht man dann auch, daß das CLC *gefälligst* vor die gesamte Gruppe gehört und nicht direkt vor das erste ADC (also nicht: LDA var:CLC:ADC Bitte melde dich an, um diesen Link zu sehen.:STA var [...]!)

    Mit Ausnahme der Einblendungen des Char-ROMs bei $1000..$1FFF und $9000..$9FFF sieht der VIC-II nur RAM, egal was im Portregister des 6510 steht.

    Heißt, der VIC-II kann mühelos auf das RAM bei $D000..$DFFF "unter" I/O und Char-ROM zugreifen. Tatsächlich sieht er gar nicht die "Kopie" des Char-ROMs bei $D000 - die existiert so nur für die CPU. Die Zuordnung wird so von der PLA realisiert.

    (Zugriff auf das Farb-RAM ist noch eine andere Sache - das wird parallel mit einem eigenen 4-Bit-Datenbus angesprochen und liegt aus Sicht des VIC-II *überall* - teilt sich aber die unteren 10 Bit der Adresse mit dem adressierten Screen-RAM).

    Da braut jeder schon seit Jahren sein eigenes Süppchen ganz nach Geschmack.

    Der verwendete Assembler hat da noch den größten Einfluß, gerade wenn es um so Details wie Formelauswertung für Adressen, bedingte Assemblierung, Makros und Schleifen geht.

    Die Sprache selber gibt das nicht her - Struktur mußt Du da selbst reinbringen. Wichtig ist eine gute Aufteilung des Programms in Unterroutinen und ein konsistenter Stil bei der Auswahl der Labelnamen.

    Gerade, was letzteres angeht, bin ich dabei hängen geblieben, Labels in Unterroutinen durch einen angehängten Unterstrich plus zweistellige Nummer zu versehen. "Generische" Label wie "skip" oder "loop" meide ich wie die Pest. Typischer Code (ohne "Boilerplate" drumherum) sieht bei mir so aus:


    Der Assembler, den ich verwende, läßt auch mehrere Befehle in einer Zeile zu. Das kommt durchaus auch zum Einsatz, wenn die dadurch entstehende Befehlsgruppe ein klares Ziel hat (etwa "Datensammeln und am Ende der Zeile wegspeichern.") - erstreckt sich das dann noch über mehrere Zeilen, sieht so ein Programmteil manchmal einem Kristall nicht unähnlich. :D