CS61C 2020计算机组成原理Lab03

news2024/9/20 5:10:02

Exercise 1: Familiarizing yourself with Venus

.data
.word 2, 4, 6, 8
n: .word 9

.text
main:   
		# 
    add t0, x0, x0
		# addi 是 "add immediate"(立即数加法)的缩写,表示这是一个加法指令,其中一个加数是一个立即数(即直接在指令中给出的数)
		# 将 x0 寄存器的值(即 0)和立即数 1 相加,然后将结果(0 + 1 = 1)存储在 t1 寄存器中。
		# 简而言之,这条指令将 t1 寄存器的值初始化为 1
    addi t1, x0, 1
		# la 是 "load address" 的缩写,表示这是一个加载地址的指令
		# 将标签 n 的地址加载到寄存器 t3 中
    la t3, n
		# lw 代表 "load word"(加载字),用于从内存中加载一个 32 位的字(word)到寄存器中。
		# 0(t3) 指定了要从哪里加载字的内存地址。这里使用了基址加偏移量的寻址方式,其中 0 是偏移量,t3 是基址寄存器
		# 执行这条指令之后,t3 不再持有地址,而是持有该地址处的数据
    lw t3, 0(t3)
fib:    
		# beq 代表 "branch if equal"(如果相等则跳转)。这是一个条件分支指令,用于比较两个寄存器的值,如果它们相等,则执行跳转到指定的标签。
		# 如果 t3 寄存器中的值等于 0(因为 x0 的值永远是 0),那么程序将跳转到标签 finish 指示的位置。
		# 如果 t3 的值不是 0,程序将不会跳转,而是继续执行下一条指令。
    beq t3, x0, finish
    add t2, t1, t0
		# 是将 t1 寄存器中的数据复制到 t0 寄存器中。
    mv t0, t1
    mv t1, t2
    addi t3, t3, -1  
		# j 是 "jump"(跳转)的缩写,表示这是一个无条件跳转指令,用于将程序的控制流跳转到指定的位置。
		# 使程序跳转到标签 fib 所在的代码位置,继续从那里执行
    j fib
finish: 
		# addi a0, x0, 1 这条指令将 a0 寄存器的值设置为 1。
		# 在 RISC-V 架构中,进行系统调用(ecall)时,a0 寄存器通常用于指定系统调用的类型或功能代码。
		# 这里,将 a0 设置为 1 通常表示执行的是打印整数的系统调用
    addi a0, x0, 1
		# addi a1, t0, 0 这条指令将 t0 寄存器的值复制到 a1 寄存器中。
		# 在进行打印整数的系统调用时,a1 寄存器用于存储要打印的整数值。
    addi a1, t0, 0
		# 接着执行 ecall 指令时,系统将根据 a0 和 a1 寄存器的值来执行相应的操作。
		# 由于 a0 被设置为 1(表示打印整数的系统调用),并且 a1 包含了要打印的整数(来自 t0),
		# 因此 ecall 指令会导致打印出 a1 中的整数值
    ecall # print integer ecall
		# 将 a0 设置为 10 通常表示执行的是一个终止程序的系统调用
    addi a0, x0, 10
		# 随后执行的 ecall 指令,系统将根据 a0 寄存器的值来执行相应的操作
    ecall # terminate ecall

Paste the contents of ex1.s in Venus and record your answers to the following questions. Some of the questions will require you to run the RISC-V code using Venus’ simulator tab.

  1. What do the .data, .word, .text directives mean (i.e. what do you use them for)? Hint: think about the 4 sections of memory.

A: In the context of assembly language, particularly for systems like RISC-V or MIPS, the directives .data, .word, and .text are used to organize and manage different sections of a program’s memory. These align with the typical memory sections in a compiled program:

  1. .data Directive:
    • Purpose: The .data directive is used to declare the data segment of the program. This segment is for initializing variables that retain their values throughout the program’s execution. It’s primarily used for static and global variables.
    • Memory Section: It corresponds to the data segment in memory, which is different from the stack and heap segments. This segment is used for static data allocation.
  2. .word Directive:
    • Purpose: The .word directive is used within the data segment to define 32-bit words. Each .word allocates 4 bytes of memory (since a word is 32 bits) and can be used to initialize these words with specific values. For example, .word 2, 4, 6, 8 will allocate 16 bytes of memory and initialize them with the given values.
    • Memory Section: These words are part of the data segment. This directive helps in reserving memory space and initializing data in this segment.
  3. .text Directive:
    • Purpose: The .text directive indicates the start of the code segment of the program. This is where the actual instructions of the program are written. It’s the executable part of the program.
    • Memory Section: It corresponds to the text (or code) segment in memory. This segment is used for the executable instructions of the program and is distinct from data, stack, and heap segments.

These directives help in organizing a program into distinct sections, each with its own role in the program’s structure and execution. The .data segment for initializing and storing data, the .word directive for defining 32-bit integers in the data segment, and the .text segment for the actual executable instructions. This organization reflects the typical division of memory in compiled programs into segments like text (code), data, stack (for function calls and local variables), and heap (for dynamically allocated memory).

  1. Run the program to completion. What number did the program output? What does this number represent?

A: The program output the number 34. This number represents the 9th number in the Fibonacci sequence.

In the Fibonacci sequence, each number is the sum of the two preceding ones, starting from 0 and 1. So, the sequence goes 0, 1, 1, 2, 3, 5, 8, 13, 21, 34,… and so on. The number 34 is the 9th term in this sequence (if we start counting from 0 as the first term).

  1. At what address is n stored in memory? Hint: Look at the contents of the registers.

A: after “la t3, n” , t3这个地方变成了 268435464

在这里插入图片描述

Exercise 2: Translating from C to RISC-V

C程序:

int source[] = {3, 1, 4, 1, 5, 9, 0};
int dest[10];

int fun(int x) {
	return -x * (x + 1);
}

int main() {
    int k;
    int sum = 0;
    for (k = 0; source[k] != 0; k++) {
        dest[k] = fun(source[k]);
        sum += dest[k];
    }
    return sum;
}

对应的汇编语言:

.data
source:
    .word   3
    .word   1
    .word   4
    .word   1
    .word   5
    .word   9
    .word   0
dest:
    .word   0
    .word   0
    .word   0
    .word   0
    .word   0
    .word   0
    .word   0
    .word   0
    .word   0
    .word   0

.text
main:
    addi t0, x0, 0
    addi s0, x0, 0
		# load address , 将source的内存地址加载到 寄存器s1中
    la s1, source
    la s2, dest
loop:
		# slli:这是一个指令操作码,代表“Shift Left Logical Immediate”。它表示将一个寄存器中的值向左逻辑移位(即在右边补零)一个立即数指定的位数
		# 将 t0 寄存器中的值向左移动 2 位,然后将结果存入 s3 寄存器
    slli s3, t0, 2
		# add 是寄存器中的值相加
    add t1, s1, s3
    lw t2, 0(t1)
    beq t2, x0, exit
    add a0, x0, t2
		# 用于对栈指针(Stack Pointer)进行操作
		# addi sp, sp, -8 的操作是将栈指针 sp 的当前值减去 8,它实际上在栈上为新的数据或变量预留了 8 个字节的空间
    addi sp, sp, -8
		# 代表“Store Word”。它用于将一个字(word)的数据从寄存器存储到内存中。
		# 将寄存器 t0 中的数据存储到栈指针 sp 当前指向的内存地址处。由于偏移量是 0,这意味着数据被存储在栈顶
    sw t0, 0(sp)
    sw t2, 4(sp)
		# Jump and Link”。这个指令用于跳转到程序中的另一个位置(在这个例子中是标签 square 所指示的位置),同时将返回地址(即跳转之后下一条指令的地址)保存在一个特定的寄存器中
    jal square
    lw t0, 0(sp)
    lw t2, 4(sp)
    addi sp, sp, 8
    add t2, x0, a0
    add t3, s2, s3
    sw t2, 0(t3)
    add s0, s0, t2
    addi t0, t0, 1
		# jal x0, loop 的操作是跳转到标签 loop 指示的代码位置,但不保存返回地址(因为它写入了零寄存器 x0)
    jal x0, loop
