动手写操作系统6----调色板

        本节实现调色板功能,之前开启显存的图形模式,对于每一个像素点利用8位数字来表示颜色,可以得到256种颜色,颜色丰富度不够,因此使用调色板功能来增强颜色显示,使用RGB模式,表示一个RGB颜色需要24位数。

调色板功能简单实现

        调色板原理:把多种RGB颜色的24位数值放入到一个数组中,原来的八位数不再对应一个颜色值,而是变成这个数组的下标,硬件在显示像素颜色时,从像素对应的显存读取这个八位数,然后把这个数当做下标,在RGB颜色素组中找到对应的RGB颜色值,再把这个颜色值显示到对应的像素上。这里使用以下16种RGB颜色:

0x000000 全黑
0xff0000 亮红
0x00ff00 亮绿
0xffff00 亮黄
0x0000ff 亮蓝
0xff00ff 亮紫
0x00ffff 浅亮
0xffffff 全白
0xc6c6c6 亮灰
0x840000 暗红
0x008400 暗绿
0x848400 暗黄
0x000084 暗蓝
0x840084 暗紫
0x008484 浅暗蓝
0x848484 暗灰

显存调色板模式的设置方式:

  • 关闭中断,防止CPU被干扰
  • 将调色板的号码写入端口0x03c8, 由于我们现在只有一个调色板,因此调色板的编号默认为0,如果要设置多个调色板,那么其他调色板的编号可以一次为1,2...等
  • 将RGB的颜色数值依次写入端口0x3c9, 假设我们要写入的RGB颜色值是
    0x848484 暗灰
    那么在C语言中,要分3次调用io_out8, 例如:
    io_out(0x3c9, 0x84);
    io_out(0x3c9, 0x84);
    io_out(0x3c9, 0x84);

端口读写数据原理

IN指令从指定端口读取数据   in al, dx 把端口dx的数据读入寄存器al

汇编语言与C语言函数调用对应关系   port号压入堆栈  mov edx, [esp+4]将port读入edx寄存器  函数值通过eax寄存器返回

这里寄存器eax 32位   ax(16)=ah(8)+al(8)

;读取指定端口8位数据  int io_in8(int port);
io_in8:
    mov edx, [esp+4]
    mov eax, 0
    in al, dx
    ret

OUT指令向端口写数据  out  dx, al   把寄存器al中数据写入到端口dx

调用io_out8函数   参数从右向左压入堆栈,

因此mov edx, [esp+4]将端口号port读入edx      mov al, [esp+8]将要写入的数据value读入al

;向指定端口写入8位数据 void io_out8(int port, int value);
io_out8:
    mov edx, [esp+4]
    mov al, [esp+8]
    out dx, al
    ret

eflags寄存器

Flag Register(标志寄存器)

                                        
CF进位标志位     PF奇偶标志位     AF辅助进位标志位      ZF零标志位      SF符号标志位   OF溢出标志位

IF中断允许标志位,由CLI,STI两条指令来控制;设置IF位使CPU可识别外部(可屏蔽)中断请求,复位IF位则禁止中断,IF位对不可屏蔽外部中断和故障中断的识别没有任何作用;

DF向量标志位,由CLD,STD两条指令来控制;

 IOPL I/O特权级字段,它的宽度为2位,它指定了I/O指令的特权级。如果当前的特权级别在数值上小于或等于IOPL,那么I/O指令可执行。否则,将发生一个保护性故障中断;

NT控制中断返回指令IRET,它宽度为1位。若NT=0,则用堆栈中保存的值恢复EFLAGS,CS和EIP从而实现中断返回;若NT=1,则通过任务切换实现中断返回。

eflags寄存器读写

        汇编语言能直接访问EFLAGS寄存器,因为汇编中没有 "mov eax, EFLAGS" 这种指令,pushfd 指令是专门把EFLAGS寄存器的内容压入堆栈的,使用指令 pushfd把EFLAGS的数据压入堆栈,然后再从堆栈中,把压入的数据弹出到eax寄存器里面。

;获取程序状态字  int io_readFlag();
io_readFlag:
    pushfd
    pop eax
    ret

;设置程序状态字  void io_writeFlag(int value)
io_writeFlag:
    mov edx, [esp+4]
    push eax
    popfd
    ret

具体实现过程如下所示:

1.io.asm 实现基本IO操作函数

