MIPS汇编语言之内存数据的读写

967 阅读3分钟

「这是我参与11月更文挑战的第23天,活动详情查看:2021最后一次更文挑战」。

简介

内存空间布局

从mars中可以查看到内存分布起始物理地址

转成图后:

栈的伸缩在mips和x86架构中是由高地址往低地址进行伸缩, 在arm架构中可升序也可降序

内存碎片

在内存动态分配(heap区)过程中容易出现一些小且不连续的空闲内存区域,这些未被使用的内存称作内存碎片

分类:

  • 内部碎片:比如数据在内存中采用4个字节对齐的方式进行存储, 比如我们申请一块3个字节的空间用于存储一个数据,但是系统给我们分配了4个字节空间,这时多出来的一个字节的空间就被称之为内部碎片
  • 外部碎片:在我们进行内存回收和分配的时候容易出现外部碎片,比如我连续申请了三块4个字节的内存空间,当我释放第二块内存空间然后紧接着申请一块8个字节的空间,此时由于之前释放的4个字节空间太小无法使用,这就造成了内存块空闲,这种碎片叫做外部碎片

PC 寄存器

称作 程序计数寄存器(Program Counter Register) :用于存储程序即将要执行的指令所对应在内存中的实际物理地址, 如果改变该值可以让指令跳转到我们想要跳转的地方

如何修改pc寄存器中的值

使用以下转移指令

  1. jr指令

    jr 寄存器  #$ra寄存器实际上就是保存着函数调用前的pc寄存器的值
    
  2. jal指令

    jal 标号  #跳转的同时给$ra寄存器赋值
    
  3. j指令

    j 标号   #直接跳转
    

内存数据的读写

  1. 从指定内存中读取数据

    
    #整型数据
    lw $t0,0x10010000 #读取4个字节数据 赋值给t0寄存器
    ld $t0,0x10010000 #读取8个字节数据 赋值给t0和t1寄存器
    
    #单精度浮点数据
    lwc1   $f0,0x10010000  
    
    
    #双精度浮点数据
    ldc1   $f0,0x10010000  
    
    
    #字符数据 
    ### 由于字符数据是以ascii码16进制的形式存放到内存中,因此只能获取到ascii码值,该值属于整型数据,直接使用lw或者ld即可
    lw $t0,0x10010000 #读取4个字节数据 赋值给t0寄存器
    ld $t0,0x10010000 #读取8个字节数据 赋值给t0和t1寄存器
    

    从内存中读取数据的宽度取决于寄存器的大小,由于32位cpu寄存器最大存储32位数据,因此lw $t0表示一次性读取4个字节的数据到$t0寄存器, 如果想要连续读取八个字节的数据,那么需要使用ld $t0,表示一次性读取8个字节的数据到$t0,$t1连个连续的寄存器,

  2. 往指定内存中写入数据

    1. 第一种 数据定义的同时指定物理地址
    .data 0x10010020 ;将以下定义的数据存放在0x10010020这个物理地址
    	.ascii "a"
    	.ascii "b"
    	
    .data 0x10010000
    	.ascii "c"
    
    1. 第二种 在代码段中使用指令
    #整型数据
    li $s1,4
    sw $s1,0x10010000  ;将$s1寄存器中的数据存入0x10010000这个物理地址
    
    #单精度浮点数
    .data 
    	f1: .float 3.14
    	
    .text
    lwc1 $f2,f1
    swc1   $f2,0x10010000 
    
    
    #双精度浮点数
    .data 
    	d1: .double 3.14
    	
    .text
    ldc1 $f2,d1
    sdc1   $f2,0x10010000 
    

以上直接使用的是简单粗暴的十六进制表示物理地址,很多时候内存的地址会保存在寄存器中,你可能会看到以下写法:

lw $s1, $s2
sw $s1, $s2 

或者
lw $s1, 20($s2)
sw $s1, 20($s2) ;将地址往高位偏移20个字节 相当于sw $s1, 20+$s2


或者
lw $s1, -20($s2)
sw $s1, -20($s2) ;将地址往低位偏移20个字节

注意: 往指定内存中读取写入数据时,代码段不允许直接写入和读取