《6.2 子例程示例电子课件 计算机系统基础:C语言视角(RISC-V版).ppt》由会员分享,可在线阅读,更多相关《6.2 子例程示例电子课件 计算机系统基础:C语言视角(RISC-V版).ppt(27页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、6.2 子例程示例电子课件 计算机系统基础:C语言视角(RISC-V版)子例程子例程示例示例示例:除法运算示例:除法运算计算两个正数的除法,采用与十进制除法相同的算计算两个正数的除法,采用与十进制除法相同的算法法根据余数减除数够减与否确定商根据余数减除数够减与否确定商二进制除法的商只有二进制除法的商只有1 1或或0 0,若够减则商,若够减则商1 1,否则商,否则商0 0具体过程如下:具体过程如下:1.1.不够减:将余数和除数作比较,除数比较大,商的对应位上为不够减:将余数和除数作比较,除数比较大,商的对应位上为0 0;2.2.够减:将余数减去除数,商的对应位上为够减:将余数减去除数,商的对应位
2、上为1 1,计算出新的余数;,计算出新的余数;3.3.重复以上的过程,直到计算完被除数的最后一位。就得到了商和余重复以上的过程,直到计算完被除数的最后一位。就得到了商和余数。数。以以6363 6 6为例,以为例,以8 8位二进制整数表示位二进制整数表示除法结果:商是除法结果:商是0000101000001010,即十进制数,即十进制数1010,余,余数是数是011011,即,即3 37 6 5 4 3 2 1 0000010100000011000111111-01100111-0110011DivideDivide子例程子例程被除数在被除数在x10 x10中,除数在中,除数在x11x11中,
3、计算出来的商中,计算出来的商在在x9x9中,余数在中,余数在x18x18中中#计算除法子例程计算除法子例程#初始化初始化Divide:andix9,x9,0#x9,商,商,31位为位为0,即表示正数,即表示正数addix18,x0,0#x18,余数为,余数为0 addi x8,x0,32#循环次数循环次数 luix5,0 x80000#掩码掩码#循环任务循环任务#新的新的余数:上一步得到的余数和被除数的对应位结合余数:上一步得到的余数和被除数的对应位结合Dloop:sllix9,x9,1sllix18,x18,1 andx6,x10,x5 beqzx6,r0 orix18,x18,1#商商0或
4、或1,够减则更新余数,够减则更新余数r0:bltx18,x11,Dnext#是否够减?是否够减?subx18,x18,x11#新的余数新的余数-余数余数-除数除数orix9,x9,1#商的相应位上为商的相应位上为1#被除数的下一位被除数的下一位 Dnext:sllix10,x10,1#循环次数循环次数 addi x8,x8,-1beqzx8,DexitjDloop#从子例程返回从子例程返回Dexit:ret示例:字符串逆序示例:字符串逆序假设字符串的起始地址位于假设字符串的起始地址位于x11x11中,字符串长中,字符串长度在度在x10 x10中中将原字符串替换为逆序结果将原字符串替换为逆序结果
5、流程图流程图计数器控制计数器控制的循环的循环循环的次数为字符串长度的循环的次数为字符串长度的1/21/2重复执行的任务为:重复执行的任务为:交换交换第一第一个字符和最后一个字符,第二个字符和最后一个字符,第二个字符和倒数第二个字符,第个字符和倒数第二个字符,第三个字符和倒数第三个字符三个字符和倒数第三个字符直到交换结束直到交换结束StrReverse子例程子例程StrReverse:addx8,x11,x10addix8,x8,-1sraix10,x10,1#x10-x10/2,交换次数,交换次数Rloop:beqzx10,Rexit lbx6,0(x11)#交换字符交换字符 lbx7,0(x
6、8)sbx7,0(x11)sbx6,0(x8)addix10,x10,-1addix8,x8,-1addix11,x11,1jRloopRexit:ret示例:数据类型转换示例:数据类型转换在二进制补码整数和在二进制补码整数和ASCIIASCII码字符序列之间进码字符序列之间进行数据类型转换行数据类型转换C C语言中,格式化输出函数语言中,格式化输出函数printfprintf“%d”%d”将一个存储在计算机中的二进制补码整数转化为对应的将一个存储在计算机中的二进制补码整数转化为对应的十进制数的十进制数的ASCIIASCII码字符序列码字符序列/字符串字符串格式化输入函数格式化输入函数scan
7、fscanf“%d”%d”将输入的将输入的ASCIIASCII码字符序列转换为二进制补码整数码字符序列转换为二进制补码整数假设二进制补码整数在寄存器假设二进制补码整数在寄存器x10 x10中,字符序中,字符序列的起始地址位于列的起始地址位于x11x11中中Str2IntStr2Int子例程:子例程:ASCIIASCII码码字符序列字符序列 二进制补码整数二进制补码整数Int2StrInt2Str子例程:子例程:二进制补码整数二进制补码整数 ASCIIASCII码码字符序列字符序列 Str2IntStr2Int子例程子例程正的十进制数的正的十进制数的ASCIIASCII码字符串码字符串 二进制补
8、码整数二进制补码整数假如假如ASCIIASCII码字符串被存储在从码字符串被存储在从0 x1000 00000 x1000 0000开始的连开始的连续存储单元中,以非数字字符结束续存储单元中,以非数字字符结束例:一个三位数例:一个三位数“123”123”,以,以换行符换行符0 x0A0 x0A结束结束流程图流程图:Str2IntStr2Int标志控制标志控制的循环的循环标志:遇到非数字字符标志:遇到非数字字符从高位开始转换从高位开始转换Str2IntStr2Int子例程子例程01 01#02 02#将一个正的十进制数的将一个正的十进制数的ASCIIASCII码字符串转换成二进制补码整数;码字符
9、串转换成二进制补码整数;03 03#ASCIIASCII码字符串被存储在从码字符串被存储在从InbufInbuf开始的存储单元中;开始的存储单元中;04 04#x10 x10用来存储结果。用来存储结果。05 05#0606.datadata0707.align.align2 208 SaveReg1:08 SaveReg1:.word.word0 009 SaveReg2:09 SaveReg2:.word.word0,00,00A Inbuf:0A Inbuf:.byte.byte49,50,51,1049,50,51,100B 0B#0C0C.text.text0E0E.align.ali
10、gn2 20F0F.globl.globlmainmain10 main:10 main:.#省略省略11 11 callcallStr2IntStr2Int1212.#省略省略1313j jendend14 14#Str2Int#Str2Int子例程子例程15 15 Str2Int:Str2Int:addiaddix10,x0,0 x10,x0,0#x10#x10用于存储结果用于存储结果 16 16 lalax11,Inbufx11,Inbuf#x11#x11指向指向ASCIIASCII码字符串码字符串17 17#循环任务循环任务18 18 S2ILoop:S2ILoop:lblbx8,0(
11、x11)x8,0(x11)#从最高位开始,依次取出从最高位开始,依次取出ASCIIASCII码码19 19 addiaddix8,x8,-48x8,x8,-48#转换为整数转换为整数1 1A A#遇到非数字字符遇到非数字字符?1 1B B bltzbltzx8,DoneS2Ix8,DoneS2I1C1Caddiaddix6,x0,10 x6,x0,101D1Dbgebgex8,x6,DoneS2Ix8,x6,DoneS2I1E 1E#计算计算x10-x10*10+x9x10-x10*10+x91F1Flalax6,SaveReg2x6,SaveReg22020swswx11,0(x6)x11,
12、0(x6)#caller-save#caller-save21 21 swswx1,4(x6)x1,4(x6)#caller-save#caller-save2222addiaddix11,x0,10 x11,x0,10#x11#x11,乘数乘数2323callcallMultiplyMultiply#x9=x10*10#x9=x10*102424mvmvx5,x9x5,x92525lalax6,SaveReg2x6,SaveReg22626lwlwx11,0(x6)x11,0(x6)#寄存器恢复寄存器恢复27 27 lwlwx1,4(x6)x1,4(x6)#寄存器恢复寄存器恢复2828add
13、addx10,x8,x5x10,x8,x52929addiaddix11,x11,1x11,x11,1#x11#x11指向下一个字符指向下一个字符2 2A Aj jS2ILoopS2ILoop2B 2B#从子例程返回从子例程返回2 2C DoneS2I:C DoneS2I:retret2D 2D#Multiply#Multiply子例程子例程2 2E Multiply:E Multiply:lalax5,SaveReg1x5,SaveReg12F 2F#.#省略省略3030retret31 31#32 end:32 end:.#下一个任务下一个任务回顾回顾MultiplyMultiply子例程
14、子例程采用采用callee-savecallee-save策略策略保存保存/恢复寄存器恢复寄存器x8x8的值的值调用调用MultiplyMultiply子例程返回后,子例程返回后,x8x8的值仍然是调用的值仍然是调用前的值(前的值(2828行)行)问题问题1 1计算计算x10*x11x10*x112222行,把乘数行,把乘数x11x11先赋值为先赋值为10102323行,调用行,调用MultiplyMultiply问题:问题:在在MultiplyMultiply子例程中,子例程中,x11x11被改为被改为0 0!x11x11在在Str2IntStr2Int子例程中,是用于指向字符的指针,子例程
15、中,是用于指向字符的指针,在调用返回后,还要用到这个字符指针,如何解在调用返回后,还要用到这个字符指针,如何解决这个问题?决这个问题?caller-savecaller-save(调用者保存)(调用者保存)由由调用者调用者完成寄存器的保存完成寄存器的保存/恢复工作恢复工作0909行:使用数据区的存储单元作为保存寄存器的空间行:使用数据区的存储单元作为保存寄存器的空间1F1F和和2020行:将行:将x11x11的值保存到预留的空间中的值保存到预留的空间中2525和和2626行:将行:将x11x11的值恢复的值恢复.09 SaveReg2:.word 0,0.1Flax6,SaveReg220sw
16、x11,0(x6)#caller-save.22addix11,x0,10#x11,乘数乘数23callMultiply#x9=x10*10.25lax6,SaveReg226lwx11,0(x6)#寄存器恢复寄存器恢复.29addix11,x11,1#x11指向下一个字符指向下一个字符问题问题2 2在执行在执行call Multiplycall Multiply伪指令后,伪指令后,寄存器寄存器x1x1发发生了改变生了改变执行执行callcall伪指令(伪指令(2323行),即行),即jalrjalr指令后,指令后,x1x1的的值为值为2424行的行的PCPC值值问题:问题:到到2C2C行,执
17、行行,执行retret伪指令,从子例程返回伪指令,从子例程返回该指令为该指令为jalr x0,0(x1)jalr x0,0(x1),只能返回到,只能返回到2424行行!而而不能返回到不能返回到1212行行(调用(调用Str2IntStr2Int子例程后的下子例程后的下一条指令)一条指令)caller-savecaller-save(调用者保存)(调用者保存)由由调用者调用者完成寄存器的保存完成寄存器的保存/恢复工作恢复工作0909行:使用数据区的存储单元作为保存寄存器的空间行:使用数据区的存储单元作为保存寄存器的空间2121行:将行:将x1x1的值保存到预留的空间中的值保存到预留的空间中272
18、7行:将行:将x1x1的值恢复的值恢复.09 SaveReg2:.word 0,0.1Flax6,SaveReg2.21swx1,4(x6)#caller-save.23callMultiply#x9=x10*10.25lax6,SaveReg2.27lwx1,4(x6)#寄存器恢复寄存器恢复.2C DoneS2I:retInt2StrInt2Str子例程子例程正的二进制补码整数正的二进制补码整数 十进制数的十进制数的ASCIIASCII码字符串码字符串x10 x10,二进制补码整数二进制补码整数 x19x19,记录整数位数,记录整数位数流程图:流程图:Int2StrInt2StrInt2St
19、rInt2Str子例程子例程#将一个正的二进制补码整数转换为一个将一个正的二进制补码整数转换为一个ASCIIASCII码字符串。码字符串。#二进制补码整数在二进制补码整数在x10 x10中。中。#转换后的转换后的ASCIIASCII码字符串被存储在从码字符串被存储在从OutbufOutbuf开始的存储单元中;开始的存储单元中;x19x19记录整数位数。记录整数位数。#Int2Str:Int2Str:lalax11,Outbufx11,Outbuf#x11#x11指向起始单元指向起始单元addiaddix19,x0,0 x19,x0,0#x19#x19,位数,位数#子任务子任务1 1,将二进制数
20、转换为,将二进制数转换为ASCIIASCII码字符串,按照从个位到最高位的顺序存储码字符串,按照从个位到最高位的顺序存储#STask1:STask1:lalax7,SaveReg3x7,SaveReg3swswx11,0(x7)x11,0(x7)#caller-save#caller-saveswswx1,4(x7)x1,4(x7)#caller-save#caller-saveaddiaddix11,x0,10 x11,x0,10#除数除数callcallDivideDividemvmvx6,x18x6,x18#余数余数mvmvx10,x9x10,x9#商商lalax7,SaveReg3x7
21、,SaveReg3lwlwx11,0(x7)x11,0(x7)#寄存器恢复寄存器恢复lwlwx1,4(x7)x1,4(x7)#寄存器恢复寄存器恢复addiaddix6,x6,48x6,x6,48#余数转换为余数转换为ASCIIASCII字符字符sbsbx6,0(x11)x6,0(x11)#存储存储addiaddix19,x19,1x19,x19,1#位数加位数加1 1addiaddix11,x11,1x11,x11,1#x11#x11指向下一个单元指向下一个单元beqzbeqzx10,STask2x10,STask2#没有位数需要处理没有位数需要处理j jSTask1STask1#子任务子任务
22、2 2,字符串逆序,字符串逆序#STask2:STask2:lalax11,Outbufx11,Outbuf#x11#x11指向起始单元指向起始单元mvmvx10,x19x10,x19#x10#x10,位数位数lalax7,SaveReg3x7,SaveReg3swswx1,0(x7)x1,0(x7)#caller-save#caller-savecallcallStrReverseStrReverse#从子例程返回从子例程返回lalax7,SaveReg3x7,SaveReg3lwlwx1,0(x7)x1,0(x7)#寄存器恢复寄存器恢复retret寄存器的保存寄存器的保存/恢复恢复如果一个
23、寄存器内的值将在该寄存器的值被改如果一个寄存器内的值将在该寄存器的值被改变之后再次被用到,必须在其值变之后再次被用到,必须在其值被改变之前将被改变之前将其保存其保存,在再次,在再次使用它之前将其恢复使用它之前将其恢复通过将寄存器的值存进内存,来保存它的值通过将寄存器的值存进内存,来保存它的值通过把它加载回寄存器,来恢复它的值通过把它加载回寄存器,来恢复它的值策略策略如果调用子例程将造成某些寄存器值的改变如果调用子例程将造成某些寄存器值的改变采用采用caller-savecaller-save(调用者保存)或(调用者保存)或callee-savecallee-save(被调用者保存)策略,进行寄存器的保存和恢复(被调用者保存)策略,进行寄存器的保存和恢复注意:如果在子例程中又调用了子例程,必须采注意:如果在子例程中又调用了子例程,必须采用用caller-savecaller-save(调用者保存)的策略,保存(调用者保存)的策略,保存/恢恢复复返回地址返回地址x1x1