;io操作函数定义,给C语言调用
;eax作为返回值

[SECTION .s32]
[BITS 32]

;hlt void io_hlt();
io_hlt:
    hlt
    ret

;读取指定端口8位数据  int io_in8(int port);
io_in8:
    mov edx, [esp+4]
    mov eax, 0
    in al, dx
    ret

;读取指定端口16位数据 int io_in16(int port);
io_in16:
    mov edx, [esp+4]
    mov eax, 0
    in ax, dx
    ret

;读取指定端口32位数据 int io_in32(int port);
io_in32:
    mov edx, [esp+4]
    in  eax, dx
    ret

;向指定端口写入8位数据 void io_out8(int port, int value);
io_out8:
    mov edx, [esp+4]
    mov al, [esp+8]
    out dx, al
    ret

;向指定端口写入16位数据 void io_out16(int port, int value);
io_out16:
    mov edx, [esp+4]
    mov ax, [esp+8]
    out dx, ax
    ret

;向指定端口写入32位数据 void io_out32(int port, int value);
io_out32:
    mov edx, [esp+4]
    mov eax, [esp+8]
    out dx, eax
    ret

;关中断 void io_cli();
io_cli:
    cli
    ret

;开中断  void io_seti();
io_seti:
    sti
    ret

;获取程序状态字  int io_readFlag();
io_readFlag:
    pushfd
    pop eax
    ret

;设置程序状态字  void io_writeFlag(int value)
io_writeFlag:
    mov edx, [esp+4]
    push eax
    popfd
    ret

2.io.h  定义C语言函数声明  与汇编语言io.asm相对应

extern void io_hlt();

extern char io_in8(int port);

extern int io_in16(int port);

extern int io_in32(int port);

extern void io_out8(int port, char value);

extern void io_out16(int port, int value);

extern void io_out32(int port, int value);

extern void io_cli();

extern void io_seti();

extern int io_readFlag();

extern void io_writeFlag(int value);

3. 实现显存显示主函数

#include<stdio.h>
#include "io.h"

void initPallet();

void kernel_main(){
    initPallet();
    for(;;){
        io_hlt();
    }
}

void initPallet(){
    //定义调色板
    static char table_rgb[16*3] = {
        0x00,  0x00,  0x00,
        0xff,  0x00,  0x00,
        0x00,  0xff,  0x00,
        0xff,  0xff,  0x00,
        0x00,  0x00,  0xff,
        0xff,  0x00,  0xff,
        0x00,  0xff,  0xff,
        0xff,  0xff,  0xff,
        0xc6,  0xc6,  0xc6,
        0x84,  0x00,  0x00,
        0x00,  0x84,  0x00,
        0x84,  0x84,  0x00,
        0x00,  0x00,  0x84,
        0x84,  0x00,  0x84,
        0x00,  0x84,  0x84,
        0x84,  0x84,  0x84,
    };
    char *p = table_rgb;
	//读取eflags寄存器值
    int flag = io_readFlag();
	//关中断
    io_cli();
	//写入调色板编号
    io_out8(0x03c8, 0);
	//调色板颜色与坐标索引对应值
    for(int i=0; i<16; i++){
        io_out8(0x0309, p[i]);
        io_out8(0x0309, p[i+1]);
        io_out8(0x0309, p[i+2]);
        p += 3;
    } 
    //将eflags寄存器重新赋值
    io_writeFlag(flag);
    //显存起始地址	
    p = (char *)0xa0000;
	//绘制画面
    for(int i=0; i<=0xFFFF; i=i+2){
        *p = i & 0x0F;
        p++;
    }
    
}

4. kernel.asm相应修改如下

主要包括 包含io.asm  去掉之前io_hlt声明

SEG_CODE32:
    mov ax, SelectorStack
    mov ss, ax
    mov esp, STACK_TOP

    mov ax, SelectorVRAM
    mov ds, ax

    call kernel_main  ;调用c语言函数

;GLOBAL io_hlt      ;声明函数供c语言调用 void io_hlt();
;io_hlt:
;    HLT
;    RET

;注意汇编文件引入位置 在代码段结束符之上
%include "io.asm"
%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.c如下

#include<stdio.h>
#include "io.h"

//定义调色板颜色
#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

//屏幕宽度
#define SCREEN_WIDTH 320

