09.调色板绘制系统界面
簡介
上一節(jié)我們使用C語言繪制了簡單的圖形界面,實現(xiàn)了匯編語言和C語言共同開發(fā)操作系統(tǒng),只有當C語言力不能逮,特別是需要操作硬件時,才會使用匯編語言。
我們實現(xiàn)圖像繪制的辦法是,給每一個像素設(shè)定指定的數(shù)值,這個數(shù)值只能位于0-256這個范疇,256種顏色顯然是不夠用的,而且還不論顏色的亮度,飽和度等這些成分呢。為能夠比較好的表示顏色的,一般都使用RGB模式,表示一個RGB顏色需要24位數(shù),便開發(fā)了調(diào)色板系統(tǒng)。
調(diào)色板系統(tǒng)就是把多種RGB顏色的24位數(shù)值放入到一個數(shù)組中,原來的八位數(shù)不再對應(yīng)一個顏色值,而是變成這個數(shù)組的下標,硬件在顯示像素顏色時,從像素對應(yīng)的顯存讀取這個八位數(shù),然后把這個數(shù)當做下標,在RGB顏色素組中找到對應(yīng)的RGB顏色值,再把這個顏色值顯示到對應(yīng)的像素上。
為了簡單起見,我們使用的RGB顏色不對,只有16種,編號0-15,分別為:
0x000000 全黑
0xff0000 亮紅
0x00ff00 亮綠
0xffff00 亮黃
0x0000ff 亮藍
0xff00ff 亮紫
0x00ffff 淺亮
0xffffff 全白
0xc6c6c6 亮灰
0x840000 暗紅
0x008400 暗綠
0x848400 暗黃
0x000084 暗藍
0x840084 暗紫
0x008484 淺暗藍
0x848484 暗灰
要想使顯存系統(tǒng)將顏色的顯示模式轉(zhuǎn)換成調(diào)色板模式,要給硬件發(fā)送命令,只要想發(fā)送命令時,給某個指定的端口寫入特定數(shù)值就可以,硬件接收到命令后,可能會產(chǎn)生一些反饋,這些反饋也會存入某些指定的端口,要想得知反饋,程序只要讀取某些端口的數(shù)據(jù)就可以了。
在匯編語言中,從指定端口讀取數(shù)據(jù)的指令叫in, 假設(shè)我們把要讀取的端口的編號寫入到寄存器dx中,那么指令 in al, dx 就把指定端口的數(shù)據(jù)讀入寄存器al. 如果要想往某個端口中寫入數(shù)據(jù),假設(shè)我們把要寫入的端口編號放入dx, 把要寫入端口的數(shù)據(jù)放入寄存器al, 那么指令 out dx, al就可以把數(shù)據(jù)寫入指定端口了。
目標
1、使用匯編編寫硬件操作函數(shù)供C語言調(diào)用
io.s 匯編文件如下:
2、注意:下面的開發(fā)環(huán)境將切換至Ubuntu環(huán)境下實現(xiàn)(由于筆者使用的是Mac 電腦,在編譯os.c文件反匯編后始終不能繪制出正確的矩形圖案,在百般排查中發(fā)現(xiàn)代碼沒有問題,需要換一個開發(fā)平臺。工具:clang 編譯器、nasm、objconv、VirtualBox。
;io 操作函數(shù)定義,給C語言調(diào)用 ;根據(jù)C語言函數(shù)調(diào)用規(guī)則,eax 寄存器作為返回值,edx 在定義的匯編函數(shù)中沒有保存到棧中;[SECTION .s32] [BITS 32];實現(xiàn)hlt 功能 ;void io_hlt(); io_hlt:hltret;讀取指定端口8位數(shù)據(jù) ;char io_in8(int port); io_in8:mov edx,[esp+4]mov eax,0in al,dxret ;讀取指定端口16位數(shù)據(jù) ;int io_in16(int port); io_in16:mov edx,[esp+4]mov eax,0in ax,dxret ;讀取指定端口32位數(shù)據(jù) ;int io_in32(int port); io_in32:mov edx,[esp+4]in eax,dxret ;指定端口寫入8位數(shù)據(jù) ;void io_out8(int port,char value); io_out8:mov edx,[esp+4]mov al,[esp+8]out dx,alret ;指定端口寫入16位數(shù)據(jù) ;void io_out16(int port,int value); io_out16:mov edx,[esp+4]mov ax,[esp+8]out dx,axret ;指定端口寫入32位數(shù)據(jù) ;void io_out32(int port,int value); io_out32:mov edx,[esp+4]mov eax,[esp+8]out dx,eaxret;關(guān)閉cpu可屏蔽中斷 ;void io_cli(); io_cli:cli ret ;打開cpu可屏蔽中斷 ;void io_seti(); io_seti:sti ret;獲取程序狀態(tài)寄存器值 ;int io_readFlag(); io_readFlag:pushfd pop eax ret ;設(shè)置程序狀態(tài)寄存器值 ;void io_writeFlag(int value); io_writeFlag:mov edx,[esp+4]push eax popfdret為了方便C語言調(diào)用匯編編寫的函數(shù),我們新建一個C語言頭文件存放函數(shù)聲明,方便在*.c 文件中使用匯編函數(shù),io.h 頭文件內(nèi)容如下:
// // io.h // os-test // // Created by vincent on 2018/10/31. // Copyright ? 2018年 vincent. All rights reserved. ////實現(xiàn)hlt 功能 extern void io_hlt();//讀取指定端口8位數(shù)據(jù) extern char io_in8(int port);//讀取指定端口16位數(shù)據(jù) extern int io_in16(int port);//讀取指定端口32位數(shù)據(jù) extern int io_in32(int port);//指定端口寫入8位數(shù)據(jù) extern void io_out8(int port,char value);//指定端口寫入16位數(shù)據(jù) extern void io_out16(int port,int value);//指定端口寫入32位數(shù)據(jù) extern void io_out32(int port,int value);//關(guān)閉cpu可屏蔽中斷 extern void io_cli();//打開cpu可屏蔽中斷--在設(shè)置調(diào)色板時調(diào)用開啟可屏蔽中斷會奔潰 //extern void io_seti();//獲取程序狀態(tài)寄存器值 extern int io_readFlag();//設(shè)置程序狀態(tài)寄存器值 extern void io_writeFlag(int value);2、使用C語言設(shè)置調(diào)色板,os.c 文件如下:
#include<stdio.h> #include "io.h"void initPallet();//操作系統(tǒng)C語言入口函數(shù)--可以指定為其他,kernel.s 匯編代碼call 需要指定調(diào)用 void init_main() {initPallet();for(; ;){io_hlt();}}void initPallet(){//定義調(diào)色板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;int flag = io_readFlag();io_cli();io_out8(0x03c8, 0);for(int i=0;i<16;i++) {io_out8(0x03c9, p[i]);io_out8(0x03c9, p[i+1]);io_out8(0x03c9, p[i+2]);p += 3;}io_writeFlag(flag);p = (char *)0xa0000;for (int i = 0; i <= 0xffff; i++) {*p = i & 0x0f;p++;}}由于調(diào)色板的RGB數(shù)組是我們程序設(shè)定的,因此就必須要把我們設(shè)定的調(diào)色板數(shù)組的數(shù)據(jù)傳遞給硬件顯示系統(tǒng),這樣它才能使用我們設(shè)定的顏色來描繪每一個像素點。把調(diào)色板的數(shù)據(jù)發(fā)送給硬件需要以下操作步驟:
1)關(guān)閉中斷,防止CPU被干擾
2)將調(diào)色板的號碼寫入端口0x03c8, 由于我們現(xiàn)在只有一個調(diào)色板,因此調(diào)色板的編號默認為0,如果要設(shè)置多個調(diào)色板,那么其他調(diào)色板的編號可以一次為1,2…等
3) 將RGB的顏色數(shù)值依次寫入端口0x3c9, 假設(shè)我們要寫入的RGB顏色值是
0x848484 暗灰
那么在C語言中,要分3次調(diào)用io_out8, 例如:
io_out(0x3c9, 0x84);
io_out(0x3c9, 0x84);
io_out(0x3c9, 0x84);
按前面方法編譯、修改相關(guān)文件,虛擬機加載虛擬軟盤文件運行結(jié)果如下:
至此,我們的調(diào)色板設(shè)置完成!
3、有了調(diào)色板功能我們再繪制其他圖形看看效果,下面的C語言代碼將在屏幕上顯示幾個矩形,修改os.c 文件如下:
// !compile method // clang -m32 -c os.c -o os.o // objconv -fnasm os.o -o os.s#include "io.h"//定義調(diào)色板顏色 #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 320void initPallet();/***繪制矩形*x 矩形左上角x坐標*y 矩形左上角y坐標*width 寬度*height 高度*colIndex pallet_color 類型調(diào)色板顏色索引,即矩形顏色*/void fillRect(int x,int y,int width,int height,char colIndex);//操作系統(tǒng)C語言入口函數(shù)--可以指定為其他 void init_main() {initPallet();fillRect(10, 30, 100,100, COL8_FF0000);fillRect(50, 80, 100, 100, COL8_00FF00);fillRect(100, 110, 100, 100, COL8_0000FF);for(; ;){io_hlt();}}void initPallet(){//定義調(diào)色板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 *rgb = table_rgb;int flag = io_readFlag();io_cli();io_out8(0x03c8, 0);for(int i=0;i<16;i++){io_out8(0x03c9,rgb[0]);io_out8(0x03c9,rgb[1]);io_out8(0x03c9,rgb[2]);rgb += 3;}io_writeFlag(flag); }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;}} }將上面的代碼編譯、反編譯成匯編,去掉相應(yīng)多余的匯編語句,編譯出kernel.bat 后加載執(zhí)行后效果如下:
使用調(diào)色板功能實現(xiàn)矩形的繪制成功完成!
4、繪制桌面背景
修改os.c 文件如下繪制桌面背景:
加載虛擬軟盤文件運行如下:
至此繪制的背景桌面已經(jīng)基本出來了!
總結(jié)
以上是生活随笔為你收集整理的09.调色板绘制系统界面的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 汽车通信协议:一文搞懂Flexray通信
- 下一篇: 如何在html中制作个人简历表单