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