热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

动手写操作系统5C语言实现绘制系统界面

本节实现C语言与汇编互相调用,利用C语言实现系统界面绘制功能。之前显示字符串等功能都是使用显卡的字符界面模式,接下来需要打开显卡的图形模式,打开显卡图形模式需要使用BIOSINT

本节实现C语言与汇编互相调用,利用C语言实现系统界面绘制功能。

之前显示字符串等功能都是使用显卡的字符界面模式,接下来需要打开显卡的图形模式,打开显卡图形模式需要使用BIOS INT 0x10中断

;设置屏幕色彩模式
mov al, 0x13
mov ah, 0
INT 0x10

其中al 的值决定了要设置显卡的色彩模式,下面是一些常用的模式设置:

  1. 0x03, 16色字符模式
  2. 0x12, VGA图形模式, 640 * 480 * 4位彩色模式,独特的4面存储模式
  3. 0x13, VGA图形模式, 320 * 200 * 8位彩色模式,调色板模式
  4. 0x6a, 扩展VGA图形模式, 800 * 600 * 4彩色模式

这里采用0x13模式,其中320*200*8 中,最后的数值8表示的是色彩值的位数,也就是我们可以用8位数值表示色彩,总共可以显示256种色彩。系统显存的地址是0x000a0000,往显存地址写入数据,那么屏幕就会出现相应的变化。

具体实现过程如下:

1. 准备C语言绘制系统界面函数

write_vram.c

//extern void io_hlt();
void showChar() {
int i;
char *p = (char *)0xa0000;
for(i=0; i<=0xFFFF; i++){
*p = i & 0x0F;
p++;
}
for(;;){
io_hlt();
}
}

将指针P指向vga显存地址0xa0000, vga显存地址从0xa0000开始,直到0xaffff结束,总共64k.
*p = i & 0x0f 将一个数值写入显存,这个值可以是0-256中任意一个数值,这里将i的最后4位作为像素颜色写入显存

2.将C语言函数编译为二进制文件

gcc -m32 -c -fno-asynchronous-unwind-tables -fno-pic write_vram.c -o write_vram.o

这里我使用的gcc版本为gcc (Ubuntu 8.3.0-6ubuntu1) 8.3.0,不同的gcc编译遇到的问题可能不一样

这里在编译时注意增加编译条件,由于编译之后的汇编文件需要反汇编,关联到kernel.asm中,因此这里要添加编译条件

-m32:编译出来的是32位程序,既可以在32位操作系统运行,又可以在64位操作系统运行。

对于C语言编译不太熟悉,感觉版本依赖太强,后面慢慢研究吧。

3.安装objconv 反汇编二进制文件得到汇编文件

反汇编工具objconv安装 https://github.com/vertis/objconv.git
下载后进入objconv目录,编译该工具,运行下面的命令:
 

