当前位置: 首页 > news >正文

CS61C 2020计算机组成原理Lab03

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 寄存器的值初始化为 1addi 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, finishadd t2, t1, t0# 是将 t1 寄存器中的数据复制到 t0 寄存器中。mv t0, t1mv t1, t2addi 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, 0addi s0, x0, 0# load address , 将source的内存地址加载到 寄存器s1中la s1, sourcela s2, dest
loop:# slli:这是一个指令操作码,代表“Shift Left Logical Immediate”。它表示将一个寄存器中的值向左逻辑移位(即在右边补零)一个立即数指定的位数# 将 t0 寄存器中的值向左移动 2 位,然后将结果存入 s3 寄存器slli s3, t0, 2# add 是寄存器中的值相加add t1, s1, s3lw t2, 0(t1)beq t2, x0, exitadd 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 squarelw t0, 0(sp)lw t2, 4(sp)addi sp, sp, 8add t2, x0, a0add t3, s2, s3sw t2, 0(t3)add s0, s0, t2addi t0, t0, 1# jal x0, loop 的操作是跳转到标签 loop 指示的代码位置,但不保存返回地址(因为它写入了零寄存器 x0)jal x0, loop
square:add t0, a0, x0add t1, a0, x0addi t0, t0, 1addi t2, x0, -1mul t1, t1, t2mul a0, t0, t1# jr ra 的操作是从 ra 寄存器中读取存储的地址,并跳转到那个地址继续执行程序。# 这种指令通常用于函数的末尾,用来返回到调用该函数的地方。# 当一个函数执行完毕,通过 jr ra 指令,程序控制权会返回到调用该函数的下一条指令处继续执行# Jump Register(操作指令) 和 Return Address寄存器jr ra
exit:add a0, x0, s0add a1, x0, x0ecall # 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, nlw a0, 0(t0)lw t0, 0(t0)# 跳转到标签 factorial 指示的地址去执行代码,并将下一条指令的地址保存在 ra 寄存器中jal ra, factorialfinish:addi a1, a0, 0addi a0, x0, 1 #a0设置为1,表示打印整数ecall # Print Resultaddi a1, x0, '\n'addi a0, x0, 11 # a0设置为11,表示打印字符ecall # Print newline# a0设置为10,表示终止程序addi a0, x0, 10ecall # Exitfactorial:# YOUR CODE HEREaddi t1, t0, -1beq t1, x0, finishmul a0, a0, t1add t0, t1, x0j factorial

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

Exercise 4: RISC-V function calling with map

.globl map.text
main:# 跳转到标签 create_default_list 指示的地址去执行代码,并将下一条指令的地址保存在 ra 寄存器中jal ra, create_default_listadd s0, a0, x0  # a0 = s0 is head of node list#print the listadd a0, s0, x0jal ra, print_list# print a newlinejal ra, print_newline# load your argsadd 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 mapjal ra, map# print the listadd a0, s0, x0jal ra, print_list# print another newlinejal ra, print_newlineaddi a0, x0, 10ecall #Terminate the programmap:# Prologue: Make space on the stack and back-up registers### YOUR CODE HERE ###addi sp, sp, -12  # 为三个寄存器分配栈空间sw ra, 0(sp)      # 保存 rasw s0, 4(sp)      # 保存 s0sw s1, 8(sp)      # 保存 s1beq 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 s0add 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    # 递归调用 mapdone:# Epilogue: Restore register values and free space from the stack### YOUR CODE HERE ###lw ra, 0(sp)   # 恢复 ralw s0, 4(sp)   # 恢复 s0lw s1, 8(sp)   # 恢复 s1addi sp, sp, 12  # 释放栈空间jr ra # Return to callersquare:add t0, a0, x0mul a0 ,t0, t0jr racreate_default_list:addi sp, sp, -12sw  ra, 0(sp)sw  s0, 4(sp)sw  s1, 8(sp)# li 指令的全称是 "Load Immediate"。它用于将一个立即数(即直接指定的数值)加载到一个寄存器中。这个指令通常用于初始化寄存器的值li  s0, 0       # pointer to the last node we handledli  s1, 0       # number of nodes handled
loop:   #do...li  a0, 8jal ra, malloc      # get memory for the next nodesw  s1, 0(a0)   # node->value = isw  s0, 4(a0)   # node->next = lastadd s0, a0, x0  # last = nodeaddi    s1, s1, 1   # i++addi t0, x0, 10bne s1, t0, loop    # ... while i!= 10lw  ra, 0(sp)lw  s0, 4(sp)lw  s1, 8(sp)addi sp, sp, 12jr raprint_list:# bne: 这是一个条件分支指令,代表“branch if not equal”。它的作用是比较两个寄存器的值,如果它们不相等,则跳转到指定的标签或地址。bne a0, x0, printMeAndRecursejr ra       # nothing to print
printMeAndRecurse:add t0, a0, x0  # t0 gets current node addresslw  a1, 0(t0)   # a1 gets value in current nodeaddi a0, x0, 1      # prepare for print integer ecallecalladdi    a1, x0, ' '     # a0 gets address of string containing spaceaddi    a0, x0, 11      # prepare for print string syscallecalllw  a0, 4(t0)   # a0 gets address of next nodejal x0, print_list  # recurse. We don't have to use jal because we already have where we want to return to in raprint_newline:addi    a1, x0, '\n' # Load in ascii code for newlineaddi    a0, x0, 11ecalljr  ramalloc:addi    a1, a0, 0addi    a0, x0 9ecalljr  ra

在这里插入图片描述


http://www.mrgr.cn/news/30611.html

相关文章:

  • 使用ookii-dialogs-wpf在WPF选择文件夹时能输入路径
  • 如何在 Docker 容器中启动 X11 图形界面程序
  • fragment的动态调用方法
  • Linux——Linux常见开发工具详解
  • 【363】基于springboot的高校竞赛管理系统
  • C++正则库
  • Linux 进程2
  • kubernetes存储之GlusterFS(GlusterFS for Kubernetes Storage)
  • 代码随想录算法训练营第51天 | 岛屿数量、岛屿的最大面积
  • index嵌入vue.js, 包括了vue.js的下载地址
  • iotop 命令:磁盘IO监控和诊断
  • 前缀和与差分(一维)
  • Springboot请求响应案例
  • Deutsch intensiv C1 Schreiben
  • Maven 和 gradle JavaFX 项目的休眠行为差异
  • 2024java面试-软实力篇
  • VMware Workstation Pro 17下载及安装教程
  • 828华为云征文|华为Flexus云服务器打造 mediacms 线上影院
  • JS和Node.js的事件循环
  • 计算生物学:概念、历史、现状与展望?
  • iostat 命令:系统状态监控
  • 典型的MVC设计模式:使用JSP和JavaBean相结合的方式来动态生成网页内容典型的MVC设计模式
  • Springboot的三层架构
  • JDK自带的序列化
  • C++ : 继承问题 [virtual函数调用,为什么禁止在virtual使用默认参数]
  • 向上转移和向下转型