自制作業系統(6)C語言繪製介面
阿新 • • 發佈:2020-11-14
利用C語言繪製圖像介面
首先說明一個BIOS呼叫:VGA顯示卡色彩功能
mov al,0x13h
mov ah,0x00
int 0x10
al值的解釋:
0x03表示16色字元模式
0x12表示VGA圖形模式,640 * 480 * 4位彩色模式
0x13表示VGA圖形模式,320 * 200 * 8位彩色模式,調色盤模式
0x6a表示擴充套件VGA圖形模式,800 * 600 * 4彩色模式
我們使用的是0x13,最後的8表示色彩值是8位,也就是256種
系統視訊記憶體的地址是0x000a0000,往視訊記憶體裡面寫入資料螢幕就會發生變化
修改核心程式碼:
kernel.asm
%include "pm.inc" org 0x9000 VRAM_ADDRESS equ 0x000a0000 ;定義視訊記憶體開始地址 jmp LABEL_BEGIN [SECTION .gdt] ; 段基址 段界限 屬性 LABEL_GDT: Descriptor 0, 0, 0 LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_C + DA_32 LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW LABEL_DESC_VRAM: Descriptor 0, 0ffffffffh, DA_DRW ;4G記憶體變成可讀寫的 LABEL_DESC_STACK: Descriptor 0, TopOfStack, DA_DRWA+DA_32 ;為C語言設定的棧 GdtLen equ $ - LABEL_GDT GdtPtr dw GdtLen - 1 dd 0 SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT SelectorStack equ LABEL_DESC_STACK - LABEL_GDT SelectorVram equ LABEL_DESC_VRAM - LABEL_GDT [SECTION .s16] [BITS 16] LABEL_BEGIN: mov ax, cs mov ds, ax mov es, ax mov ss, ax mov sp, 0100h mov al, 0x13 ;VGA顯示卡呼叫 mov ah, 0 int 0x10 xor eax, eax mov ax, cs shl eax, 4 add eax, LABEL_SEG_CODE32 mov word [LABEL_DESC_CODE32 + 2], ax shr eax, 16 mov byte [LABEL_DESC_CODE32 + 4], al mov byte [LABEL_DESC_CODE32 + 7], ah xor eax, eax ;為C語言設定堆疊 mov ax, cs shl eax, 4 add eax, LABEL_STACK mov word [LABEL_DESC_STACK + 2], ax shr eax, 16 mov byte [LABEL_DESC_STACK + 4], al mov byte [LABEL_DESC_STACK + 7], ah xor eax, eax mov ax, ds shl eax, 4 add eax, LABEL_GDT mov dword [GdtPtr + 2], eax lgdt [GdtPtr] cli in al, 92h or al, 00000010b out 92h, al mov eax, cr0 or eax , 1 mov cr0, eax jmp dword SelectorCode32: 0 [SECTION .s32] [BITS 32] LABEL_SEG_CODE32: ;調到這一行就進入了保護模式 mov ax, SelectorStack ;初始化堆疊 mov ss, ax mov esp, TopOfStack mov ax, SelectorVram mov ds, ax C_CODE_ENTRY: %include "write_vga.asm" ;引入C語言的反彙編程式碼 io_hlt: ;進入休眠 HLT RET SegCode32Len equ $ - LABEL_SEG_CODE32 [SECTION .gs] ALIGN 32 [BITS 32] LABEL_STACK: times 512 db 0 ;初始化512位元組都為0 TopOfStack equ $ - LABEL_STACK
相應地修改pm.inc檔案:
pm.inc
%macro Descriptor 3 dw %2 & 0FFFFh dw %1 & 0FFFFh db (%1>>16) & 0FFh dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) db (%1 >> 24) & 0FFh %endmacro DA_32 EQU 4000h ;32位段 DA_C EQU 98h ;存在的只執行程式碼段屬性值 DA_DRW EQU 92h ;存在的可讀寫資料段屬性值 DA_DRWA EQU 93h ;存在的已訪問可讀寫資料段型別值
由於上面初始化的堆疊是512位元組,所以C語言的區域性變數最大是512位元組
這種寫法就會出錯:
void fun(){
char buf[513]
}
這種溢位會被黑客劫持利用
看一下C語言程式碼:
write_vga.c
void CMain(void) { int i; char*p = 0; // 視訊記憶體的起始地址到結束地址 for (i = 0xa0000; i <= 0xaffff; i++) { p = i; // 隨意構造一個數值進行寫入 *p = i & 0x0f; } for(;;) { // 調用匯編裡面的程式碼進入死迴圈 io_hlt(); } }
反編譯C語言得到write_vga.asm
gcc -m32 -fno-asynchronous-unwind-tables -s -c -o write_vga.o write_vga.c
./objconv -fnasm ../write_vga.o -o ../write_vga.asm
最終的write_vga.asm需要修改一點:
;global CMain: function
;extern io_hlt ; near
;SECTION .text align=1 execute ; section number 1, code
CMain: ; Function begin
push ebp ; 0000 _ 55
mov ebp, esp ; 0001 _ 89. E5
sub esp, 24 ; 0003 _ 83. EC, 18
mov dword [ebp-10H], 0 ; 0006 _ C7. 45, F0, 00000000
mov dword [ebp-0CH], 655360 ; 000D _ C7. 45, F4, 000A0000
jmp ?_002 ; 0014 _ EB, 17
?_001: mov eax, dword [ebp-0CH] ; 0016 _ 8B. 45, F4
mov dword [ebp-10H], eax ; 0019 _ 89. 45, F0
mov eax, dword [ebp-0CH] ; 001C _ 8B. 45, F4
and eax, 0FH ; 001F _ 83. E0, 0F
mov edx, eax ; 0022 _ 89. C2
mov eax, dword [ebp-10H] ; 0024 _ 8B. 45, F0
mov byte [eax], dl ; 0027 _ 88. 10
add dword [ebp-0CH], 1 ; 0029 _ 83. 45, F4, 01
?_002: cmp dword [ebp-0CH], 720895 ; 002D _ 81. 7D, F4, 000AFFFF
jle ?_001 ; 0034 _ 7E, E0
?_003: call io_hlt ; 0036 _ E8, FFFFFFFC(rel)
jmp ?_003 ; 003B _ EB, F9
; CMain End of function
;SECTION .data align=1 noexecute ; section number 2, data
;SECTION .bss align=1 noexecute ; section number 3, bss
然後編譯核心程式碼kernel.asm
nasm kernel.asm -o kernel.bat
nasm boot.asm -o boot.bat
Java工具類的OperatingSystem.java需要修改一部分,為了顯示出使用了幾個扇區:
while (in.read(buf) > 0) {
floppyDisk.writeFloppy(Floppy.MAGNETIC_HEAD.MAGNETIC_HEAD_0, cylinder, beginSec, buf);
System.out.println("Load file " + fileName + " to floppy with cylinder: " + cylinder + " and sector:" + beginSec);
beginSec++;
if (beginSec > MAX_SECTOR_NUM) {
beginSec = 1;
cylinder++;
}
}
使用Java工具類生成system.img檔案,發現列印如下:
Load file boot.bat to floppy with cylinder: 0 and sector:1
Load file kernel.bat to floppy with cylinder: 1 and sector:2
Load file kernel.bat to floppy with cylinder: 1 and sector:3
說明kernel.bat檔案寫了柱面1的兩個扇區
於是修改boot.asm的部分程式碼:
boot.asm
org 0x7c00
LOAD_ADDR EQU 0X9000 ;這裡修改下
entry:
mov ax, 0
mov ss, ax
mov ds, ax
mov es, ax
mov si, ax
readFloppy:
mov CH, 1
mov DH, 0
mov CL, 2
mov BX, LOAD_ADDR
mov AH, 0x02
mov AL, 2 ;al=2表示讀取兩個扇區
mov DL, 0
INT 0x13
JC fin
jmp LOAD_ADDR
fin:
HLT
jmp fin
再次執行命令並生成system.img
VirtualBox成功啟動並且顯示了圖形介面