自制操作系统日记(6):静态桌面初步

简介: 在上篇中我们成功的加载跳转执行了C语言的代码,本篇中将跟随书籍,初步展示了一个系统页面的初步界面,看到桌面那刻还是有点意思的

代码仓库地址:https://github.com/freedom-xiao007/operating-system

简介

在上篇中我们成功的加载跳转执行了C语言的代码,本篇中将跟随书籍,初步展示了一个系统页面的初步界面,看到桌面那刻还是有点意思的

最终结果展示

不多啰嗦,这里先直接展示结果,最终的运行界面如下:

image.png

可以看到有点模样了,令人激动啊。当最终到这一步的时候,心里开心极了,哈哈

相关代码说明

本篇中的代码修改基本上是抄《30天自制操作系统》中的,但可能是nask和nasm的区别问题,我们需要单独修改的地方也不少,下面我们详细说明下需要修改的地方

setup.asm文件修改

这个文件需要添加界面相关的参数设置,我们将其插入到start的下面,如下:

start:
    ; 画面モードを設定
    MOV        AL,0x13            ; VGA显卡,320x200x8bit
    MOV        AH,0x00
    INT        0x10
    MOV        BYTE [VMODE],8    ; 屏幕的模式(参考C语言的引用)
    MOV        WORD [SCRNX],320
    MOV        WORD [SCRNY],200
    MOV        DWORD [VRAM],0x000a0000

func.asm文件修改

这次新增了比较多的函数,但不复杂,也不多,这个文件的内容如下:

[BITS 32]                          ; 制作32位模式用的机器语言
        GLOBAL  _io_hlt, _io_cli, _io_sti, io_stihlt
        GLOBAL  _io_in8,  _io_in16,  _io_in32
        GLOBAL  _io_out8, _io_out16, _io_out32
        GLOBAL  _io_load_eflags, _io_store_eflags
[SECTION .text]
_io_hlt:     ; void io_hlt(void);
        HLT
        RET
_io_cli:     ; void io_cli(void);
        CLI
        RET
_io_sti:     ; void io_sti(void);
        STI
        RET
_io_stihlt: ; void io_stihlt(void);
        STI
        HLT
        RET
_io_in8:     ; int io_in8(int port);
        MOV      EDX, [ESP+4]      ; port
        MOV      EAX,0
        IN       AL, DX
        RET
_io_in16:   ; int io_in16(int port);

        MOV      EDX, [ESP+4]      ; port
        MOV      EAX,0
        IN       AX, DX
        RET
_io_in32:   ; int io_in32(int port);
        MOV      EDX, [ESP+4]      ; port
        IN       EAX, DX
        RET
_io_out8:   ; void io_out8(int port, int data);
        MOV      EDX, [ESP+4]      ; port
        MOV      AL, [ESP+8]       ; data
        OUT      DX, AL
        RET
_io_out16:  ; void io_out16(int port, int data);
        MOV      EDX, [ESP+4]      ; port
        MOV      EAX, [ESP+8]      ; data
        OUT      DX, AX
        RET
_io_out32:  ; void io_out32(int port, int data);
        MOV      EDX, [ESP+4]      ; port
        MOV      EAX, [ESP+8]      ; data
        OUT      DX, EAX
        RET
_io_load_eflags:     ; int io_load_eflags(void);
        PUSHFD       ; 指PUSH EFLAGS
        POP      EAX
        RET
_io_store_eflags:   ; void io_store_eflags(int eflags);
        MOV      EAX, [ESP+4]
        PUSH EAX
        POPFD ; 指POP EFLAGS
        RET

c入口文件修改

本次直接使用书中的C相关代码,但有几处不适配,需要我们单独进行修改

  1. 第一处是原书中的boxfill8函数,用于绘制矩形的:原书中是选循环y再循环x,但在博主运行时,总是会多出一个memset函数,导致运行失败,系统不断重启

需要将其修改下,替换为先循环x再循环y(不断地尝试后发现的,但没有搞懂为啥会这样)

  1. 第二处是调用boxfill8时的线条高度问题:原书中下发的两个矩形都是线条出度为1左右(我猜测),但在博主尝试中,小于3的高度不会显示

