我试图以小时:分钟:第二格式获取MIPS中的当前时间。我知道使用系统调用30会将自1970年1月1日以来的总毫秒数放入寄存器$ a1和$ a0,但我不知道如何将寄存器加在一起并除以1000得到总秒数。我相信其余的应该很容易。
谢谢!
答案 0 :(得分:3)
除以64位数字
由于在MARS上模拟的MIPS不支持64÷32⇒64个分区 1 ,我们需要实现我们自己的多字分区。
本书 Hacker delight 作为chapter,主要基于Knuth的计算机编程艺术。
这个想法原则上非常简单:将64位数字视为两位数字,每个数字为32位(因此该数字的基数为2 32 ),并执行小学数字分部。
让我们用一个基数为例来解决这个想法:考虑53/2
我们可以通过将5除以2来计算结果,得到结果 m 1 为2且余数 r 1 1.
m 1 是结果的第一位数(最重要的一位)。
然后我们在53中“删除”3得到数字13,这是 r 1 * 10 + 3。
我们再做13/2来得到 m 0 = 6而 r 0 = 1。
结果是26(余数为1)。
我们采用相同的方法,唯一的区别是我们处理的数字范围从0到2 32 - 1.
因此,64位数字(如时间系统调用返回的数字)被视为两位数字:最重要的一个(最右边)在$a0
中,另一个在$a1
中。
例如,数字0x15EF18933B1被视为
Digit 1 Digit 0
0x15E 0xF18933B1
我们正在寻找的算法只是一个循环,我们将最后一个余数“加”到当前数字,我们除以除数得到结果的当前数字和剩余部分用于下一步。
请注意,“添加”是指“添加重量”,余下的 r i 未添加到当前数字 n i ,它由缩放由基础然后添加(这正是我们之前做的,我们做了1 * 10 + 3,而不是1 + 3)。
我不打算深入解释这个算法,因为它可以在网上广泛使用。
需要注意的一件非常重要的事情是,我们实际上并没有到达任何地方
由于当前余数的缩放,我们仍然需要执行64÷32⇒64格(对于十进制情况,13/2不比53/2容易!)。
不同之处在于我们知道该数字最多为两位数。
要解开这个循环论证,我们需要缩小到32÷16⇒32格 MIPS支持32÷32⇒32,通过将我们的除数限制在最多65535,我们得到了我们想要的结果 因此该算法使用16位的半字,然后将64位数视为4位数。
代码是
#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
输入参数位于a0:a1
(64位被除数,a0
中的低位字)和a2
(除数)。
结果位于a0:a1
(商)和hi
(余数)。
注意除数有限制:它的大小必须为16位。
这大大简化了除法算法,但在计算时间时需要一些解决方法。
计算时间
鉴于来自Unix纪元(1970年1月1日)的ms获得一天中的时间除以1000 * 3600 * 24并取余数。
但是,1000 * 3600 * 24不适合16位。我们可以用三个部门来做,但是我们需要将剩余部分组合起来。
有一种更简单,更直观的方法 首先,我们摆脱了ms。我们在一天中的时间内不需要ms准确度,因此我们可以完全丢弃其余部分。
li $v0, 30
syscall
#a0:a1 = ms since epoch
li $a2, 1000
jal div64x16
#a0:a1 = seconds since epoch
现在我们需要除以3600 * 24 = 86400,但我们不能
一个很好的技巧是除以3600 * 12 = 43200(适合16位),这给了我们半天的数量(我们称之为 hh )和余数给出了我们在半天之内的第二个数字(称之为 hs )
由于半天有43200秒,时间最多可以是11:59:59
我们不知道2:0:0是下午2点还是凌晨2点,我们需要检查 hh 是奇数还是知道,如果 hh 是偶然那么我们是在一天的前半天和时间是正确的,否则我们在下半天,我们将<43>(半天的秒数)添加到 hs 。
这会在天中将半天内的秒数转换为秒数。
#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
一旦拥有当天的秒数(32位数字),其余部分就很容易了。
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
注意时间系统调用使用Java new Date().getTime()
值,这是GMT时区中的当前时间。除非你住在那个时区,否则时间会有所不同。
1 此表示法表示64位被除数,32位除数和64位结果。