g++ -o objconv -O2 src/*cpp , -O2中的圆圈是大写字母O

或者

g++ -o objconv -O2 src/*cpp --std=c++98

这里由于C++语言版本的问题,可能在编译objconv会出现类型转换报错

src/coff.cpp:142:1: error: narrowing conversion of ‘2147483648’ from ‘unsigned int’ to ‘int’ inside { } [-Wnarrowing],

这里添加C++版本98即可解决。

编译objconv完成之后,反汇编上一步二进制文件write_vram.o得到汇编文件write_vram.asm

./objconv -fnasm write_vram.o -o write_vram.asm

4.编辑汇编文件write_vram.asm

主要是去掉一些全局声明和段描述信息

; Disassembly of file: write_vram.o
; Sun Feb 9 10:04:31 2020
; Mode: 32 bits
; Syntax: YASM/NASM
; Instruction set: 80386
;global showChar: function
;extern io_hlt ; near
;SECTION .text align=1 execute ; section number 1, code
showChar:; Function begin
push ebp ; 0000 _ 55
mov ebp, esp ; 0001 _ 89. E5
sub esp, 24 ; 0003 _ 83. EC, 18
mov dword [ebp-0CH], 655360 ; 0006 _ C7. 45, F4, 000A0000
mov dword [ebp-10H], 0 ; 000D _ C7. 45, F0, 00000000
jmp ?_002 ; 0014 _ EB, 15
?_001: mov eax, dword [ebp-10H] ; 0016 _ 8B. 45, F0
and eax, 0FH ; 0019 _ 83. E0, 0F
mov edx, eax ; 001C _ 89. C2
mov eax, dword [ebp-0CH] ; 001E _ 8B. 45, F4
mov byte [eax], dl ; 0021 _ 88. 10
add dword [ebp-0CH], 1 ; 0023 _ 83. 45, F4, 01
add dword [ebp-10H], 1 ; 0027 _ 83. 45, F0, 01
?_002: cmp dword [ebp-10H], 65535 ; 002B _ 81. 7D, F0, 0000FFFF
jle ?_001 ; 0032 _ 7E, E2
?_003: call io_hlt ; 0034 _ E8, FFFFFFFC(rel)
jmp ?_003 ; 0039 _ EB, F9
; showChar End of function
;SECTION .data align=1 noexecute ; section number 2, data
;SECTION .bss align=1 noexecute ; section number 3, bss

5.编辑kernel.asm

kernel.asm汇编代码如下:

; 全局描述符结构 8字节
; byte7 byte6 byte5 byte4 byte3 byte2 byte1 byte0
; byte6低四位和 byte1 byte0 表示段偏移上限
; byte7 byte4 byte3 byte2 表示段基址
;定义全局描述符数据结构
;3 表示有3个参数分别用 %1、%2、%3引用参数
;%1:段基址 %2:段偏移上限 %3:段属性
%macro GDescriptor 3
dw %2 & 0xffff ;设置段偏移上限0,1字节
dw %1 & 0xffff ;设置段基址2,3字节
db (%1>>16) & 0xff ;设置段基址4字节
dw ((%2>>8) & 0x0f00) | (%3 & 0xf0ff) ;设置段偏移上限6字节低四位
db (%1>>24) & 0xff ;设置段基址7字节
%endmacro
DA_32 EQU 0x4000 ;32位段属性
DA_CODE EQU 0x98 ;执行代码段属性
DA_RW EQU 0x92 ;读写代码段属性值
org 0x9000 ;内核代码在内存中起始加载处
jmp entry
[SECTION .gdt]
;全局描述符 段基址 段偏移上线 段属性
LABLE_GDT: GDescriptor 0, 0, 0
LABLE_DESC_CODE: GDescriptor 0, SegCodeLen-1, DA_CODE + DA_32
LABLE_DESC_VIDEO: GDescriptor 0xb8000, 0xffff, DA_RW ;显存内存地址从0xB8000开始
LABLE_DESC_STACK: GDescriptor 0, STACK_TOP-1, DA_32 + DA_RW ;函数堆栈
LABLE_DESC_VRAM: GDescriptor 0, 0xffffffff, DA_RW ;显存描述符
;GDT表大小
GdtLen EQU $ - LABLE_GDT
;GDT表偏移上限和基地址
GdtPtr dw GdtLen-1
dd 0
;cpu寻址方式
;实模式 段寄存器×16+偏移地址
;保护模式下 段寄存器中存储的是GDT描述表各个描述符的偏移
SelectorCode32 EQU LABLE_DESC_CODE - LABLE_GDT
SelectorVideo EQU LABLE_DESC_VIDEO - LABLE_GDT
SelectorStack EQU LABLE_DESC_STACK - LABLE_GDT
SelectorVRAM EQU LABLE_DESC_VRAM - LABLE_GDT
[SECTION .s16]
[BITS 16]
entry:
mov ax, cs
mov ss, ax
mov ds, ax
mov es, ax
mov sp, 0x100
;设置屏幕色彩模式
mov al, 0x13
mov ah, 0
INT 0x10
;设置LABLE_DESC_CODE描述符段基址
mov eax, 0
mov ax, cs
shl eax, 4
add eax, SEG_CODE32
mov word [LABLE_DESC_CODE+2], ax
shr eax, 16
mov [LABLE_DESC_CODE+4], al
mov [LABLE_DESC_CODE+7], ah
;设置栈空间
xor eax, eax
mov ax, cs
shl eax, 4
add eax, LABLE_STACK
mov word [LABLE_DESC_STACK+2], ax
shr eax, 16
mov byte [LABLE_DESC_STACK+4], al
mov byte [LABLE_DESC_STACK+7], ah
mov eax, 0
mov ax, ds
shl eax, 4
add eax, LABLE_GDT
mov dword [GdtPtr+2], eax

;设置GDTR寄存器
lgdt [GdtPtr]
cli ;关中断
;打开A20
in al, 0x92
or al, 0x02
out 0x92, al

;进入保护模式CR0寄存器最低位PE设为1
mov eax, cr0
or eax, 1
mov cr0, eax
jmp dword SelectorCode32:0
[SECTION .s32]
[BITS 32]
SEG_CODE32:
mov ax, SelectorStack
mov ss, ax
mov esp, STACK_TOP
mov ax, SelectorVRAM
mov ds, ax
call showChar ;调用c语言函数
GLOBAL io_hlt ;声明函数供c语言调用 void io_hlt();
io_hlt:
HLT
RET
;注意汇编文件引入位置 在代码段结束符之上
%include "write_vram.asm"
;32位模式代码长度
SegCodeLen EQU $ - SEG_CODE32
[SECTION .gs]
ALIGN 32
[BITS 32]
LABLE_STACK:
TIMES 512 DB 0
STACK_TOP EQU $ - LABLE_STACK

这里主要注意引入write_vram.asm的汇编文件的位置在代码段结束符之上。

添加了对于显存段和栈的描述符定义以及初始化代码,系统启动加载内核之后,内核实现段描述符的初始化,然后进入到保护模式,跳转到代码段处开始执行,在代码段处首先将栈段描述符放入ss寄存器,esp寄存器指向栈顶,将显存段描述符放入ds,然后调用showChar函数。

6.编译boot.asm和kernel.asm

编译kernel.asm   编译之后的kernel字节大于1个扇区,也就是说内核代码应该放在软盘的多个扇区位置,之前默认放在1柱面2扇区

nasm -f bin kernel.asm -o kernel

修改makeFloppy.c  将kernel从1柱面2扇区开始往后写入  默认最大写入16个扇区  当超过16个扇区时,这种方式写入会有问题,以后会修改。

#include
#include
#include "floppy.h"
#include
int main(int argc, char *argv[]){
printf("boot %s \n", argv[1]);
FILE* boot = fopen(argv[1], "r");
printf("kernel %s \n", argv[2]);
FILE* kernel = fopen(argv[2], "r");
printf("img %s \n", argv[3]);
FILE* img = initFloppy(argv[3]);
if(boot == NULL || kernel == NULL){
printf("The boot or kernel file not found");
exit(0);
}

//写引导扇区cylinder0 sector1
char buf[512];
memset(buf, 0, 512);
fread(buf, 512, 1, boot);
writeFloppy(0, 0, 1, img, buf);
//写内核 cylinder1 sector2
for(int i=0; !feof(kernel); i++){
memset(buf, 0, 512);
fread(buf, 512, 1, kernel);
writeFloppy(1, 0, 2+i, img, buf);
printf("The %d read kernel write img sector %d \n", i+1, 2+i);
}
fclose(boot);
fclose(kernel);
}

修改boot.asm  系统启动后将软盘1柱面2扇区之后的16个扇区全部读入到内核代码起始处

org 0x7c00
LOAD_ADDR EQU 0x9000 ;内核代码起始处
jmp entry
db 0x90
DB "OSKERNEL"
DW 512
DB 1
DW 1
DB 2
DW 224
DW 2880
DB 0xf0
DW 9
DW 18
DW 2
DD 0
DD 2880
DB 0,0,0x29
DD 0xFFFFFFF
DB "MYFIRSTOS "
DB "FAT12 "
RESB 18
entry:
mov ax, 0
mov ss, ax
mov ds, ax
mov es, ax
readFloppy:
mov ch, 1 ;磁道号cylinder
mov dh, 0 ;磁头号head
mov cl, 2 ;扇区号sector

mov bx, LOAD_ADDR ;数据存储缓冲区即将内核从该位置开始加载到内存中
mov ah, 0x02 ;读扇区
mov al, 16 ;连续读取扇区数量 先读取16个扇区数
mov dl, 0 ;驱动器编号
INT 0x13 ;调用BIOS中断
jc fin
jmp LOAD_ADDR
fin:
HLT
jmp fin
TIMES 0x1FE-($-$$) DB 0x00
DB 0x55, 0xAA

执行脚本run.sh

#!/bin/bash
nasm boot.asm
echo "nasm boot.asm"
nasm kernel.asm
echo "nasm kernel.asm"
gcc floppy.c makeFloppy.c -o makeFloppy
echo "gcc floppy.c makeFloppy.c -o makeFloppy"
./makeFloppy boot kernel system.img
echo "./makeFloppy boot kernel system.img"

利用virtualBox 加载system.img最终结果如下:

总结分析

        这里通过编译C语言,然后反汇编,再整合所有汇编文件,编译得到最终结果。其实也可以以C语言为核心,通过内联汇编来开发操作系统。核心点:

1.C语言与汇编语言是如何交互的

2.显存图形功能的使用

3.C语言的执行过程  参数传递   函数栈     

https://blog.csdn.net/u014106644/article/details/97260697

        在kernel.asm中添加了对于显存和函数栈描述符的定义  这里定义显存段描述符为0到4GB,定义函数栈为512B,即C语言函数执行过程中使用栈空间不能超过512B。具体细节以后慢慢研究。

代码位置  https://github.com/ChenWenKaiVN/bb_os_core/tree/develop

参考资料:

https://stackoverflow.com/questions/17676026/converting-c-to-nasm-assembly

https://blog.csdn.net/qq_31567335/article/details/100531788

https://stackoverflow.com/questions/22634337/error-compiling-asm-binary-format-does-not-support-any-special-symbol-types

https://blog.csdn.net/cloudblaze/article/details/78526456

https://gcc.gnu.org/onlinedocs/gcc-8.3.0/gcc.pdf

https://www.jianshu.com/p/b27105b9b07d


推荐阅读
  • c语言基础编写,c语言 基础
    本文目录一览:1、C语言如何编写?2、如何编写 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • C语言注释工具及快捷键,删除C语言注释工具的实现思路
    本文介绍了C语言中注释的两种方式以及注释的作用,提供了删除C语言注释的工具实现思路,并分享了C语言中注释的快捷键操作方法。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 恶意软件分析的最佳编程语言及其应用
    本文介绍了学习恶意软件分析和逆向工程领域时最适合的编程语言,并重点讨论了Python的优点。Python是一种解释型、多用途的语言,具有可读性高、可快速开发、易于学习的特点。作者分享了在本地恶意软件分析中使用Python的经验,包括快速复制恶意软件组件以更好地理解其工作。此外,作者还提到了Python的跨平台优势,使得在不同操作系统上运行代码变得更加方便。 ... [详细]
  • ejava,刘聪dejava
    本文目录一览:1、什么是Java?2、java ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 本文介绍了基于c语言的mcs51单片机定时器计数器的应用教程,包括定时器的设置和计数方法,以及中断函数的使用。同时介绍了定时器应用的举例,包括定时器中断函数的编写和频率值的计算方法。主函数中设置了T0模式和T1计数的初值,并开启了T0和T1的中断,最后启动了CPU中断。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • c语言\n不换行,c语言printf不换行
    本文目录一览:1、C语言不换行输入2、c语言的 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 深度学习中的Vision Transformer (ViT)详解
    本文详细介绍了深度学习中的Vision Transformer (ViT)方法。首先介绍了相关工作和ViT的基本原理,包括图像块嵌入、可学习的嵌入、位置嵌入和Transformer编码器等。接着讨论了ViT的张量维度变化、归纳偏置与混合架构、微调及更高分辨率等方面。最后给出了实验结果和相关代码的链接。本文的研究表明,对于CV任务,直接应用纯Transformer架构于图像块序列是可行的,无需依赖于卷积网络。 ... [详细]
author-avatar
小白家的世界
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有