博學之、審問之、慎思之、明辨之、篤行之。 |
先把 cs, ds, es, ss 指到同一個值, 再把 sp 暫存器設定好完成 stack 設定就可以順利完成 x86 16 bit mode 的 c runtime, 這裡都要用上組合語言, 而從這之後, 就可以用 c 語言了, 開心吧, bss 初始化就用 c 來撰寫了, 當然也可以開始用 c++ 了。再把 read/write bios call 加入就擁有了 input/output 的能力, x86 自然是從鍵盤 input, output 到螢幕上。這樣就完成整個移植了, 說起來很簡單, 而過程就很刺激, 遇到不少問題, 紀錄如下:
- x86/start.s bios_print_char
這是用組合語言寫的 function, 讓 c++ call, 用途是在螢幕上印出一個字元。return 時必須要用 retl 而不是 ret, 差個 l 差很多。我本來打算用 inline assembly 寫這個 function 的, 不過寫不出來, 只好用純組合語言寫, inline assembly 可參考《Writing a 16-bit dummy kernel in C/C++》。
retl 會讓 sp + 4
ret 會讓 sp + 2
+4 才是我要的值, 這樣 stack 才不會亂掉。
- 在這個組合語言寫的 function 要保存 ebx/eax register, 否則以下程式碼
char cc='A'; cout << ": " << cc << endl;
會錯誤。
原因就是沒保存 ebx/eax, 我很辛苦的從組合語言去除錯, 找到問題後, 覺得自己還蠻厲害的。這段程式碼有用到 ebx, 而印出到螢幕的程式碼也用到 ebx, 呼叫印出到螢幕的程式碼後, ebx 就不是原來的值了, 所以造成錯誤。
- 縮小 heap 空間, 因為整個執行環境的記憶體只有 64 k (明明有 640k 記憶體可以用, 你有點疑惑吧! 參考: 深入认识 Turbo C 编译器 ), 包含執行檔案的大小, 若執行檔佔了 10k, 我只剩下 56 k 可用, 包含 bss, stack。這已經比 stm32f4discovyer 的 192k ram 還小了。
- 另外是 bss type 的問題, 紀錄在 linker script symbol type R_386_16, R_386_32
有兩個版本, 一個是透過 boot loader 載入, 一個是 dos 下的 .com 檔案。
完成後我把 simple scheme 也移植過來, 畢竟已經有了 dos 平台的標準程式庫, 照裡來說應該要很容易, 不過只能使用 64k 的 ram, 實在太小, 我又很辛苦的縮到 64k, 才算移植成功, 參考以下影片:
64k 實在大小, 該怎麼辦? 有一個方法是使用 big real mode, 另外一個是使用不同的節區, 我不打算搞 big real mode 這樣的方法, 來試試看不同節區的方法吧!
把 es, ds, ss 指到另外的地方, 可以再增加 64k, 所以把 start.s (這是 x86 simple c++ library 程式進入點) 改成以下的樣子:
mov $0x8000,%ax mov %ax,%ds mov %ax,%es mov %ax, %ss
這樣就可以了嗎? 當然沒那麼簡單, 這樣是會出事的。還要把 data section 複製到 0x8000 這個 segment, ex:
0x9000:data_section_addr_begin ~ 0x9000:data_section_addr_end -> 0x8000:data_section_addr_begin ~ 0x8000:data_section_addr_end
init_data_asm 就是在做這樣的事情。為什麼我會知道呢? 因為我把反組譯的程式碼看過後, 發現補上這樣的行為, 就可以符合 c 語言的正確行為, 否則在函式參數傳遞會有問題, 可能會在傳遞指標時發生問題。
再來是 ctor 的位址我在 linker script 這邊沒處理好, x86_16.ld.error L37 ~ 39 會抓到錯誤的 call_ctor address, x86_16.ld 的版本才是對的。
因為 align 的問題, ex:
00 01 02 00
本來應該要抓到 01 02, 結果抓到 00 01,
調整 linker script alignment, 就變成
01 02 00 00
就可以正確抓到 01 02。
你一定好奇我怎麼找到這問題, 使用 bochs 內建除錯器, 慢慢和組合語言搏鬥, dump 記憶體位址來觀察, 很辛苦的。
那要怎麼像 turbo c 做到 huge mode 的記憶體模式呢? 這我就不知道了, 我不想再去研究過時的東西了。
git url:
https://github.com/descent/simple_stdcpplib
branch: x86_16_support_diff_segment
考古一下, 在 ms dos real mode 上, sizeof int 是 2byte, 有玩過 dos 程式設計的人應該都知道, 不過一定都是這樣嗎?
以下 fig1, fig2 是在 dos real mode 測試 borland c++ 和 g++ sizeof int 的結果。
fig 1 bc 31 sizeof int: 2 |
fig 2 g++ sizeof int: 4 |
borland c 是 dos 下開發的霸主, 很好用, gcc 比較少人使用, c 的 type 不只和 os 有關, 就算在同一個 os 下, 不同編譯器也有著不同的結果。
ref:
深入认识Turbo C编译器
沒有留言:
張貼留言
使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。
我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。