//屏幕高度
#define SCREEN_HEIGHT 200

//调色板初始化
void initPallet();

//绘制简单图形
void draw_simple();

//绘制矩形功能
void draw_rectangle();

//绘制矩形
void fillRect(int x, int y, int width, int height, char colIndex);

//绘制桌面
void draw_desktop();

//C程序入口
void kernel_main(){
    initPallet();
    //draw_simple();
    //draw_rectangle();
    draw_desktop();
    for(;;){
        io_hlt();
    }
}

void initPallet(){
    //定义调色板
    static char table_rgb[16*3] = {
        0x00,  0x00,  0x00,
        0xff,  0x00,  0x00,
        0x00,  0xff,  0x00,
        0xff,  0xff,  0x00,
        0x00,  0x00,  0xff,
        0xff,  0x00,  0xff,
        0x00,  0xff,  0xff,
        0xff,  0xff,  0xff,
        0xc6,  0xc6,  0xc6,
        0x84,  0x00,  0x00,
        0x00,  0x84,  0x00,
        0x84,  0x84,  0x00,
        0x00,  0x00,  0x84,
        0x84,  0x00,  0x84,
        0x00,  0x84,  0x84,
        0x84,  0x84,  0x84,
    };
    char *p = table_rgb;
	//读取eflags寄存器值
    int flag = io_readFlag();
	//关中断
    io_cli();
	//写入调色板编号
    io_out8(0x03c8, 0);
	//调色板颜色与坐标索引对应值
    for(int i=0; i<16; i++){
        io_out8(0x0309, p[i]/4);
        io_out8(0x0309, p[i+1]/4);
        io_out8(0x0309, p[i+2]/4);
        p += 3;
    } 
    //将eflags寄存器重新赋值
    io_writeFlag(flag);
}

void draw_simple(){
    //显存起始地址
    char *p = (char *)0xa0000;
    //绘制画面
    for(int i=0; i<=0xFFFF; i=i+2){
        *p = i & 0x0F;
        p++;
    }
}

void fillRect(int x, int y, int width, int height, char colIndex){
    char *vram = (char *)0xa0000;
    for(int i=y; i<=y+height; i++){
        for(int j=x; j<=x+width; j++){
            vram[i*SCREEN_WIDTH+j] = colIndex;
        }
    }
}

void draw_rectangle(){
    fillRect(10, 30, 100, 100, COL8_FF0000);
    fillRect(50, 80, 100, 100, COL8_00FF00);
    fillRect(100, 100, 100, 100, COL8_0000FF);
}

void draw_desktop(){

    fillRect(0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-29, COL8_008484);
    fillRect(0, SCREEN_HEIGHT-28, SCREEN_WIDTH-1, 28, COL8_848484);

	fillRect(0, SCREEN_HEIGHT-27, SCREEN_WIDTH, 1, COL8_848484);
	fillRect(0, SCREEN_HEIGHT-26, SCREEN_WIDTH, 25, COL8_C6C6C6);
	
	fillRect(3, SCREEN_HEIGHT-24, 56, 1, COL8_FFFFFF);
	fillRect(2, SCREEN_HEIGHT-24, 1, 20, COL8_FFFFFF);

	fillRect(3, SCREEN_HEIGHT-4, 56, 1, COL8_848484);
	fillRect(59, SCREEN_HEIGHT-23, 1, 19, COL8_848484);

	fillRect(2, SCREEN_HEIGHT-3, 57, 0, COL8_000000);
	fillRect(60, SCREEN_HEIGHT-24, 0, 19, COL8_000000);

	fillRect(SCREEN_WIDTH-47, SCREEN_HEIGHT-24, 43, 1, COL8_848484);
	fillRect(SCREEN_WIDTH-47, SCREEN_HEIGHT-23, 0, 19, COL8_848484);

	fillRect(SCREEN_WIDTH-47, SCREEN_HEIGHT-3, 43, 0, COL8_FFFFFF);
	fillRect(SCREEN_WIDTH-3, SCREEN_HEIGHT-24, 0, 21, COL8_FFFFFF);
}

编译,效果如下所示:

  

https://github.com/ChenWenKaiVN/bb_os_core/tree/develop

参考链接:

https://blog.csdn.net/doubleselect/article/details/38727105

https://blog.csdn.net/Zllvincent/article/details/83591890

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

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值