首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 組合語言

GETSEG:從 C 語言呼叫組合語言取得段暫存器值

     編輯:組合語言

前言:混合語言程式設計的橋樑 — 取得執行期段位址

在 16 位元 C 語言 (Small/Medium Model) 中,程式碼與資料分散於不同段 (CS, DS, ES, SS)。有時需要在 C 代碼中獲取這些段值 (如建立遠端指標、呼叫 DOS 中斷需段:偏移、除錯顯示)。GETSEG.ASM 提供一個 near 函式 getseg,供 C 以 extern unsigned getseg(unsigned *, ...) 呼叫,將 CS, DS, ES, SS, SI, DI, SP, FLAGS 寫入呼叫端提供的緩衝區。程式展示了組合語言存取呼叫端堆疊參數、操作段暫存器、處理 C 呼叫慣例的標準技巧。

呼叫慣例與參數佈局

C 宣告:

void getseg(unsigned *cs, unsigned *ds, unsigned *es, unsigned *ss,
            unsigned *si, unsigned *di, unsigned *sp, unsigned *flags);

Small Model 下,參數以 **近指標** (16 位元偏移) 經堆疊 **右至左** 推入:

BP+4   : &cs
BP+6   : &ds
BP+8   : &es
BP+10  : &ss
BP+12  : &si
BP+14  : &di
BP+16  : &sp
BP+18  : &flags

函式需保存 BP, SI, DI, DS, ES, SS (C 要求保存),AX, BX, CX, DX 可自由使用。

核心實作邏輯

1. 取得段暫存器值

mov ax, cs   ; 程式碼段
mov bx, ds   ; 資料段
mov cx, es   ; 附加段
mov dx, ss   ; 堆疊段

直接讀取當前段暫存器。注意:此函式編譯為獨立 PROG 區段,連結時與 C 程式碼合併,故 CS 為程式碼段,DS 為 C 的資料段 (DGROUP)。

2. 經由堆疊參數指標寫回呼叫端緩衝區

uret:
mov bp, sp
mov si, [bp+4]    ; 取得 &cs 指標
mov [si], ax      ; 寫入 *cs = CS
mov si, [bp+6]
mov [si], bx      ; *ds = DS
mov si, [bp+8]
mov [si], cx      ; *es = ES
mov si, [bp+10]
mov [si], dx      ; *ss = SS
mov si, [bp+12]
mov [si], si      ; *si = SI (註: 此處 SI 已被覆寫,實際應先保存原 SI)
mov si, [bp+14]
mov [si], di      ; *di = DI
mov si, [bp+16]
mov [si], sp      ; *sp = SP
mov al, 0
lahf              ; AH = FLAGS 低位元組
mov si, [bp+18]
mov [si], ax      ; *flags = FLAGS (僅低 8 位? 實際 AH=完整 FLAGS 低位元組)
mov ax, 0         ; 回傳 0 (成功)
quit:
pop bp
ret

關鍵問題:寫入 *si 時,SI 已被用作指標讀取參數,原始 SI 值已丟失。正確做法應在開頭 PUSH SI 保存,或在寫入前從堆疊框架恢復。此為原始碼一個微妙 Bug。

3. FLAGS 取得技巧

mov al, 0
lahf          ; Load AH from Flags (將 FLAGS 低位元組載入 AH)
; AH 現在包含 SF, ZF, AF, PF, CF 等旗標

LAHF 將旗標暫存器低 8 位 (SF:ZF:0:AF:0:PF:1:CF) 載入 AH,配合 AL=0 形成 16 位元值存入 *flags。若需完整 16 位旗標,需 PUSHF / POP AX。

完整原始碼

pgroup group prog
prog segment byte public 'PROG'
assume cs:pgroup
public getseg
getseg proc near
push bp
mov bp,sp
mov si,[bp + 04]
mov ax,[si]
mov si,[bp + 06]
mov bx, [si]
mov si,[bp + 08]
mov cx, [si]
mov si,[bp + 10]
mov dx,[si]
;==============================================================================
mov ax, cs
mov bx, ds
mov cx, es
mov dx, ss
;==============================================================================
uret: mov bp,sp
mov si,[bp + 04]
mov [si],ax
mov si, [bp + 06]
mov [si], bx
mov si, [bp + 08]
mov [si], cx
mov si, [bp + 10]
mov [si], dx
mov si, [bp + 12]
mov [si], si
mov si, [bp + 14]
mov [si], di
mov si, [bp + 16]
mov [si], sp
mov al, 00
lahf
mov si, [bp + 18]
mov [si], ax
mov ax,0
quit: pop bp
ret
getseg endp
prog ends
end

教學價值與修正建議

  • 堆疊框架操作:PUSH BP / MOV BP,SP 建立框架,[BP+n] 存取參數,標準 C 近呼叫慣例。
  • 段暫存器即時值:展示 MOV AX, CS 等指令在組合語言中直接讀取段值 (C 中無法直接存取)。
  • 指標解參考寫入:參數為指標,需雙重間址 (MOV SI, [BP+n] / MOV [SI], REG) 寫回呼叫端變數。
  • 暫存器保存規範:函式修改了 SI, DI, AX, BX, CX, DX, BP,若遵循 C 慣例需保存 SI, DI, BP, DS, ES, SS。原碼僅保存 BP,SI, DI 被破壞 — 實際連結可能導致呼叫端錯誤。
  • 修正版骨架:
    getseg proc near
     push bp
     push si
     push di
     push ds
     push es
     mov bp, sp
     ... 邏輯 ...
     pop es
     pop ds
     pop di
     pop si
     pop bp
     ret
    getseg endp

應用場景

  • C 程式需呼叫 INT 21h 要求 ES:BX 遠端指標,可先 getseg(&es_val, ...) 取得 ES 段值。
  • 除錯器/監控工具顯示當前段暫存器狀態。
  • 動態建立遠端函式指標 (DWORD PTR = (seg<<16) | offset)。
  • 與 FREESPACE (ID 13300) 類似工具配合,獲取段值後計算記憶體佈局。

GETSEG 雖小,卻精準示範了「組合語言為高階語言提供底層硬體存取」的經典協作模式。理解其堆疊參數佈局、段暫存器語義、指標解參考寫入、暫存器保存責任,是掌握 16 位元混合語言開發的必修課。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved