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 彙編檔案如下:
;io 操作函式定義,給C語言呼叫 ;根據C語言函式呼叫規則,eax 暫存器作為返回值,edx 在定義的彙編函式中沒有儲存到棧中 ; [SECTION .s32] [BITS 32] ;global _io_hlt ;global _io_in8 ;global _io_in16 ;global _io_in32 ;global _io_out8 ;global _io_out16 ;global _io_out32 ;global _io_cli ;global _io_seti ;global _io_readFlag ;global _io_writeFlag ;實現hlt 功能 ;void io_hlt(); _io_hlt: hlt ret ;讀取指定埠8位資料 ;char 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,char 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 ;關閉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 popfd ret
為了方便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, 14);
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);
按前面方法編譯、修改相關檔案,虛擬機器載入虛擬軟盤檔案執行結果如下:
至此,我們的調色盤設定完成!