Hello, Guest the thread was called4.4k times and contains 27 replays

last post from JeeK at the

Dividieren in Assembler

  • Und kurz ontopic:


    Die schnellste mir bekannte Methode zur genauen 16/8-Division sieht ungefähr so aus (nicht geprüft, aber sollte funktionieren):

  • Und kurz ontopic:


    Die schnellste mir bekannte Methode zur genauen 16/8-Division sieht ungefähr so aus (nicht geprüft, aber sollte funktionieren):

    Ja, schaut gut aus. Sieht für mich auch so aus, dass es funktionieren sollte. Ist halt die korrigierte Routine von vorhin nur "unrolled".

  • Was bedeutet [..] im Code? Funktioniert das etwa?

    Das sind nur Auslassungszeichen, die Codeteile wiederholen sich einfach. Es gibt zwei Varianten, vor Label ?29 ohne Bit-9-Berücksichtigung und ab Label ?29 mit Bit-9-Berücksichtigung (da ist nach dem ROL noch ein BCS vor dem CMP mit dem Divisor und nach dem Abziehen des Divisors muss das Carry explizit gesetzt werden mit SEC, da das SBC nicht immer ein gesetztes Carry hinterlässt - nämlich dann nicht, wenn es vom zuvor besagten extra BCS angesprungen wird).

    - jeweils 8 Stück in jedem dieser Teile (der Labelnummerierung folgend).

  • Mit dieser 16u/8u-Routine von den beiden wird man eher nicht glücklich, denn sie einen Bug.

    Sehe ich mir später genau an, aber schon mal vielen Dank für die Korrektur!

    Bitte gerne.

    Ha, gleich im ersten Satz selber einen Fehler ("hat" fehlt) ... wohl zu wenig Schlaf. ||


    Übrigens die 16/16-Routine ist davon nicht betroffen, was daran liegt, dass der Remainder (Rest) mit 0 initialisiert ist und gleich groß wie der Nominator (Divisor) ist. D.h. es kann der Fall nicht eintreten, dass der Remainder gerade noch kleiner als der Denominator (Divisor) und erst ins 17. Bit geschoben werden müsste, damit der Divisor abgezogen werden kann. Da der Dividend ja nur 16 Bit hat, kommt es dazu erst gar nicht. Bei 16/8 hingegen werden 16 Bits des Dividenden durch den Remainder geschoben, wo das (frühestens nach 8 Schiebungen) passieren kann. Deswegen kann man in der Unrolled-Variante von M.J. das auch entsprechend berücksichtigen und wird dort optimiert (ersten 8 Schritte ohne, die nächsten 8 Schritte mit Bit-9-Berücksichtigung).

  • Sorry fürs offtopic.:/

    Da ich immer gerne die Implementierungen Vergleiche, kenn ich auch die eine oder andere Implementierung von 6800, 68K. Von Z80 oder 8080 eher nur die Grundlagen (also wie dort der innere Interpreter aufgebaut ist).

    Kennst Du zufällig einen guten, kompakten und ausreichend kommentierten Sourcecode für einen 68000-Forth-Interpreter? Sowas könnte ich für meine CPU als Grundlage gut gebrauchen (68000 kommt dieser am nächsten).

    Also ich hab noch aus den 80ern eine Fotokopie einer 68000/8-Implementierung, allerdings in einer Architektur mit 16-Bit-Wortbreite. Also etwas Kompaktes, das nicht mit 32-Bit-Ungetümen herumtun muss (was aber auch dazu führt, dass der Adressraum limitiert ist). Hab's gerade nicht in Griffnähe, aber es könnte die Uralt-FIG-Implementierung sein ... leider etwas sperrig, weil nicht für einen "normalen" Assembler, sondern einer eigenen Forth-Assembler-Syntax. D.h. man muss auf einem bestehenden Forth den Assembler (möglicherweise für 68K ein Quasi-Standard?) auftreiben und cross-assemblieren. Sonst kann ich leider nichts bieten (mir hat das immer fürs Vergleichen gereicht). ;)
    So jetzt reichts vermutlich mit dem Off-Topic-sein, gegebenenfalls machen wir einen separaten Thread woanders auf ...

  • Behebung:



    Beim Verschieben des Remainders muss das Carry-Flag überprüft werden. Dann ist der Remainder-Wert auf jeden Fall größer und kann ohne Vergleich direkt um den Denominator reduziert werden.
    Die Subtraktion im Remainder passiert dann nicht vor der Denominatorabfrage, sondern nachher, damit dies in beiden Fällen verwendet werden kann.
    Aber Achtung: je nach Einsprung, ist hier nicht mehr sichergestellt, dass das Carry-Flag 1 ist. Daher nach dem SBC das überflüssig aussehende SEC. Im Überlauffall, wenn also von einem 9-Bit-Wert subtrahiert werden müsste, kann man auf das 9. Bit allerdings verzichten und die Subtraktion einfach bei 8 Bit belassen, da ja das Ergebnis immer kleiner $100 wird. In diesem Fall ist allerdings dann auch das Carry-Flag gelöscht (d.h. von dieser Annahme kann im folgenden nicht mehr ausgegangen werden).


    Nebenbei wurde auch insofern optimiert, dass der Remainder nicht relativ aufwendig im X-Register gehalten wird, nur weil man den Wert der Subtraktion erhalten will. Da ist es billiger, gleich den Akku zu nehmen und die Denominator-Subtraktion nur mit einem CMP zu machen, ohne den Akku zu ändern und dann erst mit einem SBC den Remainder zu bereichnen. Das ist in allen Fällen schneller, da man im Hauptpfad 3 Befehle (6 Zyklen) spart und das zusätzliche SBC (bei der Zeropage) 3 Zyklen kostet.

    Man spart sogar ein Byte des Codes ein, obwohl die Sonderbehandlung hinzu gekommen ist.

    Danke nochmal. Soweit ich mich erinnere, habe ich damals(tm) erst die 16=16/16-Bit-Variante geschrieben, und die 16=16/8-Bit-Variante entstand dann als "Optimierung". Sonderlich viel scheine ich aber wohl nicht darüber nachgedacht zu haben - ich meine, der Bug ist zwar ärgerlich, aber die blöde Registernutzung (X statt A) stört mich irgendwie noch mehr. ;)

  • Was bedeutet [..] im Code? Funktioniert das etwa?

    Ergänzend: Die Unrolled-Variante von MJ hab ich als ACME-Source auch probiert und sie funktioniert wie dargelegt. :thumbsup: