我必须确定火星模拟器是作为家庭作业的大或小端,这看起来非常简单,但我有一些问题 .
首先我尝试在内存中存储4个字节,内存为0,0,0,1,在内存中显示为0x01000000,因此,按相反的顺序,这似乎表示模拟器是小端,但是,当我加载4时字节作为寄存器的整数,寄存器中出现的内容再次为0x01000000,据我所知,如果它是小端,将加载的是0x00000001 .
此外,当使用.word 1存储4个字节时,存储的是0x00000001,此时没有字节反转 .
我想知道模拟器是大端还是小端,并解释这种行为
2 回答
你的问题有几个层面,所以我试着逐一解决它们......
机器:
机器具有可通过字节寻址的内存 . 第一个字节有地址0,第二个字节有地址1等等...每当我在这个答案中写下内存内容时,我会使用这种格式:
01 02 0E 0F 10 ...
,使用十六进制值并在字节之间使用空格,地址从连续开始地址到结束地址 . 即如果此内容将从地址0x800000开始,则内存将为(全部为hexa):到目前为止无关紧要,无论目标MIPS平台是小端还是大端,只要涉及字节大小的内存,字节顺序就是“正常” .
如果要将 byte 从地址
0x800000
加载到t0
(使用lb
指令),t0
将等于值1
.如果你将地址
0x800000
中的 word 加载到t0
(带有lw
指令),那么最终会发现字节序 .在 little-endian 机器上
t0
将等于值0x0F0E0201
,字的第一个字节(在内存中)是2560(最低功率)的量,第二个是2561的量,...最后一个是2563的量 .在 big-endian 机器上
t0
将等于值0x01020E0F
,字的第一个字节(在内存中)是2563的数量,第二个是2562的数量,...最后一个是2560的数量 .(256 is 28,该幻数来自"one byte is 8 bits",一位可以包含两个值(0或1),一个字节有8位,因此一个字节可以包含28个不同的值)
在这两种情况下,CPU将从存储器(地址0x800000到0x800003)读取相同的四个字节,但字节顺序定义它们将以哪个顺序显示为字值的最后32位 .
t0
在CPU芯片上由32位物理构成,它没有地址 . 当你想在CPU指令中解决它(即使用存储在t0
中的值)时,你将它编码为指令$8
寄存器(为了方便汇编器,$8
有$t0
别名,所以我使用的是t0
别名) .字节序不适用于那些32位寄存器,它们已经是32位b0-b31,一旦加载了
0x0F0E0201
,那些32位被设置为0000 1111 0000 1110 ...
(I 'm writing it from top b31 bit down to bottom b0, to make sense of shift left/right instructions and also to make it work as human formatted binary number), there' s没有必要考虑寄存器的字节顺序或者这些位存储在芯片上的物理顺序,足以将其视为完整的32位值,并且在算术指令中它将起作用 .当使用
lb
将字节值加载到寄存器中时,它会进入b0-b7位,其中b8-b31包含b7的副本(将带符号的8位值符号扩展为带符号的32位值) .当将寄存器的值存储到存储器中时,字节顺序再次适用,即将
word
值0x11223344
存储到存储器中将设置单个字节为44 33 22 11
.汇编程序(源代码和编译)
为其目标平台配置良好的汇编程序将隐藏程序员的字节序,以方便使用字值 .
所以当你定义内存值时:
汇编程序将解析文本(您的源代码是文本(!),即一个字符是一个字节值 - 以ASCII编码,如果您在UTF8文本编辑器中编写源并使用非ASCII字符,它们可能跨多个字节编码,ASCII可打印字符在ASCII和UTF8中具有相同的编码,并且仅占用单个字节)"0x11223344"(10字节
30 78 31 31 32 32 33 33 34 34
),从中计算出32位字值0x11223344
,然后它将目标平台字节序应用于生成四个字节的机器代码,或者:要么:
当你在代码中使用
lw
指令时,要将myPreciousValue
从存储器加载到寄存器中,寄存器将包含预期的字值0x11223344
(只要你没有在MARS / SPIM中发生,因为它只支持小端配置在一切(VM,汇编程序,调试程序)) .因此,程序员每次在源代码中的某处写入32位值时都不必考虑字节序,汇编器将解析并处理字节值的目标变量 .
如果程序员想要定义四个字节
01 02 03 04
在内存中,她可以"smart"并使用.word 0x04030201
用于小端目标平台,但这会混淆原始意图,所以我建议在这种情况下使用.byte 1, 2, 3, 4
,因为程序员的意图是定义字节,而不是字 .当您使用
.byte
指令声明字节值时,它们将按照您编写它们的顺序进行编译,不会对其应用任何字节顺序 .调试器
最后调试器的内存/寄存器视图...这个工具将再次尝试以直观/方便的方式工作,因此当您检查内存视图并将其配置为字节时,内存将显示为:
当您将其切换到“word”视图时,它将使用配置的字节顺序来连接目标平台顺序中的字节,即在MARS / SPIM中作为将在同一内存上显示的little-endian平台:
(如果还包含ASCII视图,是否也是"worded"?如果是,那么它看起来像
4321 DCBA
. 我现在没有安装MARS / SPIM以检查调试器中的内存视图实际上是什么样的,抱歉)因此,作为程序员,您可以直接从显示中读取“字”值,而无需将字节拖拽到“正确”顺序,您已经看到了“字”值将是什么(来自这四个字节的内存内容) .
寄存器视图通常默认显示十六进制字值,即在将地址0x800000加载到
t0
之后,寄存器$8
将包含值0x34333231
(十进制875770417
) .如果您感兴趣的是用于该负载的内存中第一个字节的值是什么,此时您必须应用您对该目标平台的字节序的了解,并查看前两个数字“34”(大端),或者在寄存器视图中最后两个“31”(小端)(或者更确切地说,在字节视图模式下使用存储器视图以避免任何错误) .
代码中的运行时检测 .
所以上面提到的所有信息,运行时检测代码应该很容易理解(不幸的是我目前没有MARS / SPIM,所以我没有验证它的工作原理,让我知道):
到底有什么好处呢?只要您为单个目标平台编写软件,并且您正在通过目标CPU存储/加载单词,您就不需要关心字节顺序 .
但是如果你有多平台的软件,它确实保存了二进制文件......为了使文件在大/小端平台上以相同的方式工作,文件结构的规范也必须指定文件数据的字节顺序 . 然后根据该规范,一种类型的目标平台可能将其读作“本机”字值,另一种目标平台必须将字值中的字节混洗以读取正确的字值(加上规范还应指定多少字节“单词“是:)) . 然后这样的运行时测试可能很方便,如果你将shuffler包含在save / load例程中,使用endianness检测例程来决定它是否必须对字节字节进行洗牌 . 这将使目标平台的字节顺序“透明”到剩余的代码,这将简单地发送到保存/加载例程它的本地“字”值,并且您的保存/加载可以在每个平台上使用相同的源(至少只要你使用像C这样的多平台可移植编程语言,当然MIPS的程序集根本不能在不同的CPU上运行,并且需要从头开始重写 .
此外,网络通信通常使用自定义二进制协议(通常包含在网络层的最常见TCP / IP数据包中,甚至加密,但您的应用程序将在一个点上从中提取原始字节内容),然后发送/接收数据的字节顺序很重要,而“其他”平台必须将字节洗牌以读取正确的值 .
其他平台(非MIPS)
可以从上面应用几乎所有内容,只需检查另一个平台上的
byte
和word
(我认为byte
在过去的35年中相当于8位,但是word
可能会有所不同,例如在x86平台上word
是16位只要) . 仍然是little-endian机器将以"reversed"顺序读取"word"字节,第一个字节用作最小2560电源的数量,最后一个字节用作最高256电源的数量(x86平台上的2561,因为只有两个字节在那里形成字, MIPS "word"在x86世界中被称为"double word"或"dword" .这是来自网站:http://courses.missouristate.edu/KenVollmar/mars/Help/MarsHelpDebugging.html
你可以看到它是小端的