从零学习开发一个RISC-V操作系统(四)丨RISC-V汇编语言编程

news2024/10/6 8:39:35

本篇文章的内容

  • 一、RISC-V汇编语言简介
    • 1.1 RISC-V 汇编语言的基本格式
    • 1.2 RISC-V 汇编指令操作对象
    • 1.3 RISC-V 汇编指令编码格式
    • 1.4 RISC-V 汇编指令分类
  • 二、RISC-V汇编语言详解
    • 2.1 add 加法指令
    • 2.2 sub 减法指令


  本系列是博主参考B站课程学习开发一个RISC-V的操作系统的学习笔记,计划从RISC-V的底层汇编指令学起,结合C语言,在Ubuntu 20.04上开发一个简易的操作系统。一个目的是通过实践操作学习和了解什么是操作系统,第二个目的是为之后学习RISC-V的集成电路设计打下一定基础。本系列持续不定期更新,分享出来和大家一同交流进步。
  博主是微电子科学与工程专业的学生,对软件和操作系统难免有理解不到位的地方。如有谬误敬请不吝告知,不胜感激。

  参考课程及文章:
  【Bilibili】[完结] 循序渐进,学习开发一个RISC-V上的操作系统 - 汪辰 - 2021春


一、RISC-V汇编语言简介

  汇编语言(Assembly Language)是一种“低级”语言。不同的架构的汇编语言是不同的,因为底层的寄存器的个数和功能不同。例如x86的机器语言在RISC-V的机器上是无法运行的,但是使用高级语言时完全不需要考虑底层的机器语言。我们使用不同的编译器将高级语言翻译成不同的机器语言,来完成对内存和指令的管理和优化。而正是由于这一点,使用汇编语言时完全不需要考虑不同编译器的影响,这也是汇编语言的灵活性的体现。本课程学习和使用的汇编语言是RISC-V汇编语言,其依赖于RISC-V独有的GNU汇编器,所以将RISC-V的汇编程序移植到其他架构的处理器上也是无法运行的。

  • 汇编语言的缺点:难读、难写、难移植
  • 汇编语言的优点:灵活、强大
  • 汇编语言的应用场景
    • 需要直接访问底层硬件的地方
    • 需要对性能执行极致优化的地方

1.1 RISC-V 汇编语言的基本格式

  一个完整的RISC-V汇编程序有多条语句(statement)组成,汇编文件一般由.s结尾(不包含预处理语句,是纯粹的汇编语句)。一条典型的RISC-V汇编语句由3部分组成,分为标签、操作和注释:

[label:] [operation] [comment] 
  • label(标号): GNU汇编中,任何以冒号结尾的标识符都被认为是一个标号。标号相当于给一个指令所在的地址起的一个名字
  • operation 可以有以下多种类型:
    • instruction(指令): 直接对应二进制机器指令的字符串,例如add
    • pseudo-instruction(伪指令): 一些指令的组合。它并不对应二进制机器指令,只是为了提高编写代码的效率,可以用一条伪指令指示汇编器产生多条实际的指令(instructions),方便程序的使用,例如li。在理解和做法上与自定义的函数类似。
    • directive(指示/伪操作): 通过类似指令的形式(以“.”开头),通知汇编器如何控制代码的产生等,不对应具体的RISC-V指令,由汇编器自身定义,例如.text.global.end.macro.endm等。在理解和应用上类似C语言中的#define语句。
    • macro(宏):采用指示/伪操作 .macro/.endm 自定义的宏,汇编器碰到宏时会自动将宏替换成对应定义的内容。
  • comment(注释): 常用方式,#开始到当前行结束,也有些汇编器定义;//开头的注释格式。

  下面是一个简单的RISC-V的汇编语言程序:

# fitst RISC-V Assemble Sample

.macro do_nothing
    nop
    nop
.endm

    .text
    .global _start

_start:
    li x6, 5 # x6 = 5
    li x7, 4 # x7 = 4

    add x5, x6, x7 # x5 = x6 + x7
    do_nothing

stop: 
    j stop  # jump to stop
    .end

