2016-09-14 66 views
2

嗯,這顯然是一個初學者的問題,但這是我第一次嘗試製作操作系統在C(其實,我幾乎是全新的C ..我習慣了asm)所以,爲什麼這是無效的?據我所知,在Cpointer只是一個uint16_t用於指向內存中的某個區域,權利(或uint32_t,這就是爲什麼它不工作)? 我做了以下內核(「我已經做了一個引導程序和所有組件加載生成KERNEL.BIN文件):GCC裸機內聯彙編SI寄存器不能很好地與指針

kernel.c

void printf(char *str) 
{ 
__asm__(
"mov si, %0\n" 
"pusha\n" 
"mov ah, 0x0E\n" 
".repeat:\n" 
"lodsb\n" 
"cmp al, 0\n" 
"je .done\n" 
"int 0x10\n" 
"jmp .repeat\n" 
".done:\n" 
"popa\n" 
: 
: "r" (str) 
); 
return; 
} 

int main() 
{ 
char *msg = "Hello, world!"; 
printf(msg); 
__asm__("jmp $"); 
return 0; 
} 

我用下面的命令編譯它kernel.c

gcc kernel.c -ffreestanding -m32 -std=c99 -g -O0 -masm=intel -o kernel.bin

返回以下錯誤:

kernel.c:3: Error: operand type mismatch for 'mov'

爲什麼可能是這個錯誤的原因?

+3

會給你一個建議。如果你願意的話,你可以大便便利,但如果你對這類事情不熟悉,最糟糕的事情就是做內聯彙編程序。很容易弄錯,你需要適當的輸入,輸出和clobbers來覆蓋所有變化的東西。如果你想用匯編寫入,我建議把這些函數寫在一個單獨的彙編文件中,而不要使用_C_代碼。 –

+1

如果您使用內聯彙編程序,請謹慎使用它。只有在必要時才使用它,並讓_C_代碼爲您完成工作(包括循環等)。 –

+4

即使是經驗豐富的嘗試GCC擴展內聯彙編程序的_C_開發人員,也可以創建似乎能夠工作的代碼,然後在_C_優化程序縮減所有內容時,在未來的某個時間點不會採取應有的方式。如果你不知道自己在做什麼,那麼等待發生的負擔就會很大。 –

回答

2

正如Michael Petch已經解釋的那樣,您只能使用內聯彙編來完成C語言無法完成的絕對最小代碼。其餘部分有內聯彙編,但您必須非常小心地設置約束和clobber列表對。

讓GCC總是把值傳遞給正確的寄存器,並指定值應該在哪個寄存器中。

對於你的問題,你可能想要做這樣的事情

#include <stdint.h> 

void print(const char *str) 
{ 
    for (; *str; str++) { 
    __asm__ __volatile__("int $0x10" : : "a" ((int16_t)((0x0E << 8) + *str)), "b" ((int16_t)0) :); 
    } 
} 

編輯:您的組件具有您試圖傳遞一個指針在16位寄存器中的問題。這不適用於32位代碼,因爲32位也是指針大小。 如果您想要生成16位實模式代碼,則有-m16選項。但是這並不能使GCC成爲真正的16位編譯器,它有其侷限性。本質上,它在代碼中發出.code16gcc指令。

+0

嗯,奇怪的是,似乎沒有AL和AH單獨的限制。對於具有RH子寄存器(A,C,D或B)的寄存器有一個「Q」限制,但對特定的H寄存器沒有限制。很好的解決方法。不過,我可能使用'|'而不是'+'。 –

+0

另外,如果你至少總結了這些註釋,並且指出當你用'-m32'編譯生成32位代碼時,指針實際上是32位的,就像OP正在做的那樣,問題的關鍵部分。 –

+0

@PeterCordes:的確,我覺得有一個約束啊。但是由於那個是特殊的(與從整數大小自動選擇的EAX,AX或AL相比),它使我認爲稍微複雜一些。這就是我使用C方法的原因。 –

0

不能簡單地在32位指針上使用16位彙編指令,並期望它工作。 siesi寄存器(即32位)的低16位。

gcc -m32和-m16都使用32位指針。 -m16只是使用地址大小和操作數大小前綴來完成與普通-m32模式大致相同的操作,但是以實模式運行。

如果您嘗試在32位應用程序中使用16位尋址,則會丟棄指針的大部分,並且只需轉到不同的地方

試着讀一本關於intel 32位尋址模式的書,並且保護模式,你會發現很多事情在這種模式下是不同的。

(如果你嘗試切換到64位模式下,你會看到這一切再次發生變化)

引導加載器的東西通常不同,CPU復位迫使16位實模式開始CPU。這與32位保護模式完全不同,這是操作系統所做的第一件事情之一。引導加載程序工作在16位模式,在那裏,指針是16位寬(當然,不是,20位寬,當適當的段寄存器附加到地址)

+0

操作數大小和地址大小前綴讓16位代碼使用32位寄存器,包括32位尋址模式。 'gcc -m16'這樣做,仍然使用32位地址和整數。這是超級醜陋,臃腫,但它是技術上有效的16位代碼,將在386及更高版本上運行。如果您正確設置了段寄存器,它甚至可以用於某些事情。不過,我同意普遍的看法,認爲這似乎不是一個好主意。 –

+0

@PeterCordes,是的,但這是32位地址的一部分。如果你嘗試引用'SI'寄存器(而不是'ESI')並強制一個16位地址,彙編器可以放16位前綴。在普通的彙編程序應用程序編程中,您將缺省行爲放在程序頭文件中(在彙編了一些尋址模式指令後)。如果不這樣做,您將獲得32位保護模式,32位數據和32位默認地址,如果您一個64位的平臺,你會得到32位數據和64位地址作爲默認的linux和freebsd(至少)。不瞭解Windows應用程序環境。 –

+0

單指令操作數大小的前綴在保護模式和實模式之間不會改變。它們只是改變操作數大小:頁面保持啓用(或禁用),並且段寄存器保持它們的含義(作爲GDT/LDT中的索引,或者作爲移位4個添加來創建線性地址)。 –