手写简易操作系统(九)--实现打印函数

news/2024/5/26 20:46:14/文章来源:https://blog.csdn.net/weixin_43903639/article/details/136724231

前情提要

前面我们已经进入内核程序了,中间穿插了一点特权级的知识,现在我们开始准备一个打印函数

很不幸,还有汇编程序

一、C调用规约

因为涉及到C与汇编的联合编程,我们这里简述一下调用规约,调用规约就是约定参数压栈顺序问题,还有栈空间的清理工作。

image-20240314195531557

真的是有很多种调用规约,因为是简述,我们举个我们用到的cdecl的例子。

函数

int subtract(int a, int b);  //被调用者
int sub = subtract (3,2);    // 主调用者

主调用者,从右到左压栈,回收栈空间

push     2                         ;压入参数b      
push     3                         ;压入参数a 
call subtract                      ;调用函数subtract 
add esp, 8                         ;回收(清理)栈空间

被调用者保存寄存器,返回值放在EAX中

push ebp                     ;压入ebp备份
mov ebp,esp                  ;将esp赋值给ebp
mov eax,[ebp+0x8]            ;偏移8字节处为第1个参数a 
add eax,[ebp+0xc]            ;偏移0xc字节处是第2个参数b
mov esp,ebp                  ;为防止中间有入栈操作,用ebp恢复esp        
pop  ebp                     ;将ebp恢复
ret 

二、显卡的端口控制

VGA中的寄存器可以分为

image-20240314200518492

这些只是寄存器的分组,实际上比这个要多很多。

CRT controller寄存器组的端口地址被设置为0x3Bx,Address Register和Data Register的端口地址实际值为3B4h-3B5h。并且为了兼容monochrome 适配器(显卡),Input Status #1 Register寄存器的端口地址被设置为0x3BA。

三、打印单个字符

我们创建一个 put_char 函数

; os/src/lib/kernel/print.s
TI_GDT equ  0
RPL0  equ   0
SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0[bits 32]
section .text
global put_char
put_char:pushad	                    ; 备份32位寄存器环境mov ax, SELECTOR_VIDEO	    ; 不能直接把立即数送入段寄存器mov gs, ax                  ; 所以每次都要给视频段选择子赋值,避免错误;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;获取光标位置;先获得高8位mov dx, 0x03d4              ;索引寄存器mov al, 0x0e	            ;用于提供光标位置的高8位out dx, almov dx, 0x03d5              ;通过读写数据端口0x3d5来获得或设置光标位置 in al, dx	                ;得到了光标位置的高8位mov ah, al;再获取低8位mov dx, 0x03d4mov al, 0x0fout dx, almov dx, 0x03d5 in al, dx;将光标存入bxmov bx, ax	  ;下面这行是在栈中获取待打印的字符mov ecx, [esp + 36]	      ;pushad压入4×8=32字节,加上主调函数的返回地址4字节,故esp+36字节cmp cl, 0xd				  ;CR是0x0d,LF是0x0ajz .is_carriage_returncmp cl, 0xajz .is_line_feedcmp cl, 0x8				  ;BS(backspace)的asc码是8jz .is_backspacejmp .put_other	   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;判断是否为退格
.is_backspace:dec bxshl bx,1mov byte [gs:bx], 0x20		  ;将待删除的字节补为0或空格皆可inc bxmov byte [gs:bx], 0x07shr bx,1jmp .set_cursor;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;是ascii码
.put_other:shl bx, 1				     ; 光标位置是用2字节表示,将光标值乘2,表示对应显存中的偏移字节mov [gs:bx], cl			     ; ascii字符本身inc bxmov byte [gs:bx],0x07        ; 字符属性shr bx, 1				     ; 恢复老的光标值inc bx				         ; 下一个光标值cmp bx, 2000		   jl .set_cursor			     ; 若光标值小于2000,表示未写到显存的最后,则去设置新的光标值; 若超出屏幕字符数大小(2000)则换行处理
.is_line_feed:				     ; 是换行符LF(\n)
.is_carriage_return:		     ; 是回车符CR(\r)xor dx, dx				     ; dx是被除数的高16位,清0.mov ax, bx				     ; ax是被除数的低16位.mov si, 80				     ; 由于是效仿linux,linux中\n便表示下一行的行首,所以本系统中,div si				         ; 把\n和\r都处理为linux中\n的意思,也就是下一行的行首。sub bx, dx				     ; 光标值减去除80的余数便是取整.is_carriage_return_end:		 ; 回车符CR处理结束add bx, 80cmp bx, 2000
.is_line_feed_end:			     ; 若是LF(\n),将光标移+80便可。  jl .set_cursor;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;滚动屏幕
.roll_screen:				     ; 若超出屏幕大小,开始滚屏cld  mov ecx, 960				 ; 一共有2000-80=1920个字符要搬运,共1920*2=3840字节.一次搬4字节,共3840/4=960次 mov esi, 0xb80a0			 ; 第1行行首mov edi, 0xb8000			 ; 第0行行首rep movsd				  mov ebx, 3840			     ; 最后一行填充为空白mov ecx, 80				     ; 一行是80字符(160字节),每次清空1字符(2字节),一行需要移动80次
.cls:mov word [gs:ebx], 0x0720    ;0x0720是黑底白字的空格键add ebx, 2loop .cls mov bx,1920				     ;将光标值重置为1920,最后一行的首字符.;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;设置光标值为bx
.set_cursor:   mov dx, 0x03d4			     ;索引寄存器mov al, 0x0e			     ;用于提供光标位置的高8位out dx, almov dx, 0x03d5			     ;通过读写数据端口0x3d5来获得或设置光标位置 mov al, bhout dx, almov dx, 0x03d4mov al, 0x0fout dx, almov dx, 0x03d5 mov al, blout dx, al.put_char_done: popadret