这里就需要修改下传入boxfill8的y0和y1的差值最小为3

经过修改后,文件的所有内容如下:

#define COL8_000000 0
#define COL8_FF0000 1
#define COL8_00FF00 2
#define COL8_FFFF00 3
#define COL8_0000FF      4
#define COL8_FF00FF      5
#define COL8_00FFFF      6
#define COL8_FFFFFF      7
#define COL8_C6C6C6      8
#define COL8_840000      9
#define COL8_008400      10
#define COL8_848400      11
#define COL8_000084      12
#define COL8_840084      13
#define COL8_008484      14
#define COL8_848484      15

/*就算写在同一个源文件里,如果想在定义前使用,还是必须事先声明一下。*/
void io_hlt(void);
void io_cli(void);
void io_out8(int port, int data);
int io_load_eflags(void);
void io_store_eflags(int eflags);

void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);

void start(void)
{
    init_palette(); /* 设定调色板 */

    char *vram; /* 变量p是BYTE [...]用的地址 */
    vram = (char *) 0xa0000; /* 指定地址 */
    int xsize = 320;
    int ysize = 200;

    boxfill8(vram, xsize, COL8_008484,  0,         0,          xsize, ysize - 29);
    boxfill8(vram, xsize, COL8_C6C6C6,  0,         ysize - 28, xsize, ysize - 28);
    boxfill8(vram, xsize, COL8_FFFFFF,  0,         ysize - 27, xsize, ysize - 27);
    boxfill8(vram, xsize, COL8_C6C6C6,  0,         ysize - 26, xsize, ysize -  1);

    //     y距离有时候需要大于3
    boxfill8(vram, xsize, COL8_FFFFFF,  3,         ysize - 24, 59,         ysize - 21);
    boxfill8(vram, xsize, COL8_FFFFFF,  2,         ysize - 24,  2,         ysize -  4);
    boxfill8(vram, xsize, COL8_848484,  3,         ysize -  4, 59,         ysize -  1);
    boxfill8(vram, xsize, COL8_848484, 59,         ysize - 23, 59,         ysize -  5);
    boxfill8(vram, xsize, COL8_000000,  2,         ysize -  3, 59,         ysize -  0);
    boxfill8(vram, xsize, COL8_000000, 60,         ysize - 24, 60,         ysize -  3);

    boxfill8(vram, xsize, COL8_848484, xsize - 47, ysize - 24, xsize -  4, ysize - 21);
    boxfill8(vram, xsize, COL8_848484, xsize - 47, ysize - 23, xsize - 47, ysize -  4);
    boxfill8(vram, xsize, COL8_FFFFFF, xsize - 47, ysize -  3, xsize -  4, ysize -  0);
    boxfill8(vram, xsize, COL8_FFFFFF, xsize -  3, ysize - 24, xsize -  3, ysize -  3);

    for (; ; ) {
        io_hlt();
    }
}

void init_palette(void)
{
    static unsigned char table_rgb[16 * 3] = {
        0x00, 0x00, 0x00,   /*  0:黑 */
        0xff, 0x00, 0x00, /* 1:亮红 */
        0x00, 0xff, 0x00, /* 2:亮绿 */
        0xff, 0xff, 0x00, /* 3:亮黄 */
        0x00, 0x00, 0xff, /* 4:亮蓝 */
        0xff, 0x00, 0xff, /* 5:亮紫 */
        0x00, 0xff, 0xff, /* 6:浅亮蓝 */
        0xff, 0xff, 0xff, /* 7:白 */
        0xc6, 0xc6, 0xc6, /* 8:亮灰 */
        0x84, 0x00, 0x00, /* 9:暗红 */
        0x00, 0x84, 0x00, /* 10:暗绿 */
        0x84, 0x84, 0x00, /* 11:暗黄 */
        0x00, 0x00, 0x84, /* 12:暗青 */
        0x84, 0x00, 0x84, /* 13:暗紫 */
        0x00, 0x84, 0x84, /* 14:浅暗蓝 */
        0x84, 0x84, 0x84 /* 15:暗灰 */
    };
    set_palette(0, 15, table_rgb);
    return;

    /* C语言中的static char语句只能用于数据,相当于汇编中的DB指令 */
}