square:
    add t0, a0, x0
    add t1, a0, x0
    addi t0, t0, 1
    addi t2, x0, -1
    mul t1, t1, t2
    mul a0, t0, t1
		# jr ra 的操作是从 ra 寄存器中读取存储的地址,并跳转到那个地址继续执行程序。
		# 这种指令通常用于函数的末尾,用来返回到调用该函数的地方。
		# 当一个函数执行完毕,通过 jr ra 指令,程序控制权会返回到调用该函数的下一条指令处继续执行
		# Jump Register(操作指令) 和 Return Address寄存器
    jr ra
exit:
    add a0, x0, s0
    add a1, x0, x0
    ecall # Terminate ecall

我的回答:

  • The register representing the variable k

Answer: t0

  • The register representing the variable sum.

Answer: s0

  • The registers acting as pointers to the source and dest arrays.

Answer: s1 和 s2

  • The assembly code for the loop found in the C code.

Answer: loop

  • How the pointers are manipulated in the assembly code.

Answer: 使用偏移

gpt4的回答(更详细,举了例子,和我的答案是一样)

The register representing the variable k

  • The variable k seems to be represented by the register t0. This is inferred from the line addi t0, x0, 0, which initializes t0 to 0 (similar to k = 0 in C), and the line addi t0, t0, 1, which increments t0 by 1 (similar to k++ in a loop).

The register representing the variable sum

  • The variable sum appears to be represented by the register s0. This is based on the line addi s0, x0, 0 (initializing s0 to 0, like sum = 0), and the line add s0, s0, t2, which adds the value in t2 to s0 (akin to accumulating values in sum).

The registers acting as pointers to the source and dest arrays

  • s1 is used as a pointer to the source array (la s1, source).
  • s2 is used as a pointer to the dest array (la s2, dest).

The assembly code for the loop found in the C code

  • The loop in the assembly code is represented by the label loop: and includes instructions from this label down to jal x0, loop. The loop terminates when beq t2, x0, exit is executed, which happens when t2 (loaded from the source array) is zero, effectively translating to a check like if (source[k] == 0) break; in C.

How the pointers are manipulated in the assembly code

  • The pointers (s1 for source and s2 for dest) are manipulated using offset calculations. For instance, slli s3, t0, 2 and add t1, s1, s3 calculate the address to read from or write to in the source or dest arrays. The slli s3, t0, 2 instruction shifts t0 left by 2 bits, effectively multiplying k by 4 (since each word is 4 bytes). This value is then added to s1 or s2 to get the correct address in the array for the current loop iteration.

Exercise 3: Factorial

完成factorial.s, 实现求阶乘的功能

我的解法:

.globl factorial

.data
n: .word 8

.text
main:
    la t0, n
    lw a0, 0(t0)
    lw t0, 0(t0)
    # 跳转到标签 factorial 指示的地址去执行代码,并将下一条指令的地址保存在 ra 寄存器中
    jal ra, factorial

finish:
    addi a1, a0, 0
    addi a0, x0, 1 #a0设置为1,表示打印整数
    ecall # Print Result

    addi a1, x0, '\n'
    addi a0, x0, 11 # a0设置为11,表示打印字符
    ecall # Print newline

    # a0设置为10,表示终止程序
    addi a0, x0, 10
    ecall # Exit

factorial:
    # YOUR CODE HERE
		addi t1, t0, -1
    beq t1, x0, finish
    mul a0, a0, t1
    add t0, t1, x0
    j factorial

安装java环境后,测试代码是否正确:
在这里插入图片描述

Exercise 4: RISC-V function calling with map

.globl map

.text
main:
    # 跳转到标签 create_default_list 指示的地址去执行代码,并将下一条指令的地址保存在 ra 寄存器中
    jal ra, create_default_list
    add s0, a0, x0  # a0 = s0 is head of node list

    #print the list
    add a0, s0, x0
    jal ra, print_list

    # print a newline
    jal ra, print_newline

    # load your args
    add a0, s0, x0  # load the address of the first node into a0

    # load the address of the function in question into a1 (check out la on the green sheet)
    ### YOUR CODE HERE ###
    la a1, square   # 加载 square 函数的地址到 a1

    # issue the call to map
    jal ra, map

    # print the list
    add a0, s0, x0
    jal ra, print_list

    # print another newline
    jal ra, print_newline

    addi a0, x0, 10
    ecall #Terminate the program

