本节实现调色板功能,之前开启显存的图形模式,对于每一个像素点利用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 暗灰
显存调色板模式的设置方式:
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
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
#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
#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