1.2 RISC-V 汇编指令操作对象

  1. 寄存器
  • 32个寄存器,x0~x31(本节课只设计RV32I的通用寄存器组)
  • 在RISC-V中,Hart在执行算数逻辑运算时所操作的数据必须直接来自寄存器(Hart可以认为是处理器执行指令的最小单元,类似传统的CPU概念,但是与CPU不同),如果数据存在内存中,必须首先将要操作的数据转移到寄存器中,Hart才能对数据进行处理,计算之后的结果也只能存在寄存器中,之后根据需要再转移到外部的内存空间中。
    在这里插入图片描述
    上图中,XLEN表示机器的字长,RV32I的ISA对应的XLEN对应的就是32位。x0寄存器很特殊,其中的值恒为0,且不允许写操作,读出的值恒为0。pc即指针计数器寄存器在RISC-V中是禁止被访问的。获取程序当前运行的位置需要用特殊的方法实现。
  1. 内存
  • Hart可以执行在寄存器和内存之间的数据读写操作
  • 读写操作时,使用字节(Byte)为基本单位进行寻址;
  • RV32可以访问最多 2 32 2^{32} 232个字节的内存空间。

1.3 RISC-V 汇编指令编码格式

  做底层开发的时候,经常需要进行逐字节对照查阅指令。可以根据手册(RISC-V非特权指令集)查阅每个指令的含义。
在这里插入图片描述

  • 指令长度:在RISC-V汇编指令的规范中,所有的指令的长度都是固定的(在非压缩的情况下讨论)。RV32I中指令长度ILEN1 = 32bit。

  • 指令对齐:指一条指令开始的地址,IALIGN = 32bit表示指令开始的地址一定是4的倍数,程序加载的时候一定从这样一个指令开始的地方开始加载。

  • 每个32bit构成一个域。

  • funct3/funct7opcode共同决定了最终的指令类型。以add指令为例,它对应的opcode为0110011,通过查阅下标可以发现它是一条OP类型的指令。
    在这里插入图片描述

  • 指令在程序中按小端序(Little-Endian)排列(底字节存入低地址,高字节存入高地址)。不同的CPU规定的主机字节序不同。

  • RISC-V中有六种不同的指令格式(format):

    • R-type:(Register),每条指令中有三个 fields,用于指定 3 个寄存器参数(选择的寄存器编号)
    • I-type: (Immediate),每条指令除了带有两个寄存器参数外,还带有一个立即数参数(宽度为 12 bits)。
    • S-type: (Store),每条指令除了带有两个寄存器参数外,还带有一个立即数参数(宽度为 12 bits,但 fields 的组织方式不同于 I-type)
    • B-type: (Branch),每条指令除了带有两个寄存器参数外,还带有一个立即数参数(宽度为 12bits,但取值为 2 的倍数)。
    • U-type: (Upper),每条指令含有一个寄存器参数再加上一个立即数参数(宽度为 20bits,用于表示一个立即数的高 20 位)
    • J-type: (Jump),每条指令含有一个寄存器参数再加上一个立即数参数(宽度为 20bits)

1.4 RISC-V 汇编指令分类

  RISC-V中常用的汇编指令如下表所示:
在这里插入图片描述
  伪指令很多,一条伪指令具体执行哪些代码可以在 RISC-V非特权指令集手册 中的 RISC-V Assembly Programmer’s Handbook 章节查阅到。

二、RISC-V汇编语言详解

2.1 add 加法指令

在这里插入图片描述

  • 编码格式:R-type
  • opcode: 0110011
  • funct3 = 000, funct7 = 0000000
# Add
# Format:
#	ADD RD, RS1, RS2
# Description:
#	The contents of RS1 is added to the contents of RS2 and the result is 
#	placed in RD.

	.text				# Define beginning of text section
	.global	_start		# Define entry _start

_start:
	li x6, 1			# x6 = 1
	li x7, 2			# x7 = 2
	add x5, x6, x7		# x5 = x6 + x7

stop:
	j stop			# Infinite loop to stop execution

	.end			# End of file

Makefile操作须知
  在仓库代码中的common.mk文件中有如下的一段代码,它的意思是启动虚拟机qemu的系统模式(汇编直接编译出的代码是不能在操作系统的裸机上直接运行的,即不能在用户user模式下运行),参数-nographic表示不启动图形界面,-smp 1表示只启动一个Hart,-machine virt表示启动virt的机器类型。

QEMU = qemu-system-riscv32
QFLAGS = -nographic -smp 1 -machine virt -bios none

  运行指令make debug时实际运行的是如下的代码。-s表示在目标机(模拟机)中启动gdbserver,二者建立网络链接(在这个环境中实际上是内部网络的回环),-S表示启动调试模式后程序暂停运行。-x ${GDBINIT}表示启动一个gdb的调试脚本,自动运行gdb的相关指令,脚本的内容可以在文件gdbinit文件中找到

