自制操作系统日记(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的指针

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

相关文章
|
3月前
|
前端开发 Linux
深度探索Linux操作系统 —— 构建桌面环境3
深度探索Linux操作系统 —— 构建桌面环境
45 12
|
3月前
|
存储 搜索推荐 Linux
深度探索Linux操作系统 —— 构建桌面环境1
深度探索Linux操作系统 —— 构建桌面环境
56 8
|
3月前
|
Linux 编译器 C语言
深度探索Linux操作系统 —— 构建桌面环境2
深度探索Linux操作系统 —— 构建桌面环境
30 6
|
5月前
|
JSON 缓存 移动开发
原创自研uniapp+vue3手机桌面os管理系统
vue3-uniapp-os一款基于uniapp+vue3跨端手机版后台os系统新解决方案。
262 3
获取操作系统桌面路径
获取桌面路径和根目录。
113 0
|
Web App开发 测试技术 Windows