map:
    # Prologue: Make space on the stack and back-up registers
    ### YOUR CODE HERE ###
    addi sp, sp, -12  # 为三个寄存器分配栈空间
    sw ra, 0(sp)      # 保存 ra
    sw s0, 4(sp)      # 保存 s0
    sw s1, 8(sp)      # 保存 s1

    beq a0, x0, done    # If we were given a null pointer (address 0), we're done.

    add s0, a0, x0  # Save address of this node in s0
    add s1, a1, x0  # Save address of function in s1

    # Remember that each node is 8 bytes long: 4 for the value followed by 4 for the pointer to next.
    # What does this tell you about how you access the value and how you access the pointer to next?

    # load the value of the current node into a0
    # THINK: why a0?
    ### YOUR CODE HERE ###
    lw a0,0(s0) # 加载当前节点的值到 a0
    # Call the function in question on that value. DO NOT use a label (be prepared to answer why).
    # What function? Recall the parameters of "map"
    ### YOUR CODE HERE ###
    # 这条指令的功能是从一个寄存器中读取地址,并跳转到该地址执行,同时将下一条指令的地址(即返回地址)保存在默认的链接寄存器 ra(返回地址寄存器)
    jalr a1       # 调用 a1 寄存器中的函数
    # store the returned value back into the node
    # Where can you assume the returned value is?
    ### YOUR CODE HERE ###
    sw a0, 0(s0)  # 将返回值存回当前节点
    # Load the address of the next node into a0
    # The Address of the next node is an attribute of the current node.
    # Think about how structs are organized in memory.
    ### YOUR CODE HERE ###
    lw a0, 4(s0)  # 加载下一个节点的地址到 a0
    # Put the address of the function back into a1 to prepare for the recursion
    # THINK: why a1? What about a0?
    ### YOUR CODE HERE ###
    add a1, s1, x0  # 恢复函数地址到 a1

    # recurse
    ### YOUR CODE HERE ###
    jal ra, map    # 递归调用 map

done:
    # Epilogue: Restore register values and free space from the stack
    ### YOUR CODE HERE ###
    lw ra, 0(sp)   # 恢复 ra
    lw s0, 4(sp)   # 恢复 s0
    lw s1, 8(sp)   # 恢复 s1
    addi sp, sp, 12  # 释放栈空间

    jr ra # Return to caller

square:
    add t0, a0, x0
    mul a0 ,t0, t0
    jr ra

create_default_list:
    addi sp, sp, -12
    sw  ra, 0(sp)
    sw  s0, 4(sp)
    sw  s1, 8(sp)
    # li 指令的全称是 "Load Immediate"。它用于将一个立即数(即直接指定的数值)加载到一个寄存器中。这个指令通常用于初始化寄存器的值
    li  s0, 0       # pointer to the last node we handled
    li  s1, 0       # number of nodes handled
loop:   #do...
    li  a0, 8
    jal ra, malloc      # get memory for the next node
    sw  s1, 0(a0)   # node->value = i
    sw  s0, 4(a0)   # node->next = last
    add s0, a0, x0  # last = node
    addi    s1, s1, 1   # i++
    addi t0, x0, 10
    bne s1, t0, loop    # ... while i!= 10
    lw  ra, 0(sp)
    lw  s0, 4(sp)
    lw  s1, 8(sp)
    addi sp, sp, 12
    jr ra

print_list:
    # bne: 这是一个条件分支指令,代表“branch if not equal”。它的作用是比较两个寄存器的值,如果它们不相等,则跳转到指定的标签或地址。
    bne a0, x0, printMeAndRecurse
    jr ra       # nothing to print
