MIPS与计算机内存

COpre——MIPS汇编指令与机器码、内存 ​ 在笔者进行COpre中MIPS部分的学习时,对于MIPS指令及其机器码转换,MIPS对应的内存计算等等感到十分头痛,这一篇文章主要目的是零碎的记录一些知识。 一. 数制 ​ 在计组学习中最常用到的数值即为二进制(binary/BIN)和十六进制(hexadecimal/HEX),例如MIPS汇编指令机器码用二进制表示,而计算机内存的表示通常为十六进制。一位十六进制数相当于四位二进制数。一位二进制数通常又称为比特位(bit),关联出计算机系统中经典的换算关系。 ​ 在32位系统中: 1字节(byte)=8比特位(bit) 计算机中最小的寻址单位即为字节/B 1个字(word)=4字节(byte)=32bits 即一个字就是32位,同样64位系统中一个字是64位 1KB=1024B 计算机中K的概念是2^10即1024 1MB=1024KB 1G=1024MB 二. 浅析MIPS架构 ​ MIPS是一种经典的RISC架构,具有精简的指令集,32位定长指令,五级流水线,延迟槽,32个通用寄存器等特点。这里我们主要谈及32位定长指令、32个通用寄存器、指令集等内容,其他的部分会在日后涉及。这里需要特殊说明的是我们的MIPS是32位系统 1.32位定长编码 ​ 定长指令的优点是简化指令解析,减少解析时间。但同样的,由于指令为定长32位,这对于内存是不友好的。我们在写MIPS汇编程序时,编写的每一行指令代码均为32位,四个字节,一个字。这里指令具体的分为R型、I型、J型指令 R型指令(register type) R型指令用于寄存器之间的操作,常用于算术运算、逻辑操作和寄存器之间的数据传输,如add,sub.and.or等。 I型指令(immediate type) ​ I型指令用于立即数(常数)与寄存器之间的操作,通常用于家在常熟、内存读写、分支跳转等操作。例如,addi,lw,sw,beq等指令 J型指令(jump type) ​ J型指令用于无条件跳转到目标地址,常用于函数调用、循环跳转等控制流程的修改。例如,j,jar指令。 对于以上指令,我们只需要记住他们都占32位,4个字节,其他的具体用法详见《MIPS-C指令集》。 ​ 2.32个通用寄存器 ​ MIPS寄存器是32位寄存器,每个部分的功能如图所示 ​ 需要注意的是,每个寄存器既可以用名字表示,也可以用编号表示,如**$to<==>$8**。 3.特殊寄存器 1. HI(high)与LO(low) ​ HI与LO是MIPS中用于处理乘除法的特殊寄存器,在MIPS汇编指令中,乘除法指令的结果最多为64位,夫需要设置特殊寄存器进行保存。在乘法中,HI保存高32位,LO保存低32位;在除法中HI保存余数,LO保存商。 2.PC程序计数器 ​ PC(program counter)程序计数器,是计算机系统中的一个寄存器,用于存储下一条指令的地址。程序计数器指向执行中的指令的内存地址,当处理器执行完当前指令后会自动将程序计数器的值增加,使其指向下一条指令的地址。具体地,在MIPS汇编指令中,每执行完一条指令 PC=PC+4,这是由于在MIPS中每一条指令所占的内存空间都是4个字节。程序计数器的初始值一般为程序的入口地址(首条指令的地址 最常见的为0x0000_3000)。同时分支指令也可以使程序计数器进行跳转。总的来说,PC相当于程序运行中的内存监控,通过PC可以了解程序的流程。 1 2 3 4 5 6 7 8 9 //在我翻阅 MIPS-C指令集时,发现了如下出场率极高的代码 //BEQ: beq rs,rt,offset //描述:if rs==rt then 转移 //以BEQ:相等时转移为例,功能的C语言描述为 if(GPR[rs]==GPR[rt]) PC=PC+4+sign_extend(offset||0^2);//代表在offset后补两位0 即乘四 else PC=PC+4; //我们可以发现,该指令至少会进行一条语句的跳转(四个字节),这也是我查询PC的起始 三.COpre中提供的部分题目具体分析 1.下列指令中需要在立即数后拼接两位0的是 1 答案: beq $s2,$s3,4 在立即数后拼接两位0,即将原立即数向左移动两位,立即数4,代表着按4对齐。在beq中,立即数n的意义是跳转到第n条指令,而实际操作中,一条指令占用四字节,地址访存的话需要跳转4n字节,所以需要拼接两位0。 ...

September 12, 2023 · 2 min · sudo

MIPS基本语法