void set_palette(int start, int end, unsigned char *rgb)
{
    int i, eflags;
    eflags = io_load_eflags();  /* 记录中断许可标志的值*/
    io_cli();                      /* 将中断许可标志置为0,禁止中断 */
    io_out8(0x03c8, start);
    for (i = start; i <= end; i++) {
        io_out8(0x03c9, rgb[0] / 4);
        io_out8(0x03c9, rgb[1] / 4);
        io_out8(0x03c9, rgb[2] / 4);
        rgb += 3;
    }
    io_store_eflags(eflags);     /* 复原中断许可标志 */
    return;
}

void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
{
    int x, y;
    for (x = x0; x <= x1; x++) {
        for (y = y0; y <= y1; y++) {
            vram[y * xsize + x] = c;
        }
    }
    return;
}

脚本还是使用原来的,本篇中没有变化,一键运行后,出现文章开头令人激动的画面

初步感受C代码转汇编

在书中开始时介绍了不同手段实现同一个效果:给内存赋值

第一个方式是使用wirte_mem8时的编译nasm代码:使用写汇编函数的方式,根据传入的值,写到相应的内存中

void write_mem8(int addr, int data);
void start(void) {
    int i;
    for (i = 0xa0000; i < 0xaffff; i++) {
    write_mem8(i, i & 0x0f);
    }
}
ALIGN   8
?_001:  mov     eax, ebx                                ; 0018 _ 89. D8
        mov     dword [esp], ebx                        ; 001A _ 89. 1C 24
        add     ebx, 1                                  ; 001D _ 83. C3, 01
        and     eax, 0FH                                ; 0020 _ 83. E0, 0F
        mov     dword [esp+4H], eax                     ; 0023 _ 89. 44 24, 04
        call    _write_mem8                             ; 0027 _ E8, 00000000(rel)
        cmp     ebx, 720895                             ; 002C _ 81. FB, 000AFFFF
        jnz     ?_001                                   ; 0032 _ 75, E4

_write_mem8:     ; void write_mem8(int addr, int data);
        MOV      ECX, [ESP+4]      ; [ESP + 4]中存放的是地址,将其读入ECX
        MOV      AL, [ESP+8]       ; [ESP + 8]中存放的是数据,将其读入AL
        MOV      [ECX], AL
        RET

第二个是指针,使用char*时的编译nasm代码:突然有种回到以前学习C的感觉,想到初期使用指针时的痛苦

void start(void) {
    int i;
    char *p;
    for (i = 0xa0000; i < 0xaffff; i++) {
    p = (char *) i;
    *p = i & 0x0f;
    }

    /* 或者下面这样写,生成的汇编码也一样 */
    char *p;
    p = (char *) 0xa0000; /*将地址赋值进去*/
    for (i = 0; i <= 0xffff; i++) {
        p[i] = i & 0x0f;
    }
}
?_001:  mov     edx, eax                                ; 0018 _ 89. C2
        add     eax, 1                                  ; 001A _ 83. C0, 01
        and     edx, 0FH                                ; 001D _ 83. E2, 0F
        mov     byte [eax-1H], dl                       ; 0020 _ 88. 50, FF
        cmp     eax, 720895                             ; 0023 _ 3D, 000AFFFF
        jnz     ?_001                                   ; 0028 _ 75, EE

可以看到两种不同的实现方式,对C的指针有了更深的理解,了解底层确实是有一定帮助的

总结

本文相对而言还是比较顺利的,虽然有一点坎坷,但在不断的调试中,顺利解决了

还进一步理解了C的指针

但关于书中的控制屏幕显示部分,目前还不是很通透,很需要再刷刷

