在MIPS架构中,许多包含立即数字段的指令格式只预留了16位的空间来存放立即数。那如果使用32位的立即数该怎么办呢?

这时就需要采用特殊的编码方式来加载或存储32位的常数值以及使用完整的32位地址,需要采用两条指令来实现:

1.lui(Load Upper Immediate):将最高16位的立即数载入到目标寄存器的高16位,同时低16位清零。
2.ori(OR Immediate)或addi(Add Immediate):将剩余的低16位立即数与前一步得到的结果进行逻辑或( ori )操作,或者加法( addi ,如果目标寄存器之前已包含有效数据且希望累加的话),从而合成出完整的32位立即数。

例如,假设我们要将32位立即数0x12345678加载到寄存器t0中,我们需要这样操作:

lui $t0, 0x1234 #将高16位Ox1234载入$t0,低16位清零
ori $t0, $t0, 0x5678  #将低16位0x5678与$t0进行逻辑或操作

寻址方式 地址: 1.形式地址(A):通过A加以转化得到EA 2.有效地址(EA):真实地址

地址种类:
    1.寄存器编号
    2.内存地址
    3.I/O端口

寻址方式:是寻找指令或者操作数的有效地址的方式
    1.指令寻址:去寻找下一条指令的地址
        PC:程序计数器
            存放当前欲执行指令的地址,并且具有自动+1功能 
        
        (1)顺序寻址:直接通过PC+1(一条指令的长度),自动形成下一条指令的地址
        (2)跳跃寻址:转移类指令实现
            i.J 型指令格式的跳转指令
                J型指令是一种专门用于无条件跳转(Jump)的指令格式。在MIPS指令集中,J型指令的具体格式如下:
                    6位操作码:用于指示这是一个J型跳转指令。
                    26位地址字段:用于存储相对于当前PC(程序计数器)的偏移量。
                    
                    注:J型指令的寻址方式主要是变通的直接寻址(伪直接寻址):
                    跳转地址由指 26 位字段和 PC 高4位相连而成,最低两位补0(指令地址必定为四的倍数)
            ii.I 型指令格式的条件分支指令
                与无条件跳转指令不同,条件分支指令在跳转到目标地址之前会先比较两个操作数的值。
                如果满足比较条件,则执行跳转;否则,继续顺序执行下一条指令。具体来说:

                    1.两个操作数:这些操作数通常是从寄存器中获取的值,例如 rs (源寄存器1)和 rt (源寄存器2),用于比较它们之间的关系。

                    2.条件判断:不同的条件分支指令对应着不同的条件判断,如beq检查两者是否相等,bne检查两者是否不相等,blt则检查第一个操作数是否小于第二个操作数。

                    3.分支地址:如果满足特定条件,则程序计数器PC会被更新为指令中指定的目标地址,从而实现有条件地转移到新的指令序列继续执行。

                    注:条件分支指令的寻址方式是------>PC相对寻址
                    PC相对寻址(PC-Relative Addressing)是一种指令寻址方式,它使用当前程序计数器(ProgramCounter, PC)的值加上一个相对于当前指令地址的偏移量来确定下一条要执行的指令或数据的位置。

                    具体来说,在PC相对寻址中,指令包含了一个表示偏移量的字段。当CPU执行到含有PC相对寻址的跳转指令时,它会将当前PC值与该偏移量相加,得到的目标地址就是跳转的目的地。
                    这种方式使得程序员可以编写出与加载地址无关的代码,增强了程序的可移植性,并且简化了硬件设计,因为不需要复杂的计算逻辑去生成绝对地址。

                    优点:简洁性和可预测性。
                    缺点:限制了分支目标的范围,因为偏移量的大小是有限的。不利于代码的重定位和共享。

    注:寻址附近的指令也是加速大概率事件的另外一个例子 。

    2.数据寻址:去寻找本条指令的操作数的有效地址 
        (1)立即(数)寻址
            操作数直接包含在指令中,作为指令的一部分。这种情况下,操作数的值是一个立即数,即固定值。

            立即寻址模式通常用于加载立即数到寄存器中,或者用于算术和逻辑运算。
            MIPS 指令集并没有像某些其他指令集那样直接支持“MOV”指令,但可以通过其他方式实现寄存器到寄存器的数据移动。

            优点:速度快,不需要访问内存
            缺点:位数有限,立即数大小有限
        (2)直接寻址(EA == A)
            指令中包含一个实际的内存地址,这个地址直接指向内存中的一个位置,从中读取或写入操作数,CPU直接使用这个地址从内存中读取操作数。
            在 MIPS 指令集中,直接寻址模式通常用于访问内存中的特定地址。在直接寻址中,指令中直接包含了要访问的内存地址。

            直接寻址在 MIPS 指令集中是一种简单而直接的方式来访问内存中的特定位置。
            然而,由于其限制和可能的性能影响,它通常只在需要直接访问特定内存地址时使用。所以其实并不常用。

            优点:简单
            缺点:限制了寻址范围
        (3)寄存器寻址
            操作数存储在某个寄存器中。指令中通常包含一个寄存器标识符,处理器从该寄存器中读取或写入操作数。
            寄存器寻址通常比内存寻址更快,因为寄存器位于 CPU 内部,访问它们不需要像访问内存那样经过复杂的内存子系统。

            MIPS 指令集中的寄存器寻址非常灵活,因为大多数指令都可以接受来自寄存器或立即数的操作数。
            寄存器寻址不仅限于算术和逻辑运算,还可以用于数据移动、比较、跳转等操作。
            寄存器寻址是 MIPS 指令集中非常基础和重要的寻址模式,它允许 CPU 快速、高效地处理数据,而不需要频繁地访问内存。

            优点:快速,不需要访问内存;简单
            缺点:寄存器个数有限,造价昂贵
        (4)间接寻址( EA == (A) )
            指令中给出的地址码是存放操作数有效地址的主存单元地址。

            优点:扩大了寻址范围
            缺点:慢,多次访问内存

            寄存器间接寻址:通过寄存器间接寻址实现。
            这意味着指令的操作数地址不是直接给出的,而是存储在一个寄存器中,处理器会访问该寄存器内容所指向的内存地址来获取实际操作数
        (5)变址寻址
            以某个寄存器的内容为偏移量,加上指令中给出的基地址来计算操作数的实际地址。
            这种寻址模式在处理数组或循环时特别有用,因为它允许程序在不需要修改基址寄存器的情况下,通过改变偏移量来访问内存中的不同位置。

            变址寄存器:变址寄存器是面向用户的,在程序执行过程中,变址寄存器的内容可由用户改变(作为偏移(量),形式地址A不变(作为基地址))。

            优点:扩大了寻址范围
            缺点:必须使用一个寄存器存储偏移量
        (6)基址寻址
            类似于变址寻址,但这里的基准是固定的基址寄存器,加上一个偏移量得到操作数地址。
            有效地址是通过将基址寄存器的内容与指令中给出的偏移量相加而得到的。基址寄存器的内容在程序执行过程中通常保持不变,而偏移量可以变化。

            这种寻址模式常用于未多道程序分配存储空间

            例. lw $t0, 12($t1) 

            在 MIPS 指令集中,并没有专门的基址寻址指令,而是通过组合寄存器寻址和内存访问指令来实现基址寻址的效果。
            偏移量可以是一个立即数(如上例中的 12),也可以是一个寄存器的值,这提供了很大的灵活性。


            注:
            EA = (I) + A
            EA = (BR) + A
            EA = (PC) + A
            统称为偏移寻址

注:数据寻址方式和指令寻址方式并无绝对的界限,可交叉使用

/************************************************/

并行、 指令和同步

当任务之间相互独立的时候,任务的并行执行是比较容易的。
而大多数时候,往往是很多任务要协作进行,共享资源进行读取操作。
当多个任务共享资源并进行读写操作时,必须确保它们之间以一种有序且安全的方式进行协作,防止数据不一致和错误结果。

为了确保数据的一致性和正确性,防止数据竞争和条件竞争(race conditions)的发生,我们发明了同步机制。

同步机制是确保在多线程或多处理器环境中正确协调共享资源访问的关键技术。
这些同步机制依赖于硬件提供的同步指令,这些指令允许软件开发者在编程时创建互斥区域,确保在任何时候只有一个线程或处理器能够访问特定的代码段或数据。

加锁(lock)和解锁(unlock)是同步机制中最基础的操作。

硬件提供的同步指令是实现加锁和解锁操作的基础。

除了简单的加锁和解锁操作外,更复杂的同步机制如条件变量、信号量、读写锁(操作系统中会详细见到这些东西)等也是基于类似的原理实现的。
这些机制提供了更细粒度的控制和更灵活的同步方式,以适应不同的并发场景和需求。

虽然同步机制可以有效地协调共享资源的访问,但它们也可能引入性能开销和死锁等问题。
因此,在使用同步机制时,需要仔细考虑其适用场景和潜在风险,并采取适当的措施来避免或解决这些问题。