printMeAndRecurse:
    add t0, a0, x0  # t0 gets current node address
    lw  a1, 0(t0)   # a1 gets value in current node
    addi a0, x0, 1      # prepare for print integer ecall
    ecall
    addi    a1, x0, ' '     # a0 gets address of string containing space
    addi    a0, x0, 11      # prepare for print string syscall
    ecall
    lw  a0, 4(t0)   # a0 gets address of next node
    jal x0, print_list  # recurse. We don't have to use jal because we already have where we want to return to in ra

print_newline:
    addi    a1, x0, '\n' # Load in ascii code for newline
    addi    a0, x0, 11
    ecall
    jr  ra

malloc:
    addi    a1, a0, 0
    addi    a0, x0 9
    ecall
    jr  ra

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2147959.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

try语句块和异常处理

throw直接报错 int main() {if (true) throw std::runtime_error("Error!"); } runtime_error(或者其他错误类型)是一个类, 必须初始化, 需使用string或者c风格字符串初始化. throw放try里, catch会接住throw的error 大概就是[catch]-->{[throw]-->[try]}的关…

数据结构——二叉树堆的专题

1.堆的概念及结构 如果有一个关键码的集合K {K0 &#xff0c;K1 &#xff0c;K2 &#xff0c;K3…&#xff0c;K(N-1) }&#xff0c;把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中&#xff0c;并满足&#xff1a;Ki < K2*i1且 Ki<K2*i2 ) i 0&#…

Java语言程序设计基础篇_编程练习题**18.30 (找出单词)

题目&#xff1a;**18.30 (找出单词) 编写一个程序&#xff0c;递归地找出某个目录下的所有文件中某个单词出现的次数。从命令行如下传递参数&#xff1a; java Exercise18_30 dirName word 习题思路 &#xff08;读取路径方法&#xff09;和18.28题差不多&#xff0c;把找…

电子元件制造5G智能工厂物联数字孪生平台,推进制造业数字化转型

5G智能工厂与物联数字孪生平台的融合应用&#xff0c;不仅为电容器制造业注入了新的活力&#xff0c;更为整个制造业的数字化转型树立了新的标杆。电子元件制造过程中&#xff0c;数字孪生平台通过实时监测生产线的各个环节&#xff0c;实现了生产流程的可视化监控。管理人员可…

vue中的slot插槽,彻底搞懂及使用

1、使用slot站位&#xff0c;不传内容&#xff0c;显示默认值 //父组件 import SlotChild from ./projectConstruction-child/SlotChild.vue <div><SlotChild></SlotChild> </div>//子组件 <template><div>下面是插槽内容</div><…

【爆炸】BB机,BP机,寻呼系统基础知识,物理层讲解

本页介绍寻呼系统基础知识。其中提到了寻呼机使用的数字协议并描述了数字寻呼接收器。 寻呼是一种单向通信系统。寻呼系统向携带小型电池供电设备&#xff08;称为寻呼机&#xff09;的个人广播信号或消息。这是与员工和/或客户沟通的非常重要的方式。让我们看看寻呼系统的工作…

新发布的OpenAI o1生成式AI模型在强化学习方面迈出了重要的一步

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Xv6驱动(四):CLINT

阅读材料 Xv6代码&#xff1a;memlayout.h、start.c、kernelvec.S教材5.4节 CLINT内存映射 实际上&#xff0c;CLINT还包括若干个MSIP寄存器&#xff0c;用来触发软件中断&#xff0c;但是在Xv6中不考虑软件中断&#xff0c;因此这些寄存器也不用考虑 // core local interr…

python 运行其他命令行工具,实时打印输出内容

起因&#xff0c; 目的: python 运行一个命令&#xff0c;最简洁的写法是: import os # 转换视频格式。 cmd "ffmpeg -i a1.ts -c copy a1.mp4"os.system(cmd)问题&#xff1a; 如果上面的视频比较大&#xff0c;需要运行很长时间&#xff0c;那么感觉就像是卡住…

C# 访问Access存取图片

图片存入ole字段&#xff0c;看有的代码是获取图片的字节数组转换为base64字符串&#xff0c;存入数据库&#xff1b;显示图片是把base64字符串转换为字节数组再显示&#xff1b;直接存字节数组可能还好一点&#xff1b; 插入的时候用带参数的sql写法比较好&#xff1b;用拼接…

InternVL 微调实践闯关任务

基础任务 follow 教学文档和视频使用QLoRA进行微调模型&#xff0c;复现微调效果&#xff0c;并能成功讲出梗图. 尝试使用LoRA&#xff0c;或调整xtuner的config&#xff0c;如LoRA rank&#xff0c;学习率。看模型Loss会如何变化&#xff0c;并记录调整后效果(选做&#xff…

十、数字人IP应用方案

1、背景 在当今的数字时代,随着AI技术的突飞猛进,数字人AI已经从概念走向应用,成为知识内容创作领域一股不可忽视的力量。它的出现,在很大程度上极大地提高了内容的生产效率,大有替代知识IP,成为内容IP终结者的趋势。 数字人IP,从形象到声音,与知识博主真人的相似度可…

初中生物--7.生物圈中的绿色植物(二)

绿色植物与生物圈的水循环 1.植物对水分的吸收和运输 1.植物主要通过根吸收水分。根吸收水分的主要部位是根尖的成熟区。 2.外界溶液浓度<根毛细胞溶液浓度→细胞吸水&#xff1b; 1.在这种情况下&#xff0c;根毛细胞内的溶液浓度高于外界溶液&#xff0c;因此细胞内的…

蓝星多面体foc旋钮键盘复刻问题详解

介绍&#xff1a; 本教程是针对立创开源项目 承载我所有幻想的键盘 - 立创开源硬件平台 作者是 蓝星多面体 这里我总结一下我复刻过程中的一些问题 一 <<编译环境怎么搭建&#xff1f;>> 第一步 安装vscode 下载vscode &#xff08;可以在各大应用平台…

R语言中的shiny框架

R语言中的shiny框架 Shiny 的基本概念基本用法示例常见用法示例1. 输入控件2. 输出控件3. 动态 UI4. 数据传递和反应式编程 高级功能1. 使用 shinyjs2. 使用 shinythemes Shiny 是一个 R 语言的框架&#xff0c;用于构建交互式的网页应用&#xff0c;可以让用户以最少的 HTML、…

飞驰云联FTP替代方案:安全高效文件传输的新选择

FTP协议广泛应用各行业的文件传输场景中&#xff0c;由于FTP应用获取门槛低、使用普遍&#xff0c;因此大部分企业都习惯使用FTP进行文件传输。然而面临激增的数据量和网络安全威胁的不断演变&#xff0c;FTP在传输安全性与传输性能上有所欠缺&#xff0c;无法满足企业现在的高…

2024java面试-软实力篇

为什么说简历很重要&#xff1f; 一份好的简历可以在整个申请面试以及面试过程中起到非常好的作用。 在不夸大自己能力的情 况 下&#xff0c;写出一份好的简历也是一项很棒的能力。为什么说简历很重要呢&#xff1f; 、 先从面试来说 假如你是网申&#xff0c;你的简历必然…

Windows本地pycharm使用远程服务器conda虚拟环境

1、Windows下载pycharm专业版&#xff0c;必须是专业版&#xff0c;不然连不了远程服务器。 Download PyCharm: The Python IDE for data science and web development by JetBrains 2、添加远程服务器的虚拟环境&#xff0c;在pycharm右下角落选择 添加新的解释器>>SSH。…

计算机网络33——文件系统

1、chmod 2、chown 需要有root权限 3、link 链接 4、unlink 创建临时文件&#xff0c;用于非正常退出 5、vi vi可以打开文件夹 ../是向外一个文件夹 6、ls ls 可以加很多路径&#xff0c;路径可以是文件夹&#xff0c;也可以是文件 ---------------------------------…

【Java】并发集合

并发集合&#xff08;java.util.concurrent&#xff09; 一、List CopyOnWriteArrayList&#xff08;ReentrantLock实现线程安全&#xff09; &#xff08;1&#xff09;并发修改&#xff08;写操作&#xff09;时保证线程安全&#xff1a; 通过ReentrantLock实现多个线程并…