这个函数还是用汇编写的,因为涉及到操作一些端口。

四、打印字符串

由于我们已经实现了打印一个字符的方法,所以这里我们使用C编程来实现打印字符串,与打印数字

void put_str(uint8_t* _str) {while(*_str) {put_char(*_str);_str++;}return NULL;
}

调用我们在汇编中写的接口

五、打印数字

/* 把一个32位无符号数写到控制台光标处,十进制 */
void put_int(uint32_t num) {char int_digits[] = "0123456789";char int_string[20];  // 无符号整数转换为十六进制最多占据8位字符,再加上字符串结束符'\0'int i = 0;if (num == 0) {int_string[0] = '0';int_string[1] = '\0';} else {while (num != 0) {int_string[i] = int_digits[num % 10];num /= 10;i++;}int_string[i] = '\0';// 反转字符串int start = 0, end = i - 1;while (start < end) {char temp = int_string[start];int_string[start] = int_string[end];int_string[end] = temp;start++;end--;}}put_str(int_string);
}/* 把一个32位无符号数写到控制台光标处,十六进制 */
void put_hex(uint32_t num) {char hex_digits[] = "0123456789ABCDEF";char hex_string[9];  // 无符号整数转换为十六进制最多占据8位字符,再加上字符串结束符'\0'int i = 0;if (num == 0) {hex_string[0] = '0';hex_string[1] = '\0';} else {while (num != 0) {hex_string[i] = hex_digits[num % 16];num /= 16;i++;}hex_string[i] = '\0';// 反转字符串int start = 0, end = i - 1;while (start < end) {char temp = hex_string[start];hex_string[start] = hex_string[end];hex_string[end] = temp;start++;end--;}}put_char('0');put_char('x');put_str(hex_string);
}

写了两个接口,一个用来打印十进制,一个用来打印十六进制。

六、使用makefile

之前一直是几个程序,因为现在程序变多了,我们也使用一下makefile,提升编译效率

AS = nasm
CC = gcc
LD = ld
ACFLAG = -Wall -m32 -c -fno-builtin -fno-stack-protector -W -Wstrict-prototypes -Wmissing-prototypes 
ASFLAG = -I src/boot/
LDFLAG = -m elf_i386 -e main -Ttext 0xc0001500 -T os.lds###################################################################################### 头文件
INCUDIRS  := src/lib \src/lib/kernel
INCLUDE   := $(patsubst %, -I %, $(INCUDIRS))###################################################################################### C文件与S文件的编译
SRCDIRS   := src/kernel \src/lib \src/lib/kernelSFILES    := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.s))
CFILES    := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))
SFILENDIR := $(notdir $(SFILES))
CFILENDIR := $(notdir $(CFILES))
SOBJS     := $(patsubst %.s, build/%.o, $(SFILENDIR))
COBJS     := $(patsubst %.c, build/%.o, $(CFILENDIR))
OBJS 	  := $(COBJS) $(SOBJS) VPATH     := $(SRCDIRS)$(SOBJS):build/%.o:%.s$(AS) -f elf $(ASFLAG) $(INCLUDE) $< -o $@
$(COBJS):build/%.o:%.c$(CC) $(ACFLAG) $(INCLUDE) $< -o $@###################################################################################### 单独编译代码启动
bin/mbr.bin: src/boot/mbr.s$(AS) $(ASFLAG) $< -o $@
bin/loader.bin: src/boot/loader.s$(AS) $(ASFLAG) $< -o $@###################################################################################### 链接代码 # $(LD) $^ $(LDFLAGS) -o $@
bin/kernel.bin:$(OBJS)$(LD) $(LDFLAG) $^ -o $@.PHONY: mk_dir clear copy
# 查看编译缓存的文件是否存在,存在就继续,不存在则创建
mk_dir:if [ ! -d build ];then mkdir build;fiif [ ! -d bin ];then mkdir bin;fi
# 删除编译缓存的文件
clear:rm -rf ./build/*rm -rf ./bin/*
# 复制二进制程序
copy:dd if=bin/mbr.bin of=/home/lyj/bochs/bin/hd60M.img bs=512 count=1 seek=0 conv=notruncdd if=bin/loader.bin of=/home/lyj/bochs/bin/hd60M.img bs=512 count=8 seek=1 conv=notruncdd if=bin/kernel.bin of=/home/lyj/bochs/bin/hd60M.img bs=512 count=200 seek=10 conv=notrunc
# 启动仿真
begin:/home/lyj/bochs/bin/bochs -f /home/lyj/bochs/bin/bochsrc.disk 
# 编译程序
build: bin/mbr.bin bin/loader.bin bin/kernel.bin
# 启动所有程序
all: mk_dir clear build copy beginprint:@echo $(SOBJS)@echo $(COBJS)@echo $(OBJS)

七、仿真

看一下仿真结果

image-20240314223944164

结束语

我们今天可以在终端上输出字符了,其中有四个接口函数。显示字符还是比较简单的,只是往相应的内存位置填充字符即可。

/* 把一个字符写到控制台光标处 */
void put_char(uint8_t char_asci);
/* 把一个字符串写到控制台光标处 */
void put_str(uint8_t* _str);
/* 把一个32位无符号数写到控制台光标处,十进制 */
void put_int(uint32_t num);
/* 把一个32位无符号数写到控制台光标处,十六进制 */
void put_hex(uint32_t num);

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Jsp在Javaweb中扮演什么角色?

1.什么是Jsp JSP&#xff08;Java Server Pages&#xff0c;Java 服务器页面&#xff09;是一种动态网页技术&#xff0c;它允许在 HTML 页面中嵌入 Java 代码&#xff0c;并由 Web 服务器在请求页面时动态生成 HTML 页面。JSP 通常用于创建动态 Web 内容&#xff0c;如交互式表…

解锁基于LLMS的咒语:通过上下文学习重新思考对齐

