translate C recursive function into MIPS

Translate C recursive function into MIPS ​ 如何规范合理地把C语言中的递归函数翻译成MIPS assembly?这个问题令我头疼了一天,翻了网上很多教程总是感觉说的很浅,或者是重复着用MIPS编写计算阶乘的例子,找不到合适的教程对于一个迷茫的新手来说是一件非常绝望的事,经过我求助身边的大佬们,大佬们的一些分享,让我逐渐明白编写中的一些要点,并总结出一些编写规则。 一.C中的递归函数 ​ 对于编写MIPS程序,我们一般是先写出对应的C代码,再一句句翻译成MIPS语言。递归函数也是函数,从函数类型上看,应该有无返回值和有返回值这两种粗浅的大类,其中无返回值是一种值得注意的类型。 1.无返回值类型 ​ 在无返回值的递归函数中,我们往往只能看见递归层次中的return,但是实际上无返回值类型的返回语句可以省略,即程序运行结束时的return;可以省略,在翻译时,题目可能就会使用省略这一种写法,需要注意这个“隐藏”的return并在MIPS中自行编写。例如如i下C程序 1 2 3 4 5 6 7 8 9 10 #include<stdio.h> void print() { printf("hello world!"); (return;) } int main() { print(); } 2.有返回值类型 ​ 有返回值类型常见的为int等,有返回值类型的函数必须“显式“地说明出返回值,如果有分支,则在每个分支中都需要进行返回值的说明,这一类函数可以明显地看出哪里需要return对于编写比较友好,例如计算阶乘的示例 ...

October 22, 2023 · 5 min · sudo

P2课下&&MIPS常用宏定义

P2课下&&常用MIPS宏定义 ​ MIPS是一门非常灵活的语言,在编写过程中可以非常直观地感受到对内存的操作(虽然我编写MIPS都是照着C语言一句一句翻译),但也正因为是这样,使得MIPS编码看起来有一点繁琐,我们可以使用自己定义的宏macro进行代码风格的改善,将可以复用的代码抽离出来,类似于C语言中的函数。 1. MIPS——macro ​ MIPS中宏定义的方法是 //无参数宏定义 .macro name //statement .end_macro //有参数宏定义 .macro name(%parameter1,%parameter2,...) //statement .end_macro 1. end .macro end li $v0,10 syscall .end_macro 2. readinteger .macro readinteger(%des) li $v0,5 syscall move %des,$v0 .end_macro 3. printinteger .macro printinterger(%src) li $v0,1 move $a0,%src syscall .end_macro 4. printstr .macro printstr(%src) li $v0,4 la $v0,%src syscall .end_macro 5. 函数调用push .macro push(%src) sw %src,0($sp) subi ge$sp,$sp,4 .end_macro 6. 函数调用pop .macro pop(%des) addi $sp,$sp,4 lw %des,0($sp) .end_macro 7.一维数组计算地址偏移量 .macro getVectorAddress(%des,%col) sll %des,%col,2 .end_macro 8.二维数组计算地址偏移量 .macro getMatrixAddress(%des,%i,%j) multu %i,col(矩阵的列数) mflo %des add %des,%des,%j sll %des,%des,2 .end_macro 注意:一定是乘上矩阵的列数!QAQ之前乘了矩阵的行数还是看内存发现的bug ...

October 20, 2023 · 1 min · sudo

OOpre_HW_5:常见bug分析

