|
|
|
Startseite News   NDR-NKC Geräte Z80 Geräte 68000 Geräte 8088NKC Emulator Z80 Section Baugruppen ROM's Software68000 Section Baugruppen ROM's PASCAL/S Software CP/M 68K8088 Section Baugruppen DownloadsBussysteme Stromversorgung Input / Output Grafikkarten Speicherkarten Massenspeicher Weitere Baugruppen Projekte Dokumentation Datenblätter Glossar Portraits Links Impressum |
64-Bit Integer-ArithmetikEin etwas umfangreicherer Beitrag, der das Rechnen mit großen Zahlen auf dem NKC möglich macht. Das Projekt besteht aus einer Assembler-Bibliothek, in der die Rechenoperationen umgesetzt sind und aus einer Header-Datei, in der die Datenstruktur und Funktionen zur Ein- und Ausgabe umgesetzt sind. Da es in C keinen nativen Datentyp für 64-Bit Zahlen gibt, werden alle Parameter mittels Pointern auf die Datenstruktur an die Funktionen übergeben.
Die DatenstrukturDie 64 Bit umfassenden Werte werden in zwei Teile zu je 32 Bit aufgeteilt. Dazu wird ein eigener Datentyp U64 definiert.
Enthaltene FunktionenIn der Assembler-Bibliothek sind neben der 4 Grundrechenarten Addition, Subtraktion, Multiplikation, Division mit Rest noch Funktionen zum bitweisen Verschieben sowie Hilfsfunktionen umgesetzt Alles Weitere kann aus den Kommentaren oder den anschließenden Beispielen entnommen werden. MAKE64Erstellt ein U64 aus einer link-Variablen oder Konstanten. .globl _make64 _make64: link a6,#0 move.l 12(a6),a1 ; pointer auf &a move.l #0,(a1) ; a.hi = 0 move.l 8(a6),4(a1) ; a.lo = long unlk a6 rts DUP64Erstellt eine Kopie einer U64-Variablen. .globl _dup64 _dup64: link a6,#0 move.l 8(a6),a0 ; pointer auf &a move.l 12(a6),a1 ; pointer auf &r move.l (a0),(a1) ; a.hi -> r.hi move.l 4(a0),4(a1) ; a.lo -> r.lo unlk a6 rts ADD64Addiert zwei U64-Variablen. Das Ergebnis wird in einer dritten U64-Variable abgelegt. .globl _add64 _add64: link a6,#0 move.l 8(a6),a0 ; pointer auf &a move.l 12(a6),a1 ; pointer auf &b move.l 16(a6),a2 ; pointer auf &r move.l 4(a0),d0 ; D0 = a.lo move.l (a0),d1 ; D1 = a.hi add.l 4(a1),d0 ; D0 = a.lo + b.lo move.l (a1),d2 ; D2 = b.hi addx.l d2,d1 ; D1 = a.hi + b.hi + extend move.l d0,4(a2) ; r.lo = D0 move.l d1,(a2) ; r.hi = D1 unlk a6 rts SUB64Subtrahiert die zweite U64 von der ersten U64-Variablen. Das Ergebnis wird in einer dritten U64-Variable abgelegt. .globl _sub64 _sub64: link a6,#0 move.l 8(a6),a0 ; pointer auf &a move.l 12(a6),a1 ; pointer auf &b move.l 16(a6),a2 ; pointer auf &r move.l 4(a0),d0 ; D0 = a.lo move.l (a0),d1 ; D1 = a.hi sub.l 4(a1),d0 ; D0 = a.lo - b.lo move.l (a1),d2 ; D2 = b.hi subx.l d2,d1 ; D1 = a.hi - b.hi - borrow move.l d0,4(a2) ; r.lo = D0 move.l d1,(a2) ; r.hi = D1 unlk a6 rts MUL64Multipliziert zwei U64-Variablen. Das Ergebnis wird in einer dritten U64-Variable abgelegt. .globl _mul64 _mul64: link a6,#0 move.l 8(a6),a0 ; A0 = &a move.l 12(a6),a1 ; A1 = &b move.l 16(a6),a2 ; A2 = &r move.l 4(a0),d0 ; D0 = a.lo move.l (a0),d1 ; D1 = a.hi move.l 4(a1),d2 ; D2 = b.lo move.l (a1),d3 ; D3 = b.hi clr.l d4 ; r.lo = 0 clr.l d5 ; r.hi = 0 moveq #63,d6 ; Schleifenzaehler m64_loop: btst #0,d2 ; Bit 0 von b gesetzt? beq.s m64_noadd ; nein, nicht addieren add.l d0,d4 ; r.lo = r.lo + a.lo addx.l d1,d5 ; r.hi = r.hi + a.hi + extend m64_noadd: lsl.l #1,d0 ; a <<= 1 roxl.l #1,d1 lsr.l #1,d3 ; b >>= 1 roxr.l #1,d2 dbra d6,m64_loop ; 64 Bits bearbeiten move.l d4,4(a2) ; r.lo ablegen move.l d5,(a2) ; r.hi ablegen unlk a6 rts DIV64Dividiert zwei U64-Variablen. Als Ergebnis stehen der Quotient und der Rest gleichzeitig in zwei getrennten U64-Variablen zur Verfügung. .globl _div64 _div64: link a6,#-24 ; Platz fuer N,R,Q move.l 8(a6),a0 ; &a (Divisor) move.l 12(a6),a1 ; &b (Divident) move.l 16(a6),a2 ; &q (Quotient) move.l 20(a6),a3 ; &r (Reminder) * Division durch 0 abfangen move.l (a1),d0 ; D0 = b.lo move.l 4(a1),d1 ; D1 = b.hi or.l d1,d0 bne.s div_nz ; != 0, normale Division * b == 0 : q=0, r=a clr.l d0 clr.l d1 move.l d0,(a2) ; q.lo = 0 move.l d1,4(a2) ; q.hi = 0 move.l (a0),(a3) ; r.lo = a.lo move.l 4(a0),4(a3) ; r.hi = a.hi unlk a6 rts div_nz: * N = a move.l 4(a0),d0 move.l (a0),d1 move.l d0,-24(a6) ; N = a move.l d1,-20(a6) clr.l -16(a6) ; R = 0 clr.l -12(a6) clr.l -8(a6) ; Q = 0 clr.l -4(a6) * Schleife ueber 64 Bit moveq #63,d7 div_loop: * R <<= 1 move.l -16(a6),d0 move.l -12(a6),d1 lsl.l #1,d0 roxl.l #1,d1 move.l d0,-16(a6) move.l d1,-12(a6) * Bit 63 von N nach Bit 0 von R uebertragen move.l -20(a6),d0 btst #31,d0 beq.s no_msb move.l -16(a6),d1 bset #0,d1 move.l d1,-16(a6) no_msb: * N <<= 1 move.l -24(a6),d0 ; N.lo move.l -20(a6),d1 ; N.hi lsl.l #1,d0 roxl.l #1,d1 move.l d0,-24(a6) move.l d1,-20(a6) * Vergleich: R >= b ? move.l -12(a6),d0 ; D0 = R.hi move.l (a1),d1 ; D1 = b.hi cmp.l d1,d0 blt.s no_sub ; R < b bgt.s do_sub ; R > b move.l -16(a6),d0 ; D0 = R.lo move.l 4(a1),d1 ; D1 = b.lo cmp.l d1,d0 blt.s no_sub ; R < b do_sub: * R = R - b move.l -16(a6),d0 ; D0 = R.lo move.l -12(a6),d1 ; D1 = R.hi move.l 4(a1),d2 ; D2 = b.lo move.l (a1),d3 ; D3 = b.hi sub.l d2,d0 subx.l d3,d1 move.l d0,-16(a6) move.l d1,-12(a6) * Q = (Q << 1) | 1 move.l -8(a6),d0 ; D0 = Q.lo move.l -4(a6),d1 ; D1 = Q.hi lsl.l #1,d0 roxl.l #1,d1 bset #0,d0 ; + 1 move.l d0,-8(a6) move.l d1,-4(a6) bra.s after_q no_sub: * Q <<= 1 move.l -8(a6),d0 ; D0 = Q.lo move.l -4(a6),d1 ; D1 = Q.hi lsl.l #1,d0 roxl.l #1,d1 move.l d0,-8(a6) move.l d1,-4(a6) after_q: dbra d7,div_loop * Ergebnis speichern move.l -8(a6),d0 ; Q.lo move.l -4(a6),d1 ; Q.hi move.l d1,(a2) ; q.hi = Q.hi move.l d0,4(a2) ; q.lo = Q.lo move.l -16(a6),d0 ; R.lo move.l -12(a6),d1 ; R.hi move.l d1,(a3) ; r.hi = R.hi move.l d0,4(a3) ; r.lo = R.li unlk a6 rts SHL64Schiebt alle Bits einer U64-Variablen um eine Position nach links (Multiplikation mit 2). .globl _shl64 _shl64: link a6,#0 move.l 8(a6),a0 ; pointer auf &a move.l (a0),d0 ; D0 = a.lo move.l 4(a0),d1 ; D1 = a.hi lsl.l #1,d0 ; D0 = a.lo << 1, extend roxl.l #1,d1 ; D1 = a.hi << 1, bit 0 = extend move.l d0,(a0) ; a.lo = D0 move.l d1,4(a0) ; a.hi = D1 unlk a6 rts SHL64Schiebt alle Bits einer U64-Variablen um eine Position nach rechts (Division durch 2). .globl _shr64 _shr64: link a6,#0 move.l 8(a6),a0 ; pointer auf &a move.l (a0),d0 ; D0 = a.lo move.l 4(a0),d1 ; D1 = a.hi lsr.l #1,d1 ; D1 = a.hi >> 1, extend roxr.l #1,d0 ; D0 = a.lo >> 1, bit 31 = extend move.l d0,(a0) ; a.lo = D0 move.l d1,4(a0) ; a.hi = D1 unlk a6 rts Die obigen Routinen sind in der Datei U64.S abgelegt und werden mit AS68 U64.S in die Objekt-Datei U64.O übersetzt. Um die Funktionen in C nutzen zu können, muss die Objekt-Datei zusätzlich beim Kompilieren angegeben werden. Die Header-DateiIn der Header-Datei U64.H ist die Datenstruktur definiert. Zusätzlich C-Funktionen zum Ausgeben einer U64-Variable als Dezimalzahl und zum Umwandeln eines Strings in eine U64-Variable.
Ein TestprogrammDas Programm demonstriert die Anwendung aller oben gezeigten Funktionen.
ScreenshotNach dem Aufruf des generierten Programms T64.68K zeigt der NKC folgendes Bild.
|