一、写作动机&#xff1a; 最近的一项研究&#xff0c;LIMA&#xff0c;表明仅使用1K个示例进行SFT也可以实现显著的对齐性能&#xff0c;这表明对齐微调的效果可能是“表面的”。&#xff08;知识和推理能力来源于预训练&#xff0c;而不是必须通过对齐微调获得的。&#xff…

钉钉群内自定义机器人发送消息功能实现

文章目录 钉钉群内自定义机器人发送消息功能实现1、设置webhook自定义机器人2、查看官方文档&#xff0c;使用open api3、编写业务代码4、发送成功结果如下 钉钉群内自定义机器人发送消息功能实现 1、设置webhook自定义机器人 设置关键词 添加完成后&#xff0c;获得改机器人的…

小迪安全34WEB 攻防-通用漏洞文件上传黑白盒审计逻辑中间件外部引用

#知识点&#xff1a; 1、白盒审计三要素 2、黑盒审计四要素 3、白黑测试流程思路 #详细点&#xff1a; 1、检测层面&#xff1a;前端&#xff0c;后端等 2、检测内容&#xff1a;文件头&#xff0c;完整性&#xff0c;二次渲染等 3、检测后缀&#xff1a;黑名单&…

高级语言讲义2011计专(仅高级语言部分)

1.某公司采用公用电话传递数据&#xff0c;数据是四位的整数&#xff0c;为了安全。在传递过程中数据是加密的。加密规则如下&#xff0c;每位数字加5,然后用和除以10的余数代替该数字&#xff0c;再将第一位和第四位交换&#xff0c;第二位和第三位交换&#xff0c;编一程序&a…

Buran勒索病毒通过Microsoft Excel Web查询文件进行传播

Buran勒索病毒首次出现在2019年5月&#xff0c;是一款新型的基于RaaS模式进行传播的新型勒索病毒&#xff0c;在一个著名的俄罗斯论坛中进行销售&#xff0c;与其他基于RaaS勒索病毒(如GandCrab)获得30%-40%的收入不同&#xff0c;Buran勒索病毒的作者仅占感染产生的25%的收入,…

力扣热题100_矩阵_73_矩阵置零

文章目录 题目链接解题思路解题代码 题目链接 73.矩阵置零 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&…

机器视觉学习(一)—— 认识OpenCV、安装OpenCV

目录 一、认识OpenCV 二、通过pip工具安装OpenCV 三、PyCharm安装OpenCV 一、认识OpenCV OpenCV&#xff08;Open Source Computer Vision Library&#xff0c;开源计算机视觉库&#xff09;是一个跨平台的计算机视觉库&#xff0c;最初由威尔斯理工学院的Gary Bradski于199…

Python轴承故障诊断 (15)基于CNN-Transformer的一维故障信号识别模型

目录 往期精彩内容&#xff1a; 前言 1 轴承数据加载与预处理 1.1 导入数据 1.2 数据预处理&#xff0c;制作数据集 3 基于Pytorch的CNN-Transfromer轴承故障诊断分类 3.1 定义CNN-Transfromer分类网络模型 3.2 设置参数&#xff0c;训练模型 3.3 模型评估 代码、数据…

【学一点RISC-V】RISC-V IMSIC

IMSIC RISC-V AIA 文档 第三章 Incoming MSI Controller (IMSIC) 传入 MSI 控制器&#xff08;IMSIC&#xff09;是一个可选的 RISC-V 硬件组件&#xff0c;与 hart 紧密相连&#xff0c;每个 hart 有一个 IMSIC。IMSIC 接收并记录 Hart 的传入消息信号中断 (MSI)&#xff0c;并…

北京保险服务中心携手镜舟科技,助推新能源车险市场规范化

2022 年&#xff0c;一辆新能源汽车在泥泞的小路上不慎拖底&#xff0c;动力电池底壳受损&#xff0c;电池电量低。车主向保险公司报案&#xff0c;希望能够得到赔偿。然而&#xff0c;在定损过程中&#xff0c;保司发现这辆车的电池故障并非由拖底事件引起&#xff0c;而是由于…