MIPS代码的基本语法 1. .data数据段 1 2 3 .data fibs: .space 48 size: .word 12 ​ 在数据段进行变量的定义,<变量名> : .<伪指令> <变量内容> 2. .test代码段 ​ 定义程序的代码段,指令后为代码 1 2 .test add $t0,&t1,$t2 3. .macro ​ 使用宏可以避免相同代码多次复用 不带参数定义的宏 macro <macro_name> # # # .end_macro 常用的场景是可以替换程序结束的两行代码 1 2 li $v0,10 syscall 如果我们定义出代表这两行定义的宏 1 2 3 4 macro DONE li $v0,10 syscall .end_macro 这样我们在需要调用这两行代码的时候只需一行 ...

September 12, 2023 · 1 min · sudo

MIPS中的一维数组和二维数组

MIPS中使用数组 1.一维数组 ​ 数组存储需要申请内存空间 .data array: .space 40 # 存储这些数需要用到数组,数组需要使用 10 * 4 = 40 字节 # 一个 int 整数需要占用 4 个字节,需要存储 10 个 int 整数 # 因此,array[0] 的地址为 0x00,array[1] 的地址为 0x04 # array[2] 的地址为 0x08,以此类推。 str: .asciiz "The numbers are:\n" space: .asciiz " " .text li $v0,5 syscall # 输入一个整数 move $s0, $v0 # $s0 is n li $t0, 0 # $t0 循环变量 loop_in: beq $t0, $s0, loop_in_end # $t0 == $s0 的时候跳出循环 li $v0, 5 syscall # 输入一个整数 sll $t1, $t0, 2 # $t1 = $t0 << 2,即 $t1 = $t0 * 4 sw $v0, array($t1) # 把输入的数存入地址为 array + $t1 的内存中 addi $t0, $t0, 1 # $t0 = $t0 + 1 j loop_in # 跳转到 loop_in loop_in_end: la $a0, str li $v0, 4 syscall # 输出提示信息 li $t0, 0 loop_out: beq $t0, $s0, loop_out_end sll $t1, $t0, 2 # $t1 = $t0 << 2,即 $t1 = $t0 * 4 lw $a0, array($t1) # 把内存中地址为 array + $t1 的数取出到 $a0 中 li $v0, 1 syscall # 输出 $a0 la $a0, space li $v0, 4 syscall # 输出一个空格 addi $t0, $t0, 1 j loop_out loop_out_end: li $v0, 10 syscall # 结束程序 2.二维数组 .data matrix: .space 256 # int matrix[8][8] 8*8*4 字节 # matrix[0][0] 的地址为 0x00,matrix[0][1] 的地址为 0x04,…… # matrix[1][0] 的地址为 0x20,matrix[1][1] 的地址为 0x24,…… # …… str_enter: .asciiz "\n" str_space: .asciiz " " # 这里使用了宏,%i 为存储当前行数的寄存器,%j 为存储当前列数的寄存器 # 把 (%i * 8 + %j) * 4 存入 %ans 寄存器中 .macro getindex(%ans, %i, %j) sll %ans, %i, 3 # %ans = %i * 8 add %ans, %ans, %j # %ans = %ans + %j sll %ans, %ans, 2 # %ans = %ans * 4 .end_macro .text li $v0, 5 syscall move $s0, $v0 # 行数 li $v0, 5 syscall move $s1, $v0 # 列数 # 这里使用了循环嵌套 li $t0, 0 # $t0 是一个循环变量 in_i: # 这是外层循环 beq $t0, $s0, in_i_end li $t1, 0 # $t1 是另一个循环变量 in_j: # 这是内层循环 beq $t1, $s1, in_j_end li $v0, 5 syscall # 注意一下下面几行,在 Execute 页面中 Basic 列变成了什么 getindex($t2, $t0, $t1) # 这里使用了宏,就不用写那么多行来算 ($t0 * 8 + $t1) * 4 了 sw $v0, matrix($t2) # matrix[$t0][$t1] = $v0 addi $t1, $t1, 1 j in_j in_j_end: addi $t0, $t0, 1 j in_i in_i_end: # 这里使用了循环嵌套,和输入的时候同理 li $t0, 0 out_i: beq $t0, $s0, out_i_end li $t1, 0 out_j: beq $t1, $s1, out_j_end getindex($t2, $t0, $t1) lw $a0, matrix($t2) # $a0 = matrix[$t0][$t1] li $v0, 1 syscall la $a0, str_space li $v0, 4 syscall # 输出一个空格 addi $t1, $t1, 1 j out_j out_j_end: la $a0, str_enter li $v0, 4 syscall # 输出一个回车 addi $t0, $t0, 1 j out_i out_i_end: li $v0, 10 syscall 事实上,在做矩阵转化一题时,我用了利用一维数组模拟二维数组 ...

September 11, 2023 · 3 min · sudo