系统有 4 个按钮,分别命名为 A 到 D。按钮的内存映射到地址 0xffffe440。按钮的状态在位索引 7 到 4 处给出,其中按钮 A 在索引 7 处给出,按钮 D 在索引 4 处给出。位值 1 表示该按钮当前被按下。该系统有8个LED灯,内存映射在地址0x17880010。 LED 灯位于位索引 12 至 5。
做什么:
创建一个永远循环的程序。在循环中,读取按钮A和D的状态,如果按下A,则打开LED灯以显示十进制值13的二进制表示。如果松开 A,则应继续显示值 13,直到按下按钮 D。如果按下 D,所有 LED 灯都会关闭。如果再次按下按钮 A,则应再次显示值 13,依此类推。如果同时按下 A 和 D,则 LED 应关闭。当程序启动时,如果没有按下任何按钮,所有 LED 灯都应该关闭。请注意,部分正确的解决方案可以获得积分。
写入内存映射 I/O 端口时,无需考虑端口的其他一些未使用位是否发生更改。
我尝试过的:
这是我尝试过的,答案中的解决方案非常短,但我的解决方案很长,可能不漂亮,但对我来说看起来是正确的,我想听听你们的想法。 (我不想重写它的原因是因为我想知道这是否正确,我正在做旧考试)
funct :
lui $t0, 0xffff
ori $t0, $t0, 0xe440
lui $t1, 0x1788
ori $t1, $t1, 0x0010
loop :
lw $t2, 0($t0)
andi $t3, $t2, 0x90
addi $t4, $0, 0x90
bne $t3, $t4, check_A
andi $t9, $t9, 0x0
sw $t9, 0($t1)
j loop
check_A :
andi $t3, $t2, 0x80
addi $t4, $0, 0x80
bne $t3, $t4, check_D
andi $t9, $t9, 0x1a0
sw $t9, 0($t1)
j loop
check_D:
addi $t4, $0, 0x10
andi $t4, $0, 0x10
bne $s3, $t4, exit
andi $t9, $t9, 0x0
sw $t9, 0 ($t1)
j loop
exit:
j loop
有一行代码并不符合您的想法:
andi $t9, $t9, 0x1a0
这看起来是错误的,因为它(a)依赖于
$t9
的原始值,并且(b)用常量掩盖它,几乎没有任何效果。 $t9
的传入值很可能是 0,因此在这条指令之后仍然是 0。
可能你想要:
addi $t9, $0, 0x1a0
虽然我不得不承认我不明白这个恒定值从何而来。我本来想把 13 放在那里(或十六进制,0xD)。
以下:
check_D:
addi $t4, $0, 0x10
andi $t4, $0, 0x10
也肯定没有做你想做的事。即将
$t4
设置为 0x10,然后用 0x10 进行掩码,从而使 $t4
保留为 0x10。需要修复寄存器用法,使其更像 Check_A
部分。
为了缩短程序,让我们观察一下
lw
允许使用 16 位有符号立即数。因此,您可以在没有基址寄存器的情况下访问 0xffffe440,如下所示:
lw $t2, 0xe440($0) # Note using $0/$zero as base register
这将访问位置 0xffffe440,并且无需将
$t0
设置为按钮的基址寄存器,因此节省了 2 条指令 (lui/ori)。
接下来,内存位置0x17880010确实需要基址寄存器,但仅限于0x1788部分,因为0x0010部分可以放入
sw
指令的偏移量中,因此节省了1条指令(ori)。
$0 寄存器始终保存 0,所以:
andi $t9, $t9, 0x0
sw $t9, 0($t1)
更简单的做法如下:
sw $0, 0($t1)
(顺便说一句,
andi
是一种不寻常但还可以的清除$t9的方法,大多数人会使用addi $t9, $0, 0
或add $t9, $0, $0
)
标记为
exit
的分支应该直接分支到 loop
,从而允许消除该标签和另一条指令 (j
)。
您还可以通过将常量 0x90 等放入其自己的专用寄存器中来动态缩短循环,而不是在循环内重新加载这些常量。这不会降低静态指令计数,但有助于动态指令计数。