OOpre_HW_5:常见bug分析 ​ 本次作业的任务比较简单,对课程组给出的代码进行debug,只有中测,让我这种挂了强测的鼠鼠好欣慰。 一.输入解析类错误 ​ 常见的有scanner一类的函数,我们需要注意的无非以下几个函数的功能 1 2 3 1. scanner.next(); //读取下一个字符串 2. scanner.nextint();//读取下一个数字 3. scanner.nextLine();//读取下一行字符串 ​ 作业中出现了几次使用scanner.nextLine()方法读取下一个字符串的错误,这种错误还是比较明显的。 二.深克隆与浅克隆 ​ 在我的作业代码中,对于深克隆和浅克隆共出现了一个功能中的两次错误,即克隆小队的操作和克隆士兵的操作。 1.浅克隆 ​ 浅克隆即只对对象的引用进行克隆,换句话说是创建出来新的一个指针,与原对象指向相同的一块内存空间。本质上两个引用指向的是同一个实例,一个指针对对象进行修改,另一个指针进行访问时就会体现出这种修改。例如: 1 2 Team team1 = new Team(123456,"dqr"); Team team2 = team1; 2.深克隆 ​ 深克隆不仅要创建出新的引用,还要开辟出新的内存空间,本质上就是一个新的变量,只不过变量的构造方法中传入参数是需要被克隆的对象的参数。 1 2 Team team1 = new Team(123456,"dqr"); Team team2 = new Team(team1.getID(),team1.getName()); 三.相等比较 ==/equals ? ==是比较两个引用是否是同一个对象 equals为内容比较,比如名字,咒语等等 四.遍历容器:迭代器删除 ​ 对于容器中的元素删除,我们可能经常会用到“边遍历边删除的操作” ...

October 18, 2023 · 1 min · sudo

OOpre_HW_4强测修复&&代码架构重构

