C++Builder (BCB) 移除了傳統 Turbo C/Borland C++ 中的 inportb/outportb 等直接 I/O 函式,開發者在需要控制並行埠、序列埠或自訂硬體卡時,必須自行實作底層存取。本文詳細解析兩種主流做法:內嵌組合語言 與 __emit__ 機器碼嵌入,並提供可直接套用的封裝函式與範例專案。
BCB 使用 asm { ... } 區塊包裹組語指令,編譯器會將其直接轉換為機器碼嵌入目的檔,無需外部組譯器。
OutPortvoid OutPort(unsigned short port, unsigned char value) {
asm {
mov dx, port ; 端口位址 → DX
mov al, value ; 8-bit 資料 → AL
out dx, al ; AL → [DX] 端口
}
}
InPortunsigned char InPort(unsigned short port) {
unsigned char value;
asm {
mov dx, port ; 端口位址 → DX
in al, dx ; [DX] 端口 → AL
mov value, al ; AL → 區域變數
}
return value;
}
優點:可讀性高、易除錯、支援標註註解。
缺點:僅限 32 位元編譯器;64 位元 BCB 不支援內嵌組語。
__emit__ 機器碼直接嵌入__emit__ 為 BCB 專屬內建函式,接受一連串機器碼位元組,編譯時直接寫入目的碼,完全繞過組譯器。需熟悉 x86 指令編碼,稍有錯誤即導致當機。
| 指令 | 機器碼 | 說明 |
|---|---|---|
| MOV EDX, [port] | 8B 95 [offset] | 將區域變數 port 位址載入 EDX |
| MOV AL, [value] | 8A 85 [offset] | 將區域變數 value 載入 AL |
| OUT DX, AL | 66 EE | 16-bit 端口輸出 (66h 前綴覆蓋操作數大小) |
| IN AL, DX | 66 EC | 16-bit 端口輸入 |
| MOV [value], AL | 88 85 [offset] | AL 存回區域變數 |
void OutPort(unsigned short port, unsigned char value) {
__emit__(0x8B, 0x95, &port); // MOV EDX, port
__emit__(0x8A, 0x85, &value); // MOV AL, value
__emit__(0x66, 0xEE); // OUT DX, AL
}
unsigned char InPort(unsigned short port) {
unsigned char value;
__emit__(0x8B, 0x95, &port); // MOV EDX, port
__emit__(0x66, 0xEC); // IN AL, DX
__emit__(0x88, 0x85, &value); // MOV value, AL
return value;
}
優點:理論上可用於 64 位元編譯器 (需調整指令編碼)。
缺點:極難維護、無型別檢查、錯誤機器碼會導致系統崩潰。
建立 VCL Forms 專案,放置兩個 TButton,分別命名 btnWrite、btnRead,Caption 設為「寫端口」/「讀端口」。於表單 .h 宣告兩個 public 方法,.cpp 實作事件處理:
void __fastcall TForm1::btnWriteClick(TObject *Sender) {
OutPort(0x378, 0x00); // 資料埠輸出 0
OutPort(0x379, 0x00); // 狀態埠清除
}
void __fastcall TForm1::btnReadClick(TObject *Sender) {
int val = InPort(0x37A); // 讀取控制埠
ShowMessage("讀取值: " + IntToHex(val, 2));
}
編譯執行後,若接上並行埠 LED 測試卡,即可觀察輸出效果。若需切換為 __emit__ 版本,只需替換表單類別中的兩個函式實作即可,呼叫端完全不變。
inpout32.sys、WinRing0 等第三方驅動封裝。__emit__ (需重新計算 64-bit 指令編碼) 或改用驅動程式介面。SetupAPI + DeviceIoControl 流程。內嵌組語與 __emit__ 均能在 BCB 32 位元環境下實現 I/O 端口存取,前者適合快速原型與教學,後者適合極度精簡或需避開組譯器限制的場合。實務上建議封裝為獨立單元 (PortIO.cpp/h),並以條件編譯切換實作,隔離平台差異。