《30天自制操作系统》第四天

76 / 100 SEO Score

C语言与画面显示的练习

昨天完成了黑屏的显示,今天主要完成白屏,花屏,方块屏,基本背景的显示等等。先用汇编直接操作内存,后用C语言中的指针再写一遍同样功能的函数;其中牵扯到自定义调色板的方法和EFLAGS的使用。以后编程基本上都是C语言,总算能脱离汇编了。


汇编实现颜色显示

老规矩,复制代码

把 \30天自制操作系统\projects\04_day\harib01a 复制到 \30天自制操作系统\tolset。

naskfunc.nas文件

; naskfunc
; TAB=4

[FORMAT "WCOFF"]                  ; 指定生成的目标文件格式为WCOFF
[INSTRSET "i486p"]                ; 指定代码使用i486的指令集
[BITS 32]                         ; 声明代码是为32位模式编写
; 制作目标文件的信息
[FILE "naskfunc.nas"]             ; 指定源文件名信息

        GLOBAL    _io_hlt,_write_mem8  ;函数的声明

[SECTION .text]                   ; 定义接下来的代码属于 .text 段

_io_hlt:    ; void io_hlt(void);
        HLT
        RET
;新函数:把data送入指定的addr中
_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

bootpack.c文件

void io_hlt(void);
void write_mem8(int addr, int data);

void HariMain(void)
{
	int i;

	for (i = 0xa0000; i <= 0xaffff; i++) {
		write_mem8(i, 15);               /* MOV BYTE [i],15 */
	}

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

asmhead.nas

;部分代码。
VMODE    EQU        0x0ff2            ; 颜色位数
SCRNX    EQU        0x0ff4            ; x分辨率
SCRNY    EQU        0x0ff6            ; y分辨率
VRAM     EQU        0x0ff8            ; 图像缓冲区(显存)开始地址

         ORG        0xc200            ; 程序装载地址

         MOV        AL,0x13           ; VGA显卡调用、320x200x8bit
         MOV        AH,0x00
         INT        0x10
         MOV        BYTE [VMODE],8    ; 记录画面模式
         MOV        WORD [SCRNX],320  ; 记录分辨率
         MOV        WORD [SCRNY],200
         MOV        DWORD [VRAM],0x000a0000 

这三段代码主要实现了调用bios中断,使屏幕显示纯白。

首先是asmhead.nas中设置BIOS中断调用和各种参数

;设置为VGA显卡模式(320x200x8bit) 
MOV AL,0x13
MOV AH,0x00
INT 0x10

之后把图像分辨率、颜色位数、显存地址存入所指定的内存单元中

VMODE    EQU        0x0ff2            ; 颜色位数
SCRNX EQU 0x0ff4 ; x分辨率
SCRNY EQU 0x0ff6 ; y分辨率
VRAM EQU 0x0ff8 ; 图像缓冲区(显存)开始地址
;……………………
;8存入0x0ff2单元
MOV BYTE [VMODE],8 ; 记录画面模式
;320存入0x0ff4单元,后面以此类推。
MOV WORD [SCRNX],320 ; 记录分辨率
MOV WORD [SCRNY],200
MOV DWORD [VRAM],0x000a0000

提醒下,内存地址和软盘中数据存放的地址是不同的。软盘中的文件要加载到内存中才能运行,ORG 0xc200、VMODE EQU 0x0ff2……这些地址均为内存地址。又因为ipl10.nas中DS寄存器初始化为0,所以这些地址是实际上的物理地址。

新函数:把data送入指定的addr中

_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

C语言函数调用中,传入的参数是从右向左压入栈的,具体而言void write_mem8(int addr, int data)函数中data先入栈,addr后入栈,执行完毕后函数的返回地址入栈。ESP是栈指针寄存器,指向栈顶的内存单元。栈是一种保存在内存中的数据结构,栈中保存的数据具有后进先出的特点。

其实就是一个桶,最先放进去的东西会被压在最下面。然而在计算机里这个桶是倒扣状态,存数据时要向上塞。

int类型变量在32位系统中占用4个字节,所以addr、data占用4个存储单元,所以MOV ECX,[ESP+4]就是把addr的数据存入ECX;之后通过AL,把data的数据存入ECX所指的内存单元中。

最后在C语言程序中

	int i;

	for (i = 0xa0000; i <= 0xaffff; i++) {
		write_mem8(i, 15);               /* MOV BYTE [i],15 */
	}

15是在 8 位彩色VGA 模式中白色的意思,是调色板索引。每个像素用一个8位数值表示0x00——0xFF,这个数值不储存颜色,而是一个指向颜色查找表的索引。总之就是这个数0——255随便填,每个数都表示不同颜色。

for循环中有两个问题,为什么内存从0xa0000到0xaffff?为什么传入int类型的15时,内存地址不+4?

  • 0xA0000是规定好的标准。显示的分辨率为320x200x8bit=64000byte。64000=0xFA00,取个整数0xA0000+0xFFFF=0xAFFFF。(其实这里改成i <= 0xafa00也完全不受影响)
  • 32位模式下,没有声明的数值默认都是int类型,除非超过int类型的范围,所以函数write_mem8(i, 15)中理论上来说地址应该+4而不是+1。这是因为MOV AL,[ESP+8]。AL是个8位寄存器,32位int传入8位寄存器时高24位被直接舍弃了。

最后,模拟运行。显示纯白

harib01b显示花屏这里就改了一行代码,write_mem8(i, i & 0x0f)。没什么好说的。


C语言实现颜色显示

harib01c、harib01d、harib01e三篇都是使用C语言的指针重写void write_mem8(int addr, int data)函数。并在naskfunc.nas中删除了write_mem8(int addr, int data)

/* harib01c */
	char *p;
	for (i = 0xa0000; i <= 0xaffff; i++) {
		p = i; 
		*p = i & 0x0f;
        }

/* harib01d */
	char *p;
	p = (char *) 0xa0000;
	for (i = 0; i <= 0xffff; i++) {
		*(p + i) = i & 0x0f;
	}

/* harib01e */
	char *p;
	p = (char *) 0xa0000;
	for (i = 0; i <= 0xffff; i++) {
		p[i] = i & 0x0f;
	}

以上三段代码的功能都是实现花屏。

提一下指针的概念char *p;作者把指针称之为地址变量我觉得总结的很好。

  • p = 3; 3作为地址赋值给p。
  • p = i; i中的数据作为地址赋值给p。
  • *p = 3; 指针的解引用,将3写入p所指的内存单元中。
  • i = *p; 指针的解引用将p地址中所存的值赋值给i。
  • *(p + i)和p[i]是等效的写法。此时p[i]不是数组,没有声明。

色号设置

由于颜色输出采用256色模式,号只有8位。而如今RGB采用16进制24位颜色码,所以需要对其进行映射,例如把#ffffff映射成15号索引。这些映射的索引编号和颜色均可自定义设置。

注意!!调色板中每个RGB每个通道只有6位,不是8位,这是硬件设计决定的

#000000:黑#ffffff:白#008484:浅暗蓝#0000ff:亮蓝
#00ffff:浅亮蓝#840084:暗紫#ffff00:亮黄#008400:暗绿
#000084:暗蓝#00ff00:亮绿#840000:暗红#ff00ff:亮紫
#ff0000:亮红#c6c6c6:亮灰#848484:暗灰#848400:暗黄

复制代码

把 \30天自制操作系统\projects\04_day\harib01f 复制到 \30天自制操作系统\tolset。

bootpack.c文件

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 HariMain(void)
{
    int i; 
    char *p; 

    init_palette(); 

    p = (char *) 0xa0000;

    for (i = 0; i <= 0xffff; i++) {
        p[i] = i & 0x0f;
    }

    for (;;) {
        io_hlt();
    }
}
/* init_palette函数主要是定义和输出颜色 */
/* 颜色只有24位,若定义成int类型(32位)会浪费空间 */
/* 所以用3个连续的char类型定义颜色 */
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;


}

