Dividing 64-Bit-Zahl
Da MIPS, wie auf dem Mars emuliert, nicht mehr als 64 ÷ 32 ⇒ 64 Divisionen wir brauchen, um unser eigenes Mehrwort unterstützt Division zu implementieren.
Das Buch Hacker Freude als chapter auf, basierend auf Die Kunst der Computerprogrammierung von Knuth.
Die Idee im Prinzip sehr einfach: eine 64-Bit-Zahl als zwei Ziffern Zahl Betrachten wir jede Ziffer ist 32-Bit (so die Basis dieser Zahl ist 2), und führen Sie die Grundschul Ziffer für Ziffer Division.
Lassen Sie sich diese Idee angeht unten mit einem Base-10 Beispiel: betrachtet 53/2.
wir das Ergebnis berechnen können 5 von 2 durch Division ein Ergebnis m von 2 und ein Rest r geben von 1.
m ist die erste Ziffer des Ergebnisses (der signifikanteste eins).
Dann "drop" wir die 3 in 53 die Zahl 13 zu erhalten, ist dies r * 10 + 3.
wieder tun wir 13/2 m = 6 und zu erhalten r = 1.
Das Ergebnis ist also 26 (mit Rest 1).
Wir haben den gleichen Ansatz verfolgen, wobei der einzigen Unterschied, dass wir mit den Zahlen beschäftigen, die von 0 bis 2 Bereich - durch die Zeit syscall zurück 1.
So eine 64-Bit-Zahl, wie das ist als eine zweistellige Zahl gesehen: die letzte signifikante (die am weitesten rechts) ist in $a0
, die andere in $a1
. Der Algorithmus, den wir suchen, ist dann nur eine Schleife, wo wir „add“ der letzte Rest an die aktuelle Stelle
Zum Beispiel wird die Zahl 0x15EF18933B1 als
Digit 1 Digit 0
0x15E 0xF18933B1
gesehen, teilen wir durch den Divisor die bekommen aktuelle Ziffer des Ergebnisses und der Rest, der im nächsten Schritt verwendet werden soll.
Hinweis, daß durch „addieren“ meinen wir „mit dem Gewicht hinzufügen“ den Rest r i nicht n i auf die aktuelle Stelle hinzugefügt, es durch die Basis skaliert und dann zugegeben (Genau das haben wir früher gemacht, wir haben 1 * 10 + 3, nicht 1 + 3). Ich werde den Algorithmus nicht ausführlich erklären, da er online verfügbar ist.
Eine sehr wichtige Sache zu beachten ist, dass wir eigentlich nirgendwo hinkommen.
Aufgrund der Skalierung des aktuellen Rests müssen wir noch eine 64 ÷ 32 ⇒ 64 Division durchführen (wie bei der Dezimalzahl, 13/2 ist nicht einfacher als 53/2!).
Der Unterschied ist, dass wir wissen, dass die Nummer höchstens zwei Ziffern hat.
Um dieses zirkuläre Argument zu entwirren, müssen wir auf eine 32 ÷ 16 ⇒ 32-Division herunterskalieren.
MIPS unterstützt 32 ÷ 32 ⇒ 32, indem wir unseren Divisor auf maximal 65535 begrenzen, bekommen wir, was wir wollten.
Also arbeitet der Algorithmus mit Halbwort von 16 Bits, eine 64-Bit-Zahl wird dann als 4-stellige Zahl angesehen.
Der Code ist
#Input
# a0:a1 = N = DCBA
# a2 = K (16-bit)
#Output
# a0:a1 = quotient
# hi = reminder
div64x16:
subu $sp, $sp, 16
sw $a0, ($sp)
sw $a1, 4($sp)
add $t0, $sp, 8 # Pointer to digits (N)
add $t3, $sp, 16 # Pointer to result (M)
xor $t1, $t1, $t1 # Remainder
loop:
subu $t3, $t3, 2
subu $t0, $t0, 2
sll $t1, $t1, 16 # t1 = R * 65536
lhu $t2, ($t0) # t2 = N[i]
addu $t2, $t2, $t1 # t2 = N[i] + R * 65536
div $t2, $a2
mflo $t1 # t1 = (N[i] + R * 65536)/K
sh $t1, ($t3) # M[i] = (N[i] + R * 65536)/K
mfhi $t1 # t1 = (N[i] + R * 65536) % K
bne $t0, $sp, loop
mthi $t1
lw $a0, 8($sp)
lw $a1, 12($sp)
addu $sp, $sp, 16
jr $ra
die Eingabeargumente sind in a0:a1
(die 64-Bit-Dividend, low word in a0
) und a2
(der Divisor).
Das Ergebnis ist in a0:a1
(Quotient) und hi
(Rest).
Hinweis, dass der Divisor eingeschränkt ist: Er muss 16 Bit groß sein.
Dies vereinfacht den Divisionsalgorithmus sehr, erfordert jedoch eine gewisse Umgehung bei der Berechnung der Uhrzeit.
Berechnung der Tageszeit
die ms aus der Unix-Epoche Given (1. Januar 1970), um die Tageszeit zu bekommen einer von 1000 * 3600 * 24 dividiert und nimmt den Rest.
Allerdings passt 1000 * 3600 * 24 nicht 16 Bits. Wir können es mit drei Divisionen machen, aber dann müssen wir die Reste kombinieren.
Es gibt einen etwas einfacheren und intuitiveren Ansatz.
Zuerst werden wir die ms los. Wir brauchen keine ms-Genauigkeit in der Tageszeit, damit wir den Rest ganz verwerfen können.
li $v0, 30
syscall
#a0:a1 = ms since epoch
li $a2, 1000
jal div64x16
#a0:a1 = seconds since epoch
Jetzt müssten wir durch 3600 * 24 = 86400 teilen, aber wir können nicht.
Ein netter Trick ist es, durch 3600 * 12 = 43200 zu teilen (das passt 16 Bit), das gibt uns die Anzahl der Halbtage hh) und der Rest gibt uns die Nummer der Sekunde weit in den halben Tag (nennen Sie es hs).
Da es an einem halben Tag 43200 Sekunden gibt, kann die Zeit höchstens 11:59:59 betragen.
Wir wissen nicht, ob 2: 0: 0 ist 02.00 oder 02.00 Uhr, wir müssen überprüfen, ob hh ist ungerade oder sogar zu wissen, wenn hh ist auch dann sind wir in der ersten Halbtages eines Tag und die Zeit ist richtig, sonst sind wir im zweiten Halbtag und fügen 43200 (die Sekunden in einem halben Tag) zu hs hinzu.
Dies konvertiert Sekunden in Halbtagen in Sekunden in Tage.
#a0:a1 = seconds since epoch
li $a2, 43200
jal div64x16
#a0:a1 = half-days since epoch
#hi = seconds in half-day
mfhi $s0 #Seconds in the half-day
andi $a0, $a0, 1 #a1 = 1 if odd half-day number (otherwise 0)
ror $a0, $a0, 1 #a1 < 0 if odd half-day number (otherwise 0)
sra $a0, $a0, 31 #a1 = 0xffffffff if odd half-day number (otherwise 0)
andi $a0, $a0, 43200 #a1 = 43200 if odd half-day number (otherwise 0)
add $s0, $s0, $a0 #s0 = seconds in the day
Sobald man die Sekunden am Tag (eine 32-Bit-Nummer) hat, ist der Rest einfach.
li $t0, 3600
div $s0, $t0
mflo $s0 #s0 = Hour
mfhi $t1
li $t0, 60
div $t1, $t0
mflo $s1 #s1 = Minute
mfhi $s2 #s2 = Second
#Print the time
li $v0, 1
move $a0, $s0
syscall
li $v0, 4
la $a0, sep
syscall
li $v0, 1
move $a0, $s1
syscall
li $v0, 4
la $a0, sep
syscall
li $v0, 1
move $a0, $s2
syscall
#Exit
li $v0, 10
syscall
Hinweis Der Systemaufruf Zeit, um den Java new Date().getTime()
Wert verwendet, ist dies die aktuelle Zeit in der GMT Zeitzone. Wenn Sie nicht in dieser Zeitzone leben, werden die Stunden anders sein.
Diese Notation bezeichnet einen 64-Bit-Dividend, einen 32-Bit-Divisor und ein 64-Bit-Ergebnis.
verwandt: https://stackoverflow.com/questions/16050338/mips-integer-multiplication-and-division. Sie können entweder die Anweisung 32b/32b => 32b division verwenden, um eine Division mit erweiterter Genauigkeit durch 1000 zu erstellen, oder Sie können die multiplikative Festkomma-Inverse verwenden. Sie müssen eine 64B x 1000 => 128b multiplizieren von 32-Bit-Multiplikationen bauen, aber auf Hardware mit multiplizieren * viel * schneller als teilen könnte es sich lohnen. (gcc ruft einfach eine libgcc.a-Hilfsfunktion für 'int div_by_1000 (long long millis) { return millis/1000; }': https://godbolt.org/g/MR6h6d auf; Sie könnten sich die src ansehen.) –