HW_4强测修复&&代码重构 ​ **OOpre_HW_4是我打的最快的一次!**然而在代码风格上却不够面向对象,而且代码业务逻辑上有问题,没有成功通过强测(只得了33分),在挂掉强测之后,我痛定思痛,决定先重构代码架构再进行逻辑修复。 一.代码架构 ​ 上图是课程组推荐的代码架构,回顾我的第一版代码,主要有以下两个问题: 输入解析逻辑放在main类中,导致main代码冗长 没有对fightlog进行建类,而是将fightlog作为附属于adventure的数据处理,导致代码结构耦合复杂 ​ 经过一晚上的代码构想和助教的交流,我将代码架构修改为下图: ​ 在这次的代码逻辑中,我将fightlog视作一个个与adventure同级的个体建类(这个是最重要的思想,想了好久),fightlog中存储战斗日志的模式,攻击时间、攻击者的名字,被攻击者的名字ArrayList,这里需要注意,对于ArrayList<String> attackedname,应当分情况存储 mode == 1,此时attackedname == null mode == 2,此时attackedname中只有一个元素 mode == 3,此时attackedname中包含所有被攻击者的元素 二.bug修复 1.正则表达式修复 ​ 正则表达式出错使得战斗日志输入解析错误,导致后续从二维数组中读取时出现NullPointerException,这就是强测第一次的报错,只能说第一篇博客发早了,传播了错误的正则表达式。下面附上通过强测的正则表达式 1 2 3 Pattern p = Pattern.compile("(\\d{4}/\\d{2})-([^@#-]+)-([^@#-]+)"); Pattern p1 = Pattern.compile("(\\d{4}/\\d{2})-([^@#-]+)@([^@#-]+)-([^@#-]+)"); Pattern p2 = Pattern.compile("(\\d{4}/\\d{2})-([^@#-]+)@#-([^@#-]+)"); 2.对于携带概念的再纠正 ​ 我们知道,在第三次作业中,我对于携带的处理是为每个物品设置一个becarreid属性,在后续的处理中,如“使用”等操作,都需要进行是否“携带”概念的判断,在这次作业中,我发现了上次强测没有测出来的bug,OP9()中对于同名装备进行替换时,没有判断是否携带,下面附上改正后代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public void OP9(int i) { int advId = Integer.parseInt(inputInfo.get(i).get(1)); int equId = Integer.parseInt(inputInfo.get(i).get(2)); Adventure man = adventures.get(advId); Equipment equipment = null; ArrayList<Equipment> equipments = man.returnequ(man); for (Equipment item : equipments) { if (item.getID(item) == equId) { equipment = item; break; } } String name = equipment.getName(equipment); //检查当前想要携带的装备是否存在同名 如果有则进行替换 Equipment equipment1 = null; for (Equipment item : equipments) { if (item.getName(item).equals(name) && item.getBecarried(item)) { //已经被携带的同名装备 equipment1 = item; break; } } if (equipment1 != null) { equipment1.reset(equipment1); } equipment.set(equipment); } ​ 需要注意的是&&item.getBecarried(item),这种错误我出现了两次了,下次一定要注意,名字符合的同时要判断是否携带。 ...

October 18, 2023 · 1 min · sudo

P1上机日志

P1上机——verilog完成部件设计以及状态机 ​ 永远在周一晚上上机之前才是我学计组效率最高的时候 ​ ——地球人 一.三段式状态机的设计 ​ 在上机之前我心血来潮想要把自己编写状态机的代码风格从一直以来的一段式更新为三段式,在看了网上好多有的没的真真假假对对错错的写法之后,我决定转头求助蒋老师和曾老师,果然得到了靠谱的答案,并成功应用到晚上的上机中QAQ。 ​ 分段思路:就如同状态机的设计图,有三个模块:状态转移逻辑(组合逻辑),状态存储(时序逻辑),输出逻辑(组合逻辑),在verilog中分段实现即可,逻辑更加清晰。具体来说可以写成always-always-assign这种形式,当然最后一段也可以写成always. ​ 下面给出示例代码: ​ Moore型 输出逻辑中对状态进行判断 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 module moore_example( input in; input clk; input rst_n; output check; ); localparam s0 = 4'b0001; localparam s1 = 4'b0010; localparam s2 = 4'b0100; localparam s3 = 4'b1000; reg [3:0] state; reg [3:0] n_state; //状态存储逻辑 always@(posedge clk or negedge rst_b)begin if(!rst_n)begin state <= s0; end else begin state <= n_state; end end //状态转移逻辑 一般来说两层case嵌套逻辑更加清楚 always@(*)begin case(state)begin s0: begin case(in)begin 1'b1:n_state = s1; 1'b0:n_state = s0; end end //省略其他部分 default: n_state = s0; end end //输出逻辑 assign check = (state == s3)?1'b1:1'b0; ​ Mealy型 输出逻辑中对当前状态和输入进行判断 ...

October 16, 2023 · 3 min · sudo

verilog编写电路中的时序逻辑与组合逻辑分离

P1_L1_voter_plus时序逻辑与组合逻辑的分模块编写&&阻塞赋值、非阻塞赋值 一.问题提出 ​ 在完成P1_L1_voter_plus一题时,由于需要对输入中1的位数进行统计,我第一次使用到for循环语句来统计输入中1的个数,原码类似于下例。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 module test_for ( input [7:0] in, input clk, input rst, output reg [7:0] out ); integer i; always @(posedge clk or posedge rst) begin : test if(rst) begin out <= 8'b0; end else begin for(i=0;i<8;i=i+1) begin if(in[i]) out <= out + 1'b1; end end end endmodule ​ 此段代码并不能成功实现在一个时钟周期内实现统计个数的功能。仿真如下: ...

October 15, 2023 · 3 min · sudo

verilog

verilog(P1课前复习) 长文,做好战斗准备! 1.同步复位与异步复位 1.同步复位 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 //复位信号高电平有效 always@(posedge clk) begin if(clr) begin status <= 4'b0000; end else begin //statement end end //复位信号低电平有效 always@(posedge clk) begin if(!clr) begin status <= 4'b0000; end else begin //statement end end 2.异步复位 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 //复位信号高电平有效 always@(posedge clk or posedge clr) begin if(clr) begin status <= 4'b0000; end else begin //statement end end //复位信号低电平有效 always@(posedge clk or negedge clr) begin if(!clr) begin status <= 4'b0000; end else begin //statement end end ​ 异步复位将clr加入敏感符号列表 ...

October 14, 2023 · 5 min · sudo

P1_L1_BlockChecker

语句块模拟检查 P1_L1_BlockChecker 一.题目要求 1.题目描述 ​ 输入ASCII字母和空格,一个或多个连续出现的字母构成单词,单词不区分大小写,单词之间由一个或多个空格分隔开。检查工具检查自复位之后的输入中,begin和end是否能匹配。 注: 匹配的begin必须出现在end之前 一个begin只能匹配一个end 允许出现嵌套 出现不能按照规则匹配的begin或end,则匹配失败 保证在模块使用前进行复位 2.IO定义 信号名 方向 描述 clk I 时钟信号 reset I 异步复位信号 in[7:0] I 当前输入的ASCII码 result O 当前输入能否完成begin和end匹配 注意:输出result为“当前”的判断结果,即随着状态更新 2.状态转移图 解释: 所有的单词都以空格表示结束对应回到S0状态 如果之前没有出现过begin就出现end,之后无论输入什么都会输出0,设置为单独的状态S10,这个状态是自环的,无论输入什么都会回到S10,并输出0. 3.verilog代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 module BlockChecker ( input wire clk, input wire reset, input wire [7:0] in, output reg result ); reg [31:0] begin_cnt; reg [15:0] status; `define S0 16'b0000_0000_0000_0001 `define S1 16'b0000_0000_0000_0010 `define S2 16'b0000_0000_0000_0100 `define S3 16'b0000_0000_0000_1000 `define S4 16'b0000_0000_0001_0000 `define S5 16'b0000_0000_0010_0000 `define S6 16'b0000_0000_0100_0000 `define S7 16'b0000_0000_1000_0000 `define S8 16'b0000_0001_0000_0000 `define S9 16'b0000_0010_0000_0000 `define S10 16'b0000_0100_0000_0000 always @(posedge clk or posedge reset) begin if(reset) begin begin_cnt <= 32'b0; status <= `S0; result <= 1'b1; end else begin case (status) `S0: //empty begin if(in == " ") begin status <= `S0; end else if (in == "e"||in == "E") begin status = `S7; end else if (in == "b"||in == "B") begin status = `S1; end else begin status = `S6; end end `S1: //b begin if(in == "e"||in == "E") begin status = `S2; end else if (in == " ") begin status <= `S0; end else begin status <= `S6; end end `S2: //be begin if(in == "g"||in == "G") begin status <= `S3; end else if (in == " ") begin status <= `S0; end else begin status <= `S6; end end `S3: //beg begin if(in == "i"||in == "I") begin status <= `S4; end else if (in == " ") begin status <= `S0; end else begin status <= `S6; end end `S4: //begi begin if(in == "n"||in == "N") begin status <= `S5; result <= 1'b0; end else if (in == " ") begin status <= `S0; end else begin status <= `S6; end end `S5: begin if(in == " ") begin status <= `S0; begin_cnt <= begin_cnt + 1; end else begin status <= `S6; if(begin_cnt == 32'b0000) begin result <= 1'b1; end else begin result <= result; end end end `S6: begin if(in == " ") begin status <= `S0; end else begin status <= `S6; end end `S7: begin if(in == "n"||in =="N") begin status = `S8; end else if (in == " ") begin status <= `S0; end else begin status <= `S6; end end `S8: begin if(in == "d"||in == "D") begin if(begin_cnt == 32'b0001) //只剩余一个begin 可以完成配对 begin status <= `S9; result <= 1'b1; end else begin status <= `S9; result <= 1'b0; end end else if (in == " ") begin status <= `S0; end else begin status <= `S6; end end `S9: begin if(in == " ") //确定匹配到的一定是end begin if(begin_cnt == 32'b0) //还没有出现过begin 这种情况无论后便出现什么都是0 begin status <= `S10; result <= 1'b0; end else if(begin_cnt == 32'b1) begin status <= `S0; result <= 1'b1; begin_cnt = 32'b0000; end else if(begin_cnt > 32'b1) begin status <= `S0; result <= 1'b0; begin_cnt = begin_cnt - 32'b0001; end end else //匹配到的不是end begin status <= `S6; if(begin_cnt==32'b0) begin result <= 1'b1; end else begin result <= 1'b0; end end end `S10: begin status <= `S10; result <= 1'b0; end default: begin status <= status; result <= result; end endcase end end endmodule 4.一个坑点 `S5: begin if(in == " ") begin status <= `S0; begin_cnt <= begin_cnt + 1; end else begin status <= `S6; if(begin_cnt == 32'b0000) begin result <= 1'b1; end else begin result <= result; end end end ​ 在进行单词begin的匹配时,需要对是否成功的匹配到begin做判断,一开始在两种之间来回改,后来才想到这是两种情况应该使用分支结构: ...

October 13, 2023 · 4 min · sudo

P1_L3_ALU && Verilog中的符号处理

P1_L3_ALU && Verilog中的符号处理 1.问题引入 AC? WA? ​ 在做P1课下提交中的6处理ALU时,我发现了一件怪事,两个看似只是添加了变量差别的程序居然跑起来一个能够通过测试,一个不能,下面附上AC代码和WA代码。 AC代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 module alu ( input [31:0] A, input [31:0] B, input [2:0] ALUOp, output [31:0] C ); wire [31:0] add = A + B; wire [31:0] sub = A - B; wire [31:0] with = A & B; wire [31:0] huo = A | B; wire [31:0] logic_shift = A >> B; wire [31:0] math_shift = $signed(A) >>> B; assign C = (ALUOp == 3'b000) ? add: (ALUOp == 3'b001) ? sub: (ALUOp == 3'b010) ? with: (ALUOp == 3'b011) ? huo: (ALUOp == 3'b100) ? logic_shift: (ALUOp == 3'b101) ? math_shift: 32'b000; endmodule WA代码 ...

October 12, 2023 · 2 min · sudo

OOpre_HW4

OOpre_HW4 : 正则表达式 1.实现思路 ​ 这次作业实现思路上没有特别大难度(只新增了四条指令),但实际上作业体验下来相当于新增了一条指令,很多功能可以顺带着实现。即在我的做法中OP14()是进行战斗日志存储的方法,OP15(),OP16(),OP17(),只是将存好的战斗日志输出出来。 ​ 沿用“二维数组”的输入解析法,特判操作数为14时进行多行输入,引用变量row代表实际的行数(因为战斗日志不算在指令条数n内),利用正则表达式对输入的战斗日志进行解析,下面附上我的冗长的正则表达式 1 2 3 Pattern p = Pattern.compile("(\\d{4}/\\d{2})-([^\\s@#-]+)-([^\\s@#-]+)"); Pattern p1 = Pattern.compile("(\\d{4}/\\d{2})-([^\\s^@#-]+)@([^\\s@#-]+)-([^\\s@#-]+)"); Pattern p2 = Pattern.compile("(.*/.*)-(.*)@#-(.*)"); //这一条是助教改进的,还没太理解 ​ 之后按照题目叙述按部就班从二维数组中取出元素操作即可。这里我将战斗日志分为三个部分: ​ 注意:战斗日志的存储只能使用ArrayList只有这样才满有序性! 总表,在inputhandler中设置,在OP14()中读出后就将其加入总表,这样相当于沿着完整的时间线存入了战斗日志,对于OP15()的完成比较简单,只需要使用正则表达式从中提取出来,下面附上我的正则表达式(其实只需要对日期进行匹配) 1 Pattern p = Pattern.compile(date + ".+"); 下设在Adventure类中的attacklog和attackedlog分别记录这个人作为攻击者和被攻击的战斗记录,需要注意的是在实际操作中攻击者增加attacklog同时被攻击者要增加attackedlog。 ​ 沿着这个思路实现就好,但是助教说不够“面向对象”。(查我代码库QAQ)。 2.BUGS ​ 这次作业遇到的bug是我de时间最长的一次WWW.有很多粗心,也有一些逻辑上的不周到(第一遍写的时候没有反应过来),甚至还有笔误。这次作业我遇到的bug大部分都是输出错误,虽然要来回找很繁琐但是不值得记录,只有一个逻辑上的错误比较烦心,整整看了三个小时才通过比较AC输出调试出来,心态很崩 下面是错误代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public boolean useequ(Adventure man, Adventure man1,String name) { Equipment equipment = null; for (Equipment item : man.equipments) { if (item.getName(item).equals(name)) { equipment = item; break; } } if (equipment != null) { if(equipment.getBecarried(equipment)){ int level = man.level; man1.hitpoint = man1.hitpoint - equipment.getStar() * level; System.out.println(man1.getId(man1) + " " + man1.gethitpoint(man1)); return true; } } return false; } ​ 这种实现思路的错误之处在于:在我之前的迭代思路中,“背包”是一个概念而不是一个实体,在总库equipments中进行查找时,完全可能找到名字符合但是并没有携带的equipment(即但从名字找equipment不具有唯一性,可能会找错),这样就会使得永远也加不进去战斗日志,之前的迭代作业我们知道,一个人同名的装备只能有一件状态为carried,对于名字和是否携带的双重判断才是正确的逻辑。 ...

October 10, 2023 · 1 min · sudo