void set_palette(int start, int end, unsigned char *rgb)
{
    int i, eflags;
    eflags = io_load_eflags();    /* 用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;
}

这段代码很简单,几个新加的函数无法用C语言,只能使用汇编编写。所以新的naskfunc.nas文件中多了很多汇编指令。

naskfunc.nas文件

; naskfunc
; TAB=4

[FORMAT "WCOFF"]                  ; 指定生成的目标文件格式为WCOFF
[INSTRSET "i486p"]                ; 使用了486的指令集
[BITS 32]                         ; 声明代码是为32位模式编写
[FILE "naskfunc.nas"]             ; 指定源文件名信息

; 声明全局函数
        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]             ; 定义接下来的代码属于 .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

首先bootpack.c文件中io_out8(0x03c8, start)中的0x03c8、0x03c9等解释。

硬件指令IO

简单的说,计算机中CPU与各种设备相连,为了进行数据输入输出,对各种设备进行了编号——端口号。0x03c8、0x03c9就是某个设备的编号而已。汇编语言中向设备发送电信号的是OUT指令;从设备取得电气信号的是IN指令。端口号不能随意指定。

  • IN AL, 21H ; 从21H端口读取一个字节到AL寄存器
  • OUT 21H,AL;将AL的值发送的21H端口

中断操作

io_load_eflags、io_store_eflags、io_cli、io_sti函数

新汇编指令:

  • CLT 关中断,中断标志位(IF)置0
  • STL 开中断,中断标志位(IF)置1
  • PUSH 入栈
  • POP 出栈
  • PUSHFD 将EFLAGS(标志位) 寄存器的值按双字长入栈
  • POPFD 将栈顶数据按双字长出栈并存入EFLAGS(标志位) 寄存器

调色板设置是一个原子操作,必须要关中断。这几个函数都是开关中断相关的函数。中断标志位(IF)表示是否允许中断,保存在EFLAGS寄存器中。EFLAGS寄存器是从16位的FLAGS寄存器扩展而来的32位寄存器。只能用PUSHFD和POPFD命令读写,也称标志寄存器或程序状态字(PSW)记录了各种标志信息。结构如下图。

开关中断前要保存EFLAGS寄存器的值。作者直接把它保存在eflags变量中。恢复时直接把eflags变量的值存入EFLAGS寄存器中,没有使用STL指令。

自定义调色板

VGA端口号详情可自行阅读https://wiki.osdev.org/VGA_Hardware#Port_0x3C8

其中记录了调色板的访问步骤

  • 关中断
  • 将想设定的调色板索引写入0x03c8,然后,按RGB的顺序写入0x03c9。如果还想继续设定下一个调色板,则省略调色板索引号码(此时索引会自动+1),再按照RGB的顺序写入0x03c9
  • 如果想要读出当前调色板的状态,首先要将调色板的号码写入0x03c7,再从0x03c9读取3次。读出的顺序就是R,G,B。如果要继续读出下一个调色板,同样省略调色板号码的设置,按RGB的顺序读出。
  • 开中断

解释下init_palette();这个函数

  • 首先用static unsigned char table_rgb[16 * 3]定义了一个数组,数组中每三个单元保存一个颜色的RGB值。其中的颜色顺序可自定义。
  • set_palette(0, 15, table_rgb);函数中0,15是自定义的调色板索引号。table_rgb是table_rgb数组的首地址。
  • set_palette函数中首先记录中断许可标志,之后关中断,然后通过io_out8(0x03c8, start);把自定义的索引号写入0x03c8的内存单元。之后用for循环,把RGB的值依次写入0x03c9。rgb[0] / 4的目的是RGB中每个颜色只有6位,table_rgb是char,每个值是8位。所以要右移2位。
  • 之后rgb += 3;即rgb=rgb+3。rgb指向了下一组RGB

这一段曾卡了我几天,主要还是书上介绍的不是很详细。举例来说set_palette(0, 15, table_rgb);若改成set_palette(10, 15, table_rgb);那么黑色的索引值就变成10,亮红的索引值就变成11……

运行结果


画图

这一节也很简单,就是单纯指定内存单元中保存什么值

复制代码

把 \30天自制操作系统\projects\04_day\harib01g 复制到 \30天自制操作系统\tolsetg。

bootpack.c文件

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);
/* 声明色号的对应关系 */
#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 HariMain(void)
{
    char *p; 

    init_palette(); /* 设置调色板 */

    p = (char *) 0xa0000; /* 设置显存初始地址 */

    boxfill8(p, 320, COL8_FF0000,  20,  20, 120, 120);
    boxfill8(p, 320, COL8_00FF00,  70,  50, 170, 150);
    boxfill8(p, 320, COL8_0000FF, 120,  80, 220, 180);

    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;

}

void set_palette(int start, int end, unsigned char *rgb)
{
    int i, eflags;
    eflags = io_load_eflags();    /* 用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 (y = y0; y <= y1; y++) {
        for (x = x0; x <= x1; x++)
            vram[y * xsize + x] = c;
    }
    return;
}

程序中多了个boxfill8函数

首先,画面上有320×200(=64 000)个像素假设左上点的坐标是(0,0),右下点的
坐标是(319,199),那么像素坐标,由于像素以行优先方式存入显存单元。(x,y)对应的VRAM地址应按下式计算

VRAM地址 = 0xa0000 + x + y * 320

于是void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);函数中

  • char *vram 显存地址
  • xsize x轴分辨率
  • c 颜色索引
  • x0 矩形左上角坐标(x轴)
  • y0 举行左上角坐标(y轴)
  • x1 矩形右下角坐标(x轴)
  • y1 矩形右下角坐标(y轴)

注意,要确保 x0, y0, x1, y1的值在320×200之中,否则会发生显存越界。

运行如下

今天最后开始画一个真正的界面吧

复制代码

把 \30天自制操作系统\projects\04_day\harib01h 复制到 \30天自制操作系统\tolset。

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);

#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 HariMain(void)
{
    char *vram;
    int xsize, ysize;

    init_palette();   /* 设置调色板 */
    vram = (char *) 0xa0000;   /* 设置显存初始地址 */
    xsize = 320;
    ysize = 200;

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

    boxfill8(vram, xsize, COL8_FFFFFF,  3,         ysize - 24, 59,         ysize - 24);
    boxfill8(vram, xsize, COL8_FFFFFF,  2,         ysize - 24,  2,         ysize -  4);
    boxfill8(vram, xsize, COL8_848484,  3,         ysize -  4, 59,         ysize -  4);
    boxfill8(vram, xsize, COL8_848484, 59,         ysize - 23, 59,         ysize -  5);
    boxfill8(vram, xsize, COL8_000000,  2,         ysize -  3, 59,         ysize -  3);
    boxfill8(vram, xsize, COL8_000000, 60,         ysize - 24, 60,         ysize -  3);

    boxfill8(vram, xsize, COL8_848484, xsize - 47, ysize - 24, xsize -  4, ysize - 24);
    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 -  3);
    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;

}

void set_palette(int start, int end, unsigned char *rgb)
{
    int i, eflags;
    eflags = io_load_eflags();    /* 用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 (y = y0; y <= y1; y++) {
        for (x = x0; x <= x1; x++)
            vram[y * xsize + x] = c;
    }
    return;
}

运行结果,只是张纯粹的图片。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