相关文章
|
9月前
|
小程序 数据可视化 数据库
云开发(微信-小程序)笔记(十七)---- cms(内容管理)及案例
云开发(微信-小程序)笔记(十七)---- cms(内容管理)及案例
412 0
|
11月前
|
Windows
谈一谈|电脑(Windows系统)隐藏神技
谈一谈|电脑(Windows系统)隐藏神技
94 0
|
Web App开发 文字识别 Linux
上网神器-油小猴工具箱 及解决添加扩展但启动不成功问题
此工具箱中包含了五款实用的油猴脚本,即网盘直链下载助手、网盘智能识别助手、网页加快器、星号密码显示助手、Mactype助手,它保留了每个脚本的功能,并对某些功能进行了加强,用户使用起来更加方便。
上网神器-油小猴工具箱 及解决添加扩展但启动不成功问题
|
iOS开发 MacOS Windows
Xmind2023电脑版永久免费的思维导图软件
等待许久的Xmind 2023成功激活,全新的思维导图软件,它具有新主题,其中一些具有更柔和的色调,以提供更现代的外观。它的独特功能是“禅宗模式”,它将自动隐藏额外的面板,使您可以专注于自己的想法并添加到文档中,而不会分心。XMind2023这款软件能够为用户提供非常先进的产品功能和体验,使用过后你会惊奇的发现,你的各项工作效率都在大大提高,这样会帮助你节省大量的时间,方便提升自己工作上的便利,就我个人而言,使用几天后还是非常方便的,效率提升很大。
1127 0
|
Python
自制操作系统日记(四):进入64位模式
在上篇中从初始启动文件中加载了loader文件进行执行,本篇将在loader进行CPU模式切换,进入64位模式
|
Oracle 关系型数据库 Linux
想要一款小巧精美的虚拟机软件?VirtualBox可能是你的菜
想要一款小巧精美的虚拟机软件?VirtualBox可能是你的菜
想要一款小巧精美的虚拟机软件?VirtualBox可能是你的菜
|
JavaScript Windows CDN
【超实用技巧】✨| 提高写文的质量 和 速率必学技能:❤️ Typora 图床配置 详细说明 ❤️ 【Windows版】
❤️提高写文的质量 和 速率必学技能:Typora图床配置 【Windows版】 📢前言 🌲图床简介 🌲Typora简介 🌟方法一:PicGo-Core(command line) + gitee仓库 🏳️‍🌈1. 安装Typora软件 🏳️‍🌈2. 在Typora里下载PicGo-Core(command line)插件 🏳️‍🌈3.安装Node.js 🏳️‍🌈4. 借助picgo安装gitee图床插件 🏳️‍🌈5.创建一个仓库,gitee私人令牌获取 🏳️‍🌈6.修改picgo配置文件 🏳️‍🌈7.上传图片测试
【超实用技巧】✨| 提高写文的质量 和 速率必学技能:❤️ Typora 图床配置 详细说明 ❤️ 【Windows版】
|
数据库 Windows
艾伟:基于.NET平台的Windows编程实战(三)—— 项目的创建及主界面的设计
第一步:创建一个新的Windows项目 打开VS2005,点击“文件”-->“新建”-->“项目”,在弹出的对话框里,在左边选择“Windows”,在右边选择“Windows应用程序”,并在下面的名称里输入“QuestionnaireSystem”,选择相应的保存位置后,点“确定”,如下图3-1所示:                                  图3-1  这样一个新的Windows项目就创建好了。
726 0
|
数据库 Windows
艾伟_转载:基于.NET平台的Windows编程实战(三)—— 项目的创建及主界面的设计
第一步:创建一个新的Windows项目 打开VS2005,点击“文件”-->“新建”-->“项目”,在弹出的对话框里,在左边选择“Windows”,在右边选择“Windows应用程序”,并在下面的名称里输入“QuestionnaireSystem”,选择相应的保存位置后,点“确定”,如下图3-1所示:                                  图3-1  这样一个新的Windows项目就创建好了。
783 0
WordPress 5文章编辑真难用 换回老版经典编辑器教程
WordPress 5文章编辑真难用 换回老版经典编辑器教程 WordPress 5 正式采用了全新的“Block Editor”编辑器,从而替换了原有“Classic Editor”编辑器,相信有很多人和子凡一样会不习惯或者不喜欢新编辑器,那么新版 WordPress 该如何换回原来的 WordPress 编辑器呢? 不可否认 WordPress 还是非常人性化的,至少会考虑和兼顾更多的用户,不然子凡也不可能青睐 WordPress,所以及时 WordPress 5更换了全新的编辑器,但是也依旧给大家提供了快速便捷切换到原编辑器的插件:“Classic Editor”。
4643 0