学生时期学习资源同步-JavaSE手写代码试题

原创作者&#xff1a;田超凡&#xff08;程序员田宝宝&#xff09; 版权所有&#xff0c;引用请注明原作者&#xff0c;严禁复制转载 1、输入姓名、年龄、成绩之后&#xff0c;打印个人信息 2、输入考试成绩&#xff0c;分别用if和switch实现以下效果&#xff1a; >90 打…

Day31-计算机基础1

Day31-计算机基础1 1. 网络基础介绍1.1 什么是网络&#xff1f;1.2 为什么要有网络&#xff1f;1.3 运维人员需要学习哪些网络知识&#xff1f;1.4 按作用范围对网络分类 2.网络设备知识2.1 网络传输介质及传输信号2.2 网卡设备2.3 中继器&#xff08;RP repeater&#xff09;2…

轮趣 IMU N100 九轴 IMU 在 ROS 下安装驱动

本篇介绍如何在ROS环境中使用 WHEELTEC N100 惯导模块。 轮趣 IMU N100 的 ROS 驱动程序下载链接&#xff1a;轮趣 IMU 资料 - 坚果云 - 云盘|网盘|企业网盘|同步|备份|无限空间|免费网络硬盘|企业云盘 1、CP2102 固定串口号 1.1 、修改串口号 在 Windows 中需要把 WHEELTE…

15、设计模式之迭代器模式(Iterator)

一、什么是迭代器模式 迭代器模式是一种行为型设计模式&#xff0c;它提供了一种统一的方式来访问集合对象中的元素&#xff0c;而不是暴露集合内部的表示方式。简单地说&#xff0c;就是将遍历集合的责任封装到一个单独的对象中&#xff0c;我们可以按照特定的方式访问集合中的…

Python爬虫打印状态码为521,返回数据为乱码?

爬虫代码&#xff1a; import requests headers {User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36,Referer:https://www1.rmfysszc.gov.cn/projects.shtml?dh3&gpstate1&wsbm_slt1} …

SpringBoot 多平台文件配置

目录 一 主配置文件和辅配置文件 二 具体使用 1. 通过直接修改 application.yml 中的属性值 2. 通过 maven 进行配置修改 当我们需要部署项目的时候, 肯定会遇到不同的部署环境下, 需要有不同的配置. 例如开发环境下和生产环境下的配置肯定就不会是完全相同的, 如数据库的…

工地安全反光衣穿戴监测报警摄像机

工地安全反光衣穿戴监测报警摄像机是为了提高工地施工人员的安全意识和监管效率而设计的。这种设备结合了反光衣、监测系统和报警摄像机的功能&#xff0c;可以有效减少工地事故的发生。 首先&#xff0c;工地安全反光衣是一种具有高度可见度的服装&#xff0c;能够使穿戴者在夜…

如何在Win系统部署Tomcat服务并实现远程访问内网站点

文章目录 前言1.本地Tomcat网页搭建1.1 Tomcat安装1.2 配置环境变量1.3 环境配置1.4 Tomcat运行测试1.5 Cpolar安装和注册 2.本地网页发布2.1.Cpolar云端设置2.2 Cpolar本地设置 3.公网访问测试4.结语 正文开始前给大家推荐个网站&#xff0c;前些天发现了一个巨牛的人工智能学…

qt一个项目有且只有有一个maindow,其他小窗口用QWidget,QDialog是带有yes和no的QWidget

QMaindow QWidget QDialog区别很大 我想要在生成一个小窗口&#xff0c;结果选择基类为maindow&#xff0c;应该是QWidget 然后就出现奇奇怪怪的问题 QMaindow和QWidget不能乱选择&#xff0c;而且各自QPaintEvent也有很多区别 以下就是错误&#xff1a; 我继承maindow的基类…