.PHONY : debug
debug: all
	@echo "Press Ctrl-C and then input 'quit' to exit GDB and QEMU"
	@echo "-------------------------------------------------------"
	@${QEMU} ${QFLAGS} -kernel ${EXEC}.elf -s -S &
	@${GDB} ${EXEC}.elf -q -x ${GDBINIT}

在这里插入图片描述

  编译运行后,发现test.bin文件只有16个字节,对应程序中确实只有四条指令。test.elf文件中除了指令本身之外还有很多调试相关的内容。
在这里插入图片描述
  输入命令make hex可以看到test.bin中的数据,可以根据这些数据将所有指令反汇编出来(也可以使用make code命令自动进行反汇编):
在这里插入图片描述
  使用make debug命令对程序进行调试,在gdb页面中,输入si(step instruction)单步运行程序(makefile中已经将断点设置在.start处了)。可以看到,x6,x7,x5的值会依次变化。

如果要退出qemu中的gdb,一定要先 Ctrl+C,之后再输入quit和确认y进行退出,否则会遇到qemu的后台程序杀不死的问题,导致下次启动错误。

在这里插入图片描述

2.2 sub 减法指令

在这里插入图片描述

  • 编码格式:R-type
  • opcode: 0110011
  • funct3 = 000, funct7 = 0100000
# Substract
# Format:
#	SUB RD, RS1, RS2
# Description:
#	The contents of RS2 is subtracted from the contents of RS1 and the result
#	is placed in RD.

	.text				# Define beginning of text section
	.global	_start		# Define entry _start

_start:
	li x6, -1			# x6 = -1
	li x7, -2			# x7 = -2
	sub x5, x6, x7		# x5 = x6 - x7

stop:
	j stop			# Infinite loop to stop execution

	.end			# End of file


  原创笔记,码字不易,欢迎点赞,收藏~ 如有谬误敬请在评论区不吝告知,感激不尽!博主将持续更新有关嵌入式开发、机器学习方面的学习笔记。


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

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

相关文章

Leetcode—216.组合总和III【中等】

2023每日刷题&#xff08;七十八&#xff09; Leetcode—216.组合总和III 算法思想 实现代码 class Solution { public:vector<vector<int>> combinationSum3(int k, int n) {vector<vector<int>> ans;vector<int> path;function<void(int,…

Docker初次体验:WSL+Docker+portanier

文章目录 前言Docker是什么&#xff1f;Docker的优点Docker的使用场景&#xff1a;一件安装 Docker安装开启虚拟化安装wsl下载慢的请看这个下载成功 安装Docker修改Docker安装位置 配置Docker安装portanier&#xff08;可视化的Docker操作页面&#xff09;登录网址 总结 前言 …

Git仓库里嵌着别的仓库导致出现问题

例如这样&#xff0c;git仓库里面有箭头&#xff0c;且仓库打开是一个空仓库。 解决问题的方法 1.到子仓库路径下&#xff0c;使用ls -a指令可以看到文件夹下有一个.git文件夹。 我们使用rm -rf <子仓库路径>/.git删除每个子仓库下面的.git文件夹。 2.执行git rm --cache…

ROS:rosdep与ROS2的安装记录

鱼香ROS一键安装&#xff1a; 一键安装微信使用指南 | 鱼香ROS rosdep安装&#xff1a; rosdep 是一个在ROS&#xff08;Robot Operating System&#xff09;生态系统中非常重要的工具&#xff0c;它用于安装ROS包的依赖项。rosdep 确保了ROS包所需的系统依赖项能够被正确安装…

JVM篇----第二篇

系列文章目录 文章目录 系列文章目录前言一、JVM 选项 -XX:+UseCompressedOops 有什么作用?为什么要使用二、怎样通过 Java 程序来判断 JVM 是 32 位 还是 64位?三、32 位 JVM 和 64 位 JVM 的最大堆内存分别是多数?四、JRE、JDK、JVM 及 JIT 之间有什么不同?前言 前些天发…

STM32之002--软件安装 Keil

文章目录&#xff1a; 一、安装 Keil 二、注册 三、安装芯片支持包 一、安装 Keil 重点 1&#xff1a; 安装时&#xff0c;不能使用中文路径&#xff0c;否则无法正常使用!! 重点 2&#xff1a; 不要安装 V5.36 及以上的版本&#xff0c;其默认AC6编译器&#xff0c…

PyQt ------ QLineEditor

PyQt ------ QLineEditor 引言正文示例1------基础示例示例2------进阶示例 引言 这里给大家介绍一下 PyQt6 中的 QLineEditor 组件用法。 正文 QLineEditor 是用于做单行字符串输出的组件&#xff0c;它只能够将字符串在一行进行输出&#xff0c;如果要进行多行输出&#x…

processing集训day04

编程英语 mouse 鼠标 pressed 压平的 height 高 parent 父亲 / 母亲 else 否则 sound 声音 file 文档 music 音乐 new 新 key 键 pressed 压平的 mouse 鼠标 height 高 鼠标事件监听 监听鼠标按钮是否被按下&#xff1a; mousePressed 变量 mous…

基于SpringBoot的药品管理系统

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…

宝塔面板优惠券(折扣券)领取入口及使用教程

宝塔面板是一款服务器管理软件&#xff0c;支持Linux与Windows系统&#xff0c;提供了丰富的功能和插件&#xff0c;让服务器管理变得更加简单、安全、高效。为了让更多用户体验到宝塔面板的便利&#xff0c;官方会不定期推出优惠券活动。本文将为大家详细介绍宝塔面板优惠券的…

jQuery语法知识(表单事件)

表单事件 1、focus ( ) 触发条件&#xff1a; 获取焦点 作用对象&#xff1a; focus&#xff08;&#xff09;作用对象可以是任意元素 例&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title>…

国外网站seo教程,海外网站seo优化方案

随着互联网的发展&#xff0c;全球市场已经变得前所未有的普及。外贸企业不再局限于本土市场&#xff0c;而是可以轻松触及全球范围的潜在客户。全球市场的开放也意味着竞争的激烈。无论是大型跨国公司还是中小型企业&#xff0c;都在全球市场争夺有限的客户资源。 一、关键词策…

CSS实现文本和图片无限滚动动画

Demo图如下&#xff1a; <style>* {margin: 0;padding: 0;box-sizing: border-box;font-family: Poppins, sans-serif;}body {min-height: 100vh;background-color: rgb(11, 11, 11);color: #fff;display: flex;flex-direction: column;justify-content: center;align-i…

8 容器化微服务

文章目录 DockerSpring Boot和Buildpacks在Docker中运行系统Docker化微服务Docker化前端Docker化配置导入器Docker Compose使用Docker扩展系统共享Docker镜像小结 前面的文章&#xff1a; 1、1 一个测试驱动的Spring Boot应用程序开发 2、2 使用React构造前端应用 3、3 试驱动的…

[数据结构]顺序表

1、顺序表的概念及结构 1.1 线性表 线性表&#xff08; linear list &#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是⼀种在实际中广泛使用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串... 线性表在逻辑上是线性结构&#…

47. 全排列 II - 力扣(LeetCode)

题目描述 给定一个可包含重复数字的序列 nums &#xff0c;按任意顺序 返回所有不重复的全排列。 输入示例 nums [1,1,2]输出示例 [[1,1,2], [1,2,1], [2,1,1]]解题思路 解题代码 class Solution {List<List<Integer>> result new ArrayList<>();Deq…

探索元宇宙:游戏的全新境界

你是否曾经想过&#xff0c;在一个完全由你自己掌控的虚拟世界里&#xff0c;体验前所未有的冒险和刺激&#xff1f; UTONMOS元宇宙游戏将带你进入这个全新的领域&#xff0c;让你的想象力和创造力自由驰骋。 UTONMOS&#xff0c;基于上海和数集团自主研发的和数链&#xff0c…

MoEs学习

和多任务学习的mmoe很像哦&#xff08;有空再学习一下&#xff09;moe layer的起源&#xff1a;Switch Transformers paper MoE moe由两个结构组成&#xff1a; Moe Layer &#xff1a;这些层代替了传统 Transformer 模型中的前馈网络 (FFN) 层。MoE 层包含若干“专家”(例如…

【教程】混淆Dart 代码

什么是代码混淆&#xff1f; 代码混淆是一种将应用程序二进制文件转换为功能上等价&#xff0c;但人类难于阅读和理解的行为。在编译 Dart 代码时&#xff0c;混淆会隐藏函数和类的名称&#xff0c;并用其他符号替代每个符号&#xff0c;从而使攻击者难以进行逆向工程。 Flut…

dubbo和eureka的区别

dubbo可以作为客户端&#xff0c;也可以作为服务端&#xff0c;因此他内置了很多序列化框架可供选择&#xff0c;通过配置可以进行选择。默认是hession&#xff0c;还有gson&#xff0c;fastJson&#xff0c;jdk自带的序列化。 eureka只能作为服务端&#xff0c;他序列要与客户…