资源描述
,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,*,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,*,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,*,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,*,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,*,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,*,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,*,6.2 子例程示例电子课件 计算机系统基础:C语言视角(RISC-V版),子例程,示例,示例:除法运算,计算两个正数的除法,采用与十进制除法相同的算法,根据余数减除数够减与否确定商,二进制除法的商只有,1,或,0,,若够减则商,1,,否则商,0,具体过程如下:,1.,不够减:将余数和除数作比较,除数比较大,商的对应位上为,0,;,2.,够减:将余数减去除数,商的对应位上为,1,,计算出新的余数;,3.,重复以上的过程,直到计算完被除数的最后一位。就得到了商和余数。,以,63,6,为例,以,8,位二进制整数表示,除法结果:商是,00001010,,即十进制数,10,,余数是,011,,即,3,7,6,5,4,3,2,1,0,0,0,0,0,1,0,1,0,0,0,0,0,0,1,1,0,0,0,1,1,1,1,1,1,-,0,1,1,0,0,1,1,1,-,0,1,1,0,0,1,1,Divide,子例程,被除数在,x10,中,除数在,x11,中,计算出来的商在,x9,中,余数在,x18,中,#,计算除法子例程,#,初始化,Divide:andix9,x9,0,#x9,,商,,31,位为,0,,即表示正数,addix18,x0,0,#x18,,余数为,0,addi x8,x0,32,#,循环次数,luix5,0 x80000,#,掩码,#,循环任务,#,新的,余数:上一步得到的余数和被除数的对应位结合,Dloop:sllix9,x9,1,sllix18,x18,1,andx6,x10,x5,beqzx6,r0,orix18,x18,1,#,商,0,或,1,,够减则更新余数,r0:bltx18,x11,Dnext,#,是否够减?,subx18,x18,x11,#,新的余数,-,余数,-,除数,orix9,x9,1,#,商的相应位上为,1,#,被除数的下一位,Dnext:sllix10,x10,1,#,循环次数,addi x8,x8,-1,beqzx8,Dexit,jDloop,#,从子例程返回,Dexit:ret,示例:字符串逆序,假设字符串的起始地址位于,x11,中,字符串长度在,x10,中,将原字符串替换为逆序结果,流程图,计数器控制,的循环,循环的次数为字符串长度的,1/2,重复执行的任务为:,交换,第一个字符和最后一个字符,第二个字符和倒数第二个字符,第三个字符和倒数第三个字符,直到交换结束,StrReverse,子例程,StrReverse:addx8,x11,x10,addix8,x8,-1,sraix10,x10,1,#x10-x10/2,,交换次数,Rloop:beqzx10,Rexit,lbx6,0(x11),#,交换字符,lbx7,0(x8),sbx7,0(x11),sbx6,0(x8),addix10,x10,-1,addix8,x8,-1,addix11,x11,1,jRloop,Rexit:ret,示例:数据类型转换,在二进制补码整数和,ASCII,码字符序列之间进行数据类型转换,C,语言中,格式化输出函数,printf,“,%d”,将一个存储在计算机中的二进制补码整数转化为对应的十进制数的,ASCII,码字符序列,/,字符串,格式化输入函数,scanf,“,%d”,将输入的,ASCII,码字符序列转换为二进制补码整数,假设二进制补码整数在寄存器,x10,中,字符序列的起始地址位于,x11,中,Str2Int,子例程:,ASCII,码,字符序列,二进制补码整数,Int2Str,子例程:,二进制补码整数,ASCII,码,字符序列,Str2Int,子例程,正的十进制数的,ASCII,码字符串,二进制补码整数,假如,ASCII,码字符串被存储在从,0 x1000 0000,开始的连续存储单元中,以非数字字符结束,例:一个三位数“,123”,,以,换行符,0 x0A,结束,流程图,:,Str2Int,标志控制,的循环,标志:遇到非数字字符,从高位开始转换,Str2Int,子例程,01,#,02,#,将一个正的十进制数的,ASCII,码字符串转换成二进制补码整数;,03,#,ASCII,码字符串被存储在从,Inbuf,开始的存储单元中;,04,#,x10,用来存储结果。,05,#,06.,data,07.align2,08 SaveReg1:.word0,09 SaveReg2:.word0,0,0A Inbuf:.byte49,50,51,10,0B,#,0C.text,0E.align2,0F.globlmain,10 main:.,#,省略,11,callStr2Int,12.,#,省略,13,jend,14,#Str2Int,子例程,15,Str2Int:addix10,x0,0,#x10,用于存储结果,16,lax11,Inbuf,#x11,指向,ASCII,码字符串,17,#,循环任务,18,S2ILoop:lbx8,0(x11),#,从最高位开始,依次取出,ASCII,码,19,addix8,x8,-48,#,转换为整数,1,A,#,遇到非数字字符,?,1,B bltzx8,DoneS2I,1Caddix6,x0,10,1Dbgex8,x6,DoneS2I,1E,#,计算,x10-x10*10+x9,1Flax6,SaveReg2,20swx11,0(x6),#caller-save,21 swx1,4(x6),#caller-save,22addix11,x0,10,#x11,,,乘数,23,callMultiply,#x9=x10*10,24mvx5,x9,25lax6,SaveReg2,26lwx11,0(x6),#,寄存器恢复,27,lwx1,4(x6),#,寄存器恢复,28,addx10,x8,x5,29addix11,x11,1,#x11,指向下一个字符,2,AjS2ILoop,2B,#,从子例程返回,2,C DoneS2I:ret,2D,#Multiply,子例程,2,E Multiply:lax5,SaveReg1,2F,#.#,省略,30,ret,31,#,32 end:.,#,下一个任务,回顾,Multiply,子例程,采用,callee-save,策略,保存,/,恢复寄存器,x8,的值,调用,Multiply,子例程返回后,,x8,的值仍然是调用前的值(,28,行),问题,1,计算,x10*x11,22,行,把乘数,x11,先赋值为,10,23,行,调用,Multiply,问题:,在,Multiply,子例程中,,x11,被改为,0,!,x11,在,Str2Int,子例程中,是用于指向字符的指针,在调用返回后,还要用到这个字符指针,如何解决这个问题?,caller-save,(调用者保存),由,调用者,完成寄存器的保存,/,恢复工作,09,行:使用数据区的存储单元作为保存寄存器的空间,1F,和,20,行:将,x11,的值保存到预留的空间中,25,和,26,行:将,x11,的值恢复,.,09 SaveReg2:.word0,0,.,1Flax6,SaveReg2,20swx11,0(x6),#caller-save,.,22addix11,x0,10,#x11,,,乘数,23,callMultiply,#x9=x10*10,.,25lax6,SaveReg2,26lwx11,0(x6),#,寄存器恢复,.,29addix11,x11,1,#x11,指向下一个字符,问题,2,在执行,call Multiply,伪指令后,,寄存器,x1,发生了改变,执行,call,伪指令(,23,行),即,jalr,指令后,,x1,的值为,24,行的,PC,值,问题:,到,2C,行,执行,ret,伪指令,从子例程返回,该指令为,jalr x0,0(x1),,只能返回到,24,行,!,而,不能返回到,12,行,(调用,Str2Int,子例程后的下一条指令),caller-save,(调用者保存),由,调用者,完成寄存器的保存,/,恢复工作,09,行:使用数据区的存储单元作为保存寄存器的空间,21,行:将,x1,的值保存到预留的空间中,27,行:将,x1,的值恢复,.,09 SaveReg2:.word0,0,.,1Flax6,SaveReg2,.,21swx1,4(x6),#caller-save,.,23,callMultiply,#x9=x10*10,.,25lax6,SaveReg2,.,27lwx1,4(x6),#,寄存器恢复,.,2C DoneS2I:ret,Int2Str,子例程,正的二进制补码整数,十进制数的,ASCII,码字符串,x10,,,二进制补码整数,x19,,记录整数位数,流程图:,Int2Str,Int2Str,子例程,#,#,将一个正的二进制补码整数转换为一个,ASCII,码字符串。,#,二进制补码整数在,x10,中。,#,转换后的,ASCII,码字符串被存储在从,Outbuf,开始的存储单元中;,x19,记录整数位数。,#,Int2Str:lax11,Outbuf#x11,指向起始单元,addix19,x0,0#x19,,位数,#,#,子任务,1,,将二进制数转换为,ASCII,码字符串,按照从个位到最高位的顺序存储,#,STask1:lax7,SaveReg3,swx11,0(x7),#caller-save,swx1,4(x7),#caller-save,addix11,x0,10,#,除数,callDivide,mvx6,x18,#,余数,mvx10,x9,#,商,lax7,SaveReg3,lwx11,0(x7),#,寄存器恢复,lwx1,4(x7),#,寄存器恢复,addix6,x6,48,#,余数转换为,ASCII,字符,sbx6,0(x11),#,存储,addix19,x19,1,#,位数加,1,addix11,x11,1,#x11,指向下一个单元,beqzx10,STask2,#,没有位数需要处理,jSTask1,#,#,子任务,2,,字符串逆序,#,STask2:lax11,Outbuf,#x11,指向起始单元,mvx10,x19,#x10,,,位数,lax7,SaveReg3,swx1,0(x7),#caller-save,callStrReverse,#,从子例程返回,lax7,SaveReg3,lwx1,0(x7),#,寄存器恢复,ret,寄存器的保存,/,恢复,如果一个寄存器内的值将在该寄存器的值被改变之后再次被用到,必须在其值,被改变之前将其保存,,在再次,使用它之前将其恢复,通过将寄存器的值存进内存,来保存它的值,通过把它加载回寄存器,来恢复它的值,策略,如果调用子例程将造成某些寄存器值的改变,采用,caller-save,(调用者保存)或,callee-save,(被调用者保存)策略,进行寄存器的保存和恢复,注意:如果在子例程中又调用了子例程,必须采用,caller-save,(调用者保存)的策略,保存,/,恢复,返回地址,x1,
展开阅读全文