您的问题涉及多个层面,因此我尝试一一解决...
机器:
机器具有可按字节寻址的内存。第一个字节的地址为 0,第二个字节的地址为 1,等等......每当我在这个答案中写下内存内容时,我都会使用这种格式:01 02 0E 0F 10 ...,使用十六进制值并在字节之间使用空格,地址连续从起始地址到结束地址。 IE。如果此内容从地址 0x800000 开始,则内存将是(全部为 hexa):
address | byte value
------- | ----------
800000 | 01
800001 | 02
800002 | 0E
800003 | 0F
800004 | 10
800005 | ...
到目前为止,无论目标MIPS平台是小端还是大端都无所谓,只要涉及字节大小的内存,字节顺序是“正常的”。
如果您将 byte 从地址0x800000 加载到t0(使用lb 指令),t0 将等于值1。
如果你将 word 从地址0x800000 加载到t0 (使用lw 指令),字节序最终会发挥作用。
在 little-endian 机器上,t0 将等于值 0x0F0E0201,字的第一个字节(在内存中)的数量为 2560 (最低功率),第二个是256的数量1,...最后一个是256的数量3。
在 big-endian 机器上,t0 将等于值 0x01020E0F,字的第一个字节(在内存中)的数量为 2563,第二个是256的数量2,...最后一个是256的数量0。
(256 is 28,而那个幻数来自“一个字节是8位”,一个位可以包含两个值(0或1),一个字节有8个位,所以一个字节可以包含2个8 不同的值)
在这两种情况下,CPU 都会从内存中读取相同的四个字节(地址为 0x800000 到 0x800003),但字节序定义了它们作为最后 32 位字值出现的顺序。
t0 在 CPU 芯片上由 32 位物理组成,它没有地址。当您想在 CPU 指令中寻址它(即使用存储在 t0 中的值)时,您将其编码为指令为 $8 寄存器($8 具有 $t0 别名,以便在您的汇编程序中使用,所以我使用而是t0 别名)。
字节序不适用于这 32 位寄存器,它们已经是 32 位 b0-b31,一旦加载了值 0x0F0E0201,这 32 位将设置为 0000 1111 0000 1110 ...(我是从顶部 b31 位向下到底部 b0,以理解左移/右移指令并使其作为人类格式化的二进制数工作),没有必要考虑寄存器的字节顺序或位存储在哪个物理顺序芯片,将其视为完整的 32 位值就足够了,并且在算术指令中它可以那样工作。
当将带有lb 的字节值加载到寄存器中时,它会落入 b0-b7 位,其中 b8-b31 包含 b7 的副本(将带符号的 8 位值符号扩展为带符号的 32 位值)。
当将寄存器的值存储到内存中时,字节序仍然适用,即将word 值0x11223344 存储到内存中会将单个字节设置为44 33 22 11。
汇编器(源代码和编译)
为其目标平台配置良好的汇编器将对程序员隐藏字节顺序,以方便使用字值方便。
所以当你定义内存值时:
myPreciousValue .word 0x11223344
汇编器将解析文本(您的源代码是文本(!),即一个字符是一个字节值 - 在 ASCII 编码中,如果您在 UTF8 文本编辑器中编写源代码并使用非 ASCII 字符,它们可能会被编码跨多个字节,ASCII 可打印字符在 ASCII 和 UTF8 中具有相同的编码,并且只占用一个字节)“0x11223344”(10 个字节30 78 31 31 32 32 33 33 34 34),从中计算出 32 位字值0x11223344,然后它将对其应用目标平台字节序以生成四个字节的机器代码:
44 33 22 11 # little-endian target
或:
11 22 33 44 # big-endian target
当您随后在代码中使用 lw 指令将 myPreciousValue 从内存加载到寄存器时,寄存器将包含预期的字值 0x11223344(只要您没有混淆您的汇编程序配置并且使用了错误的字节序,在 MARS/SPIM 中不会发生,因为它只支持所有东西(VM、汇编器、调试器)中的小端序配置。
因此程序员不必每次将 32 位值写入源代码中的某处时都考虑字节顺序,汇编器会将其解析并处理为字节值的目标变体。
如果程序员想在内存中定义四个字节01 02 03 04,她可以“聪明”,将.word 0x04030201用于little-endian目标平台,但这会混淆原意,所以我建议使用.byte 1, 2, 3, 4在这种情况下,程序员的意图是定义字节,而不是单词。
当您使用 .byte 指令声明字节值时,它们会按照您编写它们的顺序进行编译,不会应用字节序。
调试器
最后是调试器的内存/寄存器视图...此工具将再次尝试以直观/方便的方式工作,因此当您检查内存视图并将其配置为字节时,内存将显示为:
0x800000: 31 32 33 34 41 42 43 44 | 1234ABCD
当您将其切换到“word”视图时,它将使用配置的字节序以目标平台顺序连接字节,即在 MARS/SPIM 作为 little-endian 平台中,它将显示在同一内存上:
0x800000: 34333231 44434241
(如果还包括 ASCII 视图,是否也包含“文字”?如果是,那么它将看起来像 4321 DCBA。我目前没有安装 MARS/SPIM 来检查它们调试器中的内存视图实际上看起来像,对不起)
因此,作为程序员,您可以直接从显示器读取“word”值,而无需将字节改组为“正确”顺序,您已经看到“word”值将是什么(从这四个字节的内存内容中)。
寄存器视图通常默认显示十六进制字值,即从地址 0x800000 加载字到t0 后,寄存器$8 将包含值0x34333231(875770417 十进制)。
如果您对用于该加载的内存中第一个字节的值感兴趣,此时您必须应用您对该目标平台的字节序的了解,并查看前两位数字“34”(大endian),或者寄存器视图中的最后两个“31”(小端)(或者更确切地说在字节视图模式下使用内存视图以避免任何错误)。
代码中的运行时检测。
所以有了上面的所有信息,运行时检测代码应该很容易理解(不幸的是我目前没有 MARS/SPIM,所以我没有验证它是否有效,请告诉我):
.data
checkEndianness: .word 0 # temporary memory for function
# can be avoided by using stack memory instead (in function)
.text
main:
jal IsLittleEndian
# ... do something with v0 value ...
... exit (TODO by reader)
# --- functions ---
# returns (in v0) 0 for big-endian machine, and 1 for little-endian
IsLittleEndian:
# set value of register to 1
li $v0,1
# store the word value 1 into memory (4 bytes written)
sw $v0,(checkEndianness)
# memory contains "01 00 00 00" on little-endian machine
# or "00 00 00 01" on big-endian machine
# load only the first byte back
lb $v0,(checkEndianness)
jr $ra
它有什么用?只要您为单一目标平台编写软件,并且您是通过目标 CPU 存储/加载单词,您就不需要关心字节序。
但是如果你有一个多平台的软件,并且它确实保存了二进制文件......为了使文件在两个大/小端平台上以相同的方式工作,文件结构的规范还必须指定字节序文件数据。然后根据该规范,一种类型的目标平台可能会将其读取为“本机”字值,另一种则必须对字值中的字节进行洗牌以读取正确的字值(此外,规范还应指定多少字节“词”是:) )。那么这样的运行时测试可能会很方便,如果您将混洗器包含在保存/加载例程中,使用字节序检测例程来决定它是否必须对字字节进行混洗。这将使目标平台字节序对其余代码“透明”,这将简单地将其本机“字”值发送到保存/加载例程,并且您的保存/加载可能在每个平台上使用相同的源(至少只要您使用一些多平台可移植编程语言,如 C,当然 MIPS 的程序集根本无法在不同的 CPU 上工作,需要从头开始重写。
此外,网络通信通常使用自定义二进制协议完成(通常包装在网络层最常见的 TCP/IP 数据包中,甚至加密,但您的应用程序会在某一时刻从中提取原始字节内容) ,然后发送/接收数据的字节顺序很重要,“其他”平台必须重新排列字节以读取正确的值。
其他平台(非 MIPS)
几乎可以从上面应用所有内容,只需检查另一个平台上的 byte 和 word 是什么(我认为 byte 在过去 35 年多的时间里一直是 8 位,但 word可能会有所不同,例如在 x86 平台上word 仅是 16 位)。仍然 little-endian 机器将以“反转”顺序读取“word”字节,第一个字节用作最小 2560 次方的数量,最后一个字节用作最高 256 次方的数量(2561 在 x86 平台上,由于那里只有两个字节组成 word,所以 MIPS 的“word”在 x86 世界中被称为“double word”或“dword”)。