2017-10-05 2 views
0

Ich versuche, die aktuelle Zeit in MIPS in Stunde: Minute: zweites Format zu erhalten. Ich weiß, dass die Verwendung von syscall 30 die Gesamtzahl der Millisekunden seit dem 1. Januar 1970 in die Register $ a1 und $ a0 legt, aber ich weiß nicht, wie man die Register zusammenfügt und durch 1000 dividiert, um die Gesamtzahl der Sekunden zu erhalten. Ich glaube, der Rest sollte danach einfach sein.Wie bekomme ich die aktuelle Zeit in Sekunden in MIPS Assembly Language?

Vielen Dank!

+0

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.) –

Antwort

3

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.

Verwandte Themen