请参阅我的解释的编辑部分。
这有点长,难以说明。但我很感激花时间阅读这篇文章。请耐心等待。
假设我有这个:
.data
str1: .asciiz "A"
str2: .asciiz "1"
myInt:
.word 42 # allocate an integer word: 42
myChar:
.word 'Q' # allocate a char word
.text
.align 2
.globl main
main:
lw $t0, myInt # load myInt into register $t0
lw $t3, str1 # load str1 into register $t3
lw $t4, str2 #load str2 into register $t4
la $a0, str1 # load address str1
la $a1, str2 # load address str2
然后在SPIM中,用户文本段为
User Text Segment [00400000]..[00440000]
[00400000] 8fa40000 lw $4, 0($29) ; 183: lw $a0 0($sp) # argc
[00400004] 27a50004 addiu $5, $29, 4 ; 184: addiu $a1 $sp 4 # argv
[00400008] 24a60004 addiu $6, $5, 4 ; 185: addiu $a2 $a1 4 # envp
[0040000c] 00041080 sll $2, $4, 2 ; 186: sll $v0 $a0 2
[00400010] 00c23021 addu $6, $6, $2 ; 187: addu $a2 $a2 $v0
[00400014] 0c100009 jal 0x00400024 [main] ; 188: jal main
[00400018] 00000000 nop ; 189: nop
[0040001c] 3402000a ori $2, $0, 10 ; 191: li $v0 10
[00400020] 0000000c syscall ; 192: syscall # syscall 10 (exit)
[00400024] 3c011001 lui $1, 4097 ; 23: lw $t0, myInt # load myInt into register $t0
[00400028] 8c280004 lw $8, 4($1)
[0040002c] 3c011001 lui $1, 4097 ; 25: lw $t3, str1 # load str1 into register $t3
[00400030] 8c2b0000 lw $11, 0($1)
[00400034] 3c011001 lui $1, 4097 ; 27: lw $t4, str2 #load str2 into register $t4
[00400038] 8c2c0002 lw $12, 2($1)
[0040003c] 3c041001 lui $4, 4097 [str1] ; 29: la $a0, str1 # load address str1
[00400040] 3c011001 lui $1, 4097 [str2] ; 31: la $a1, str2 # load address str2
[00400044] 34250002 ori $5, $1, 2 [str2]
我知道 lw 是伪代码,因此需要将其细分为两条指令。我理解这一部分。我们使用数据段的入口地址作为“基指针”并相对访问其他数据(包括第一个数据)。
我还观察到 str1 和 str2 的加载地址使用了两个不同的寄存器: $ 4 和 $ 1 。 $ 4是$ a0。 那是为什么?
如果我交换最后两条指令,在SPIM上我看到了
...
[0040003c] 3c011001 lui $1, 4097 [str2] ; 31: la $a1, str2 # load address str2
[00400040] 34250002 ori $5, $1, 2 [str2]
[00400044] 3c041001 lui $4, 4097 [str1] ; 32: la $a0, str1 # load address str1
那么为什么加载地址如此奇怪?为什么 str2 使用 $ 1 ??? 我该如何解释lui $ 1,4097 [str2]和lui $ 4,4097 [str1]是如何不同的?
PS:有人也可以向我解释为什么我们需要支架 [str2] ?lui,$ 1,4097,[str2] 仅将数据段的入口地址推送到寄存器$ 1。也就是说,0x10010000。
非常感谢!
修改
我重写了整个脚本以简化情况。
脚本:http://pastebin.com/BHh89iqt 文字细分:http://pastebin.com/t2eDEs1f
让我提醒您,我们使用伪指令编写,而不是使用真正的MIPS机器代码。也就是说,“lw”,“jal”,“addi”等都是伪指令。
例如,lw(加载词)被分解为两个机器指令(查看文本段):
lui $1, 4097 ; 23: lw $t0, myInt # load myInt into register $t0
lw $8, 4($1)
MIPS是32位,因此我们将其细分为两条指令。寻址32位地址的总和将产生43位指令集。这就是我们分解为2个部分的原因。 标签是指向我们分配给的东西的内存地址。
MIPS只能读取lw $ rt,offset($ rs)形式的指令。因此,大多数加载指令都遵循这种方法,并使用$ at将涉及标签的伪指令转换为MIPS机器指令。
对于lw来说,这很容易。对于la load地址,它有点棘手。 注意最后四个加载地址指令。 MIPS将它们转化为:
[0040003c] 3c041001 lui $4, 4097 [str1] ; 27: la $a0, str1 # load address str2
[00400040] 3c011001 lui $1, 4097 [str2] ; 28: la $a0, str2 # load address str1
[00400044] 34240002 ori $4, $1, 2 [str2]
[00400048] 3c011001 lui $1, 4097 [str2] ; 30: la $a0, str2 # load address str2
[0040004c] 34240002 ori $4, $1, 2 [str2]
[00400050] 3c041001 lui $4, 4097 [str1] ; 31: la $a0, str1 # load address str1
$ 4指的是$ a0。如果您查看说明,我交换了前两个加载指令,结果是最后两条指令。 我故意这样做来说明奇怪的行为:在交换之前,lui使用$ 4来存储str1的地址,但是如果我想加载str2的地址,我将使用$ at然后应用offset。
我无法弄清楚为什么昨晚,刚才我意识到这是因为编译器足够聪明,知道str1是数据分区中的第一个数据,所以不需要转换任何东西
然而这也很奇怪,因为编译器如何知道停止打印字符串的字节? (如果我们想打印一个字符串......)
我的猜测:终止打印的空字符。
总之。我想这只是MIPS使用的惯例。
第二次修改
事实上,如果你只是在str1之上添加一个新数据,你会看到 我的解释是正确的。
新脚本:http://pastebin.com/8DuzFrk0
新文字细分:http://pastebin.com/YXbvzc4z
我只将myCharB添加到数据段的顶部。
[0040003c] 3c011001 lui $1, 4097 [str1] ; 29: la $a0, str1 #
load address str2
[00400040] 34240004 ori $4, $1, 4 [str1]
[00400044] 3c011001 lui $1, 4097 [str2] ; 30: la $a0, str2 #
load address str1
[00400048] 34240006 ori $4, $1, 6 [str2]
答案 0 :(得分:2)
我还观察到str1和str2的加载地址使用了两个 不同的寄存器:4美元和1美元。 $ 4是$ a0。那是为什么?
嗯,谁在乎呢? xD它是内部SPIM实现,只要不破坏MIPS ABI,它就可以自由使用任何寄存器。我只是建议你不要过多地依赖伪指令来确保哪些寄存器已经改变/它们拥有什么值。通常LW也不是伪指令,但是你使用它的方式是。
有人还可以向我解释为什么我们需要括号[str2]?
您不需要任何括号。这只是程序员的SPIM信息,表明该指令正在加载str2地址。它不是集会的一部分。
lui,$ 1,4097,[str2]只将数据段的入口地址推送到 注册$ 1。也就是说,0x10010000
实际上它只加载1美元的上半字。恰好是下半字是普通零。请记住,LUI不会修改下半字,因此您必须确保它保留所需的值(重置寄存器或使用LI)。
然而这也很奇怪,因为编译器如何知道什么 字节停止打印字符串? (如果我们想打印一个字符串......)
如你所说的那样,无法终止。
我想这只是MIPS使用的惯例。
这比MIPS更早。 MIPS没有对此进行任何定义,也没有任何其他架构。这是数据处理,它在OS等上层定义。在这种情况下,它在自己的系统调用上是SPIM约定。无论如何,以null结尾的字符串非常常见。 C编程语言对字符串使用。