我正在学习计算机体系结构课程的MIPS程序集。本课程使用MARS。在接受10个数字放入数组进行分配时,我决定测试一些东西。我想看看是否可以创建一个自动将用户输入的整数推送到堆栈的循环。到目前为止,这是我的代码:
# For loop initialization
li $s0, 0 # Set $s0 equal to zero.
li $sp, -40 # Reserve space for 10 integers on the stack.
li $t0, 0 # temp var to increment stack offset.
# Stores ten user inputted numbers onto the stack.
stackLoop:
beq $s0, 10, doneStack
li $v0, 51
la $a0, prompt
syscall
sw $t0($sp)
# Print out stack values.
li $v0, 1
lw $0($sp)
syscall
addi $t0, $t0, 4
addi $s0, $s0, 1
j stackLoop
doneStack:
当我接到sw指令时遇到问题,当我指定两个参数时因为$ t0($ sp)无效而引发错误。但是,如上所示,仅指定一个参数似乎有效。在开始工作之前,我曾向我的教授询问过这个问题,他说这是不可能做到的。为什么只使用一个参数?我的结论是sw必须默认存储$ v0。但是,这并不能成为语法的借口。例如,键入sw $ v0,$ t0($ sp)会在编译时抛出错误。
同样,为什么当使用$ 0($ sp)时lw工作?我猜测lw默认加载到$ a0,这可以解释为什么li $ v0,1可行。但是,如果是这种情况,为什么lw $ a0,0($ sp)产生增量4,一个字中的字节数?它不会引用0($ sp)处的数据,这是在每次迭代时从堆栈中弹出的吗?
我查看了一些文档,但所有文档都使用sw和lw的两个参数。我班上的教科书甚至没有提到我上面所做的事情是可能的。对于中篇小说风格的帖子道歉,但我的好奇心被激怒了。
答案 0 :(得分:4)
MARS使用非常简单的解析器和更简单的标记器
在分布式JAR中,源文件可用,如果您想哭,可以查看Java类mars.mips.instructions.InstructionSet
。
由于MARS的本质,这是完全可以接受的:一个教育模拟器。
标记生成器将文本分解为空格上的标记(但输入首先分为行),逗号和括号。
因此,这些语法都是等价的:
sw $t0($sp)
sw $t0 ($sp)
sw $t0, ($sp)
MIPS ISA中有{em> no 默认寄存器sw
和lw
(或任何其他指令)。
对于每条指令都是如此,但是,尊重RISC心态,只有lw
和sw
(和兄弟姐妹)可以有括号(因为它们表示寻址模式),从而缓解问题(仍然是像or $t0 $t0 $t1
是可能的)。
最后,sw $v0, $t0($sp)
在MIPS中无法编码
sw
它是I-type instruction,因此它有一个源寄存器(t),一个基址寄存器和一个16位立即位移(i):
101011 ttttt sssss iiiiiiiiiiiiiiii
TL; DR :sw $t0($sp)
只是一个MARS工件。
答案 1 :(得分:2)
汇编不是像C,Java等编程语言...... - 你有一些编写单个表达式的免费语法。
装配更像是"助记符" (名称)用于机器指令,在CPU中硬连线,通过CPU的创建者如何设计芯片上的晶体管布局,以及它如何连接到计算机的其他部分。因此,如果CPU被设计为具有指令ori $3, $3, 0x25
,那么您可以在源代码中编写它,并且汇编器将其转换为机器代码,对于MIPS CPU,它是单词0x34630025
。当MIPS在地址pc
(程序计数器)的内存中遇到此特定字时,它将执行ori $3, $3, 0x25
而不执行任何其他操作。
您无法对ori $3, $3, 0x25 + 0x33
进行编码(在将常量预先计算为简单0x58
之前不预先计算常量),没有允许编码0x25 + 0x33的机器操作码作为应在运行时添加的两个值。一个聪明的汇编程序会让你写这个,并编译为ori $3, $3, 0x58
(IIRC MARS并不聪明)。
因此,您不能学习某种语法并构建相关指令,您必须学习CPU供应商定义的指令,并记住哪些是可能的,哪些是不可能的。汇编是从可读助记符到二进制机器代码的1:1翻译(尽管MARS汇编程序有许多"伪指令",它们不能转换为单个本机操作码,而是转换为本机操作码链( 2-3通常),模拟伪指令行为,所以它是一种编译器,虽然非常原始,所有可能的伪指令都有记录)。
这就是可能的指令受限的原因,你直接使用芯片上的HW晶体管,而你只有CPU设计人员在设计中付出的代价。如果你想创建一些新的指令,你就会对固定的CPU感到不快(尽管你可以使用其中一个FPGA芯片来创建自己的内部逻辑,但那是完全不同的主题)。
关于lw $0($sp)
- 这个语法无效,它会编译,因为MARS汇编程序不是太阳下最大的SW,所以在我希望它更聪明的情况下它会失败(像li $t1,123+34
不起作用),如果停止并报告错误会好得多,它实际上会产生一些东西。
你的lw $0($sp)
汇集为lw $0, 0($sp)
,即它会猜测是否缺少昏迷,并且缺少位移,整个指令只是空间填充,因为你可以存储到{{ 1}}又名$0
你想要的任何东西(就像$zero
那样),但你会回读为零。
运行MARS,打开帮助 F1 并检查选项卡"基本指令"和"扩展(伪)指令",这些都是你可用的。不幸的是,用于描述它们的语法是示例性的,而不是数学,因此它有时可能看起来像是可用的,直到你发现它不是,很难。
现在关于lw
......帮助说lw
。如果你是经验丰富的asm开发人员,他知道几个其他CPU的汇编,以及几个不同汇编器的语法,这是非常明显的。如果你不熟悉装配,我可以看到这是非常难以理解的。长篇描述也没有多大帮助。
但部分技巧是检查选项卡上方的绿色区域,使用"示例指令的操作数键",让我们尝试利用此lw $t1,-100($t2)
...
lw
任何整数寄存器
-100签名的16位整数(-32768到32767)装载&存储寻址模式 -100($ t2)符号扩展的16位整数添加到$ t2的内容
如您所见,没有$t1, $t2, $t3
(这意味着您可以在位移位置使用注册)。
那么如何解释这个帮助:lw $t1,$t3($t2)
是"加载单词",它是一个基本指令,它有两个操作数。
左操作数是目标寄存器(可以是32个GPR(通用寄存器)中的任何一个,即lw
到$0
,或者它们的别名如$31
,{{1}等等...... - 这是从存储器中取出的单词值将被存储的地方。
右操作数的形式为" displacement_constant($ GPR)",用于将内存计算作为GPR的内容与displacement_constant一起添加。即$at
将在寄存器$t0
中取值并从中减去-100($sp)
,并将其用作存储器地址以接触存储器芯片,并从那里获取字值。
这意味着在MIPS上,您只能通过单个寄存器间接寻址$sp
的内存,不允许使用100
之类的数学表达式。要做到这一点,你必须首先计算,如:
lw
实际上在你编译代码之后,你可以在MARS中看到反汇编的机器代码(这就是我如何弄清楚$t0 + $t2
例子中使用了什么操作码,以及是什么样的憎恶MARS从无效 add $at, $t0, $t2 # don't use $at unless you know what you are doing
lw $a0,0($at) # as 99% of pseudo ins. will use $at for their temporaries
生成 - 在"执行"标签(我不确定是否必须稍微配置一下才能显示所有内容,包括伪指令如何转换为基本指令,但无法在设置中找到任何相关内容,所以让我们来看看。希望视图是默认的。)