首先下載兩個工具DJGPP的gcc及ld:
DJGPP的安裝有點麻煩,首先來這兒下載檔案,我下載了下面幾個:
- djdev203.zip - DJGPP Basic Development Kit
- bnu219b.zip - Basic assembler, linker
- gcc432b.zip - Basic GCC compiler
- mak3791b.zip - Make (processes makefiles)
解壓之後,測試執行失敗,查找原因,好像是因為DJGPP的Windows version暫時不支援長檔案名稱,只能用8.3的format,而且用cmd的dos prompt也有問題,只能用commmand了。做了一個設定environment的.bat:
---------- initc.bat ----------
@echo off
set PATH=d:\os\tools\djgpp\bin;%PATH%
set DJGPP=d:\os\tools\djgpp\djgpp.env
d:
cd d:\os
command
---------- initc.bat ----------
@echo off
set PATH=d:\os\tools\djgpp\bin;%PATH%
set DJGPP=d:\os\tools\djgpp\djgpp.env
d:
cd d:\os
command
---------- initc.bat ----------
寫好了code,可是搞了很久也搞不定。
拿上回的kernel.asm來試,把頭頂的[BITS 16] -> [BITS 32],編譯,執行... 行不了啊! 這一下我想通了,因為我沒有把OS轉為保護模式(Protected Mode, 32bits),而DJGPP的output,卻一定是32bits的,所以怎麼樣也行不了。
之後嘗試過下載Open Watcom,因為她可以output 16bits,不過參數實在太複雜,有關的網上文章又不多。考慮了一下,之前因為不想太複雜所以沒有轉去保護模式,現在不轉的話反而更加麻煩,而且遲早也要轉到保護模式的,所以決定現在就先轉去了。(最底限度先把必要的轉過去)
開動保護模式,是由Boot Loader負責的,下面的code主要加入GDT,set CR0等,因為我也不是完全了解,不想亂解釋,有興趣的請看這 ->
http://osdever.net/tutorials/brunmar/tutorial_02.php
http://osdever.net/tutorials/brunmar/tutorial_03.php
---------- loader.asm ----------
[BITS 16] ;tell the assembler that its a 16 bit code
[ORG 0x7C00] ;Origin, tell the assembler that where the code will
;be in memory after it is been loaded
reset_drive:
mov ah, 0 ;not very sure why i need this ^^"
int 13h
or ah, ah
jnz reset_drive
mov ax, 0
mov es, ax
mov bx, 0x1000 ;copy the kernel to 0000:1000
mov ah, 02h ;Command - 02h for 'Read sector from disk'
mov al, 02h ;Number of sectors to read
mov ch, 0 ;Disk cylinder
mov cl, 02h ;Disk sector (starts with 1, not 0)
mov dh, 0 ;Disk head
int 13h
or ah, ah
jnz reset_drive ;any problem, back to reset_drive label
cli ;to protect mode
xor ax, ax ;clear DS
mov ds, ax
lgdt [gdt_desc] ;execute LGDT instruction
mov eax, cr0 ;set CR0
or eax, 1
mov cr0, eax
jmp 08h:clear_pipe
[BITS 32]
clear_pipe:
mov ax, 10h
mov ds, ax
mov ss, ax
mov esp, 090000h ;set up stack
jmp 08h:01000h ;jump to start of the kernel
gdt: ;making Global Description Table
gdt_null: ;Null Segment
dq 0 ;all 64bits 0
gdt_code: ;Code Segment
dw 0FFFFh ;16 bits
dw 0 ;16 bits
db 0 ;8 bits
db 10011010b ;8 bits
db 11001111b ;8 bits
db 0 ;8 bits
gdt_data: ;Data Segment
dw 0FFFFh
dw 0
db 0
db 10010010b
db 11001111b
db 0
gdt_end:
gdt_desc:
dw gdt_end - gdt - 1
dd gdt
TIMES 510 - ($ - $$) db 0 ;fill the rest of sector with 0
DW 0xAA55 ; add boot signature at the end of bootloader
---------- loader.asm ----------
[BITS 16] ;tell the assembler that its a 16 bit code
[ORG 0x7C00] ;Origin, tell the assembler that where the code will
;be in memory after it is been loaded
reset_drive:
mov ah, 0 ;not very sure why i need this ^^"
int 13h
or ah, ah
jnz reset_drive
mov ax, 0
mov es, ax
mov bx, 0x1000 ;copy the kernel to 0000:1000
mov ah, 02h ;Command - 02h for 'Read sector from disk'
mov al, 02h ;Number of sectors to read
mov ch, 0 ;Disk cylinder
mov cl, 02h ;Disk sector (starts with 1, not 0)
mov dh, 0 ;Disk head
int 13h
or ah, ah
jnz reset_drive ;any problem, back to reset_drive label
cli ;to protect mode
xor ax, ax ;clear DS
mov ds, ax
lgdt [gdt_desc] ;execute LGDT instruction
mov eax, cr0 ;set CR0
or eax, 1
mov cr0, eax
jmp 08h:clear_pipe
[BITS 32]
clear_pipe:
mov ax, 10h
mov ds, ax
mov ss, ax
mov esp, 090000h ;set up stack
jmp 08h:01000h ;jump to start of the kernel
gdt: ;making Global Description Table
gdt_null: ;Null Segment
dq 0 ;all 64bits 0
gdt_code: ;Code Segment
dw 0FFFFh ;16 bits
dw 0 ;16 bits
db 0 ;8 bits
db 10011010b ;8 bits
db 11001111b ;8 bits
db 0 ;8 bits
gdt_data: ;Data Segment
dw 0FFFFh
dw 0
db 0
db 10010010b
db 11001111b
db 0
gdt_end:
gdt_desc:
dw gdt_end - gdt - 1
dd gdt
TIMES 510 - ($ - $$) db 0 ;fill the rest of sector with 0
DW 0xAA55 ; add boot signature at the end of bootloader
---------- loader.asm ----------
跟著是一個用C寫的Hello World Kernel,這個Kernel會清空畫面,打出Kernel Loaded by C.字樣:
---------- kernel.c ----------
#define WHITE_TXT 0x07 // white on black text
void k_clear_screen();
void k_printf();
int main(void)
{
k_clear_screen();
k_printf();
for (;;);
}
void k_clear_screen() // clear the entire text screen
{
char *vidmem = (char *) 0xb8000;
unsigned int i=0;
while(i < (80*25*2))
{
vidmem[i]=' ';
i++;
vidmem[i]=WHITE_TXT;
i++;
}
};
void k_printf()
{
char *vidmem = (char *) 0xb8000;
unsigned int i=0;
char *message = "Kernel Loaded by C.";
unsigned int line = 1;
i=(line*80*2);
while(*message!=0)
{
if(*message=='\n') // check for a new line
{
line++;
i=(line*80*2);
*message++;
} else {
vidmem[i]=*message;
*message++;
i++;
vidmem[i]=WHITE_TXT;
i++;
}
}
};
---------- loader.asm ----------
#define WHITE_TXT 0x07 // white on black text
void k_clear_screen();
void k_printf();
int main(void)
{
k_clear_screen();
k_printf();
for (;;);
}
void k_clear_screen() // clear the entire text screen
{
char *vidmem = (char *) 0xb8000;
unsigned int i=0;
while(i < (80*25*2))
{
vidmem[i]=' ';
i++;
vidmem[i]=WHITE_TXT;
i++;
}
};
void k_printf()
{
char *vidmem = (char *) 0xb8000;
unsigned int i=0;
char *message = "Kernel Loaded by C.";
unsigned int line = 1;
i=(line*80*2);
while(*message!=0)
{
if(*message=='\n') // check for a new line
{
line++;
i=(line*80*2);
*message++;
} else {
vidmem[i]=*message;
*message++;
i++;
vidmem[i]=WHITE_TXT;
i++;
}
}
};
---------- loader.asm ----------
之前的print_f是這樣的 -> k_printf(char *message, unsigned int line),沒法運行,之後把parameters除掉,卻能執行。原因未找到,可能是Stack的問題,要再研究。
不知道什麼問題,那個initc.bat搞出來,DJGPP環境的command prompt異常難用,完全沒辦法重複之前的輸入,連copy and paste也不能,只好寫一個.bat來執行編譯指令 (其實用makefile應該比較好的,Anyway ^^)
---------- c.bat ----------
nasm loader.asm -f bin -o loader.bin
gcc -ffreestanding -c kernel.c -o kernel_b4link.o
ld -e _main -Ttext 0x1000 -o kernel.o kernel_b4link.o
objcopy -R .note -R .comment -S -O binary kernel.o kernel.bin
copy /b /y loader.bin+kernel.bin boot.img
---------- c.bat ----------
nasm loader.asm -f bin -o loader.bin
gcc -ffreestanding -c kernel.c -o kernel_b4link.o
ld -e _main -Ttext 0x1000 -o kernel.o kernel_b4link.o
objcopy -R .note -R .comment -S -O binary kernel.o kernel.bin
copy /b /y loader.bin+kernel.bin boot.img
---------- c.bat ----------
nasm編譯Boot Loader。
gcc編譯用C寫的Kernel,-ffreestanding參數指明可能沒有standard library,也可以不是由main function開始執行。
ld把開始的function定為main,並把Text section的絕對位置定在0x1000 (Kernel在記憶體內的位置)
objcopy,我是第一次用的,不太確定為什麼需要,不過她會把Note,Comment,Relocation及Symbol清除。
copy把loader及kernel放在一起。
跑到d:\os\,執行initc.bat,在跳出來的視窗執行c.bat,開動Bochs,測試成功!
5 則留言:
請問,骨仔大大這些電腦知識都是從哪裡學得的呢?
珍妮佛麥走
書本,網上,工作的經驗等等... 每天都學到一點,時間一長,就變成現在的樣子了 ^^
自修就可以這麼厲害@.@~
珍妮佛麥走
這個~ 比起未學過的人我當然是知道多一點,可是比照箇中高手,我只是路旁的小石子而已~
對初學者來講C語言並不容易上手,由此可知,骨仔大大不是初學者,必須不停的使用同一個CGI 程式碼,如果編譯器不能正確辨認句子上的謬誤,除錯的工作將會變得更困難,所以骨仔大大是明知山有虎,偏向虎山行,加油!
珍妮佛麥走
發佈留言