我有一些冗长的代码,但是一行中的问题非常小,明确地称为“此行中的问题”。为什么我们要两次(而不是一次)添加SI和AX。代码,您将可以非常准确地找到它。
DEVSTART LABEL WORD
CONSOLE_DEV: ;Header for device CON
DW AUXDEV,BIOSSEG ;Link to next device
DW 8003H ;Attributes - console input,
;output device
DW STRATEGY ;Srategy entry point
DW CON_INT ;Interrupt entry point
DB "CON " ;Device name
;-------------------------------------------------
CONSOLE_TABLE: DW EXIT ;0 - Init. (Not used)
DW EXIT ;1 - Media check (Not used)
DW EXIT ;2 - Get Bios Parameter Block
;(Not used)
DW CMDERR ;3 - Reserved. (Currently
;returns error)
DW CON_READ ;4 - Character read.
;(Destructive)
DW CON_RDND ;5 - Character read. (Non-
;destructive)
DW EXIT ;6 - Return status. (Not used)
DW CON_FLSH ;7 - Flush Input buffer.
DW CON_WRIT ;8 - Character write.
DW CON_WRIT ;9 - Character write with
;Verify.
DW CON_WRST ;10 - Character write status.
DW EXIT ;11 - Flush output buffer. (Not
;used.)
DW EXIT ;12 - IO Control.
;-----------------------------------------------
PAGE
SUBTTL Strategy and Software Interrupt routines.
;Define offsets for io data packet
IODAT STRUC
CMDLEN DB ? ;LENGTH OF THIS COMMAND
UNIT DB ? ;SUB UNIT SPECIFIER
CMD DB ? ;COMMAND CODE
STATUS DW ? ;STATUS
DB 8 DUP (?)
MEDIA DB ? ;MEDIA DESCRIPTOR
TRANS DD ? ;TRANSFER ADDRESS
COUNT DW ? ;COUNT OF BLOCKS OR
CHARACTERS
START DW ? ;FIRST BLOCK TO TRANSFER
IODAT ENDS
PTRSAV DD 0 ;Strategy pointer save.
;
; Simplistic Strategy routine for non-multi-Tasking system.
;
; Currently just saves I/O packet pointers in PTRSAV for
; later processing by the individual interrupt routines.
;
STRATP PROC FAR
STRATEGY:
MOV WORD PTR CS:[PTRSAV],BX
MOV WORD PTR CS:[PTRSAV+2],ES
RET
STRATP ENDP
;
; Console interrupt routine for processing I/O packets.
;
CONSOLE_INT:
PUSH SI
MOV SI,OFFSET CONSOLE_TABLE
JMP SHORT ENTRY
;-------------------------------------------
;
; Common program for handling the simplistic I/O packet
; processing scheme in MSDOS 2.0
;
ENTRY: PUSH AX ;Save all nessacary
;registers.
PUSH CX
PUSH DX
PUSH DI
PUSH BP
PUSH DS
PUSH ES
PUSH BX
LDS BX,CS:[PTRSAV] ;Retrieve pointer to I/O Packet.
MOV AL,[BX.UNIT] ;AL = Unit code.
MOV AH,[BX.MEDIA] ;AH = Media descriptor.
MOV CX,[BX.COUNT] ;CX = Contains byte/sector
;count.
MOV DX,[BX.START] ;DX = Starting Logical sector.
XCHG DI,AX ;Move Unit & Media into DI
;temporarily.
MOV AL,[BX.CMD] ;Retrieve Command type. (1 =>
;11)
XOR AH,AH ;Clear upper half of AX for
;calculation.
ADD SI,AX ;"PROBLEM IN THIS LINE"
;(Compute entry pointer in dispatch table).
ADD SI,AX ;"PROBLEM IN THIS LINE".
CMP AL,11 ;Verify that not more than 11
;commands.
JA CMDERR ;Ah, well, error out.
XCHG AX,DI ;Move Unit & Media back where
;they belong.
LES DI,[BX.TRANS] ;DI contains addess of Transfer
;address.
;ES contains segment.
PUSH CS
POP DS ;Data segment same as Code
;segment.
JMP [SI] ;Perform I/O packet command.
CODE CONTINUES..............
请帮助,如果您觉得很奇怪,我深表遗憾。但我确信我在这篇文章中添加了足够的信息。请参阅提到的“此行中的问题”行。如果您需要更多详细信息,请告诉我。
答案 0 :(得分:3)
CONSOLE_TABLE 用 word 大小的近指针填充。如果您想使用例如 CON_READ 是第5项,并且具有 CommandType 或 FunctionNumber 4,您必须在此表中使用8的偏移量。
这恰好是功能编号的两倍!
因此两次添加功能编号将产生正确的地址。
MOV AL,[BX.CMD] ;Retrieve Command type. (1 => 11)
XOR AH,AH ;Clear upper half of AX for calculation.
非常重要!此时,SI
已经保存了 CONSOLE_TABLE 的地址。
ADD SI,AX ; Add function number once
ADD SI,AX ; Add function number twice
加倍功能编号的替代解决方案也很简单。
MOV AL,[BX.CMD] ;Retrieve Command type. (1 => 11)
XOR AH,AH ;Clear upper half of AX for calculation.
非常重要!此时,SI
已经保存了 CONSOLE_TABLE 的地址。
SHL AX,1 ; Doubling the function number gives offset
ADD SI,AX ; Add full offset
该程序中最容易引起误解的元素是名称过于通用的标签 ENTRY 。真正的入口点当然是 CONSOLE_INT ,其中保留SI
,然后设置为指向 CONSOLE_TABLE 的开始。
原始软件程序员可能在软件中有更多这些PUSH SI
MOV SI, ...
JMP ENTRY
片段。
CONSOLE_TABLE:
+0 == 0*2 EXIT ;0 - Init. (Not used)
+2 == 1*2 EXIT ;1 - Media check (Not used)
+4 == 2*2 EXIT ;2 - Get Bios Parameter Block (Not used)
+6 == 3*2 CMDERR ;3 - Reserved. (Currently returns error)
+8 == 4*2 CON_READ ;4 - Character read. (Destructive)
+10 == 5*2 CON_RDND ;5 - Character read. (Non-destructive)
+12 == 6*2 EXIT ;6 - Return status. (Not used)
+14 == 7*2 CON_FLSH ;7 - Flush Input buffer.
+16 == 8*2 CON_WRIT ;8 - Character write.
+18 == 9*2 CON_WRIT ;9 - Character write with Verify.
+20 == 10*2 CON_WRST ;10 - Character write status.
+22 == 11*2 EXIT ;11 - Flush output buffer. (Not used.)
+24 == 12*2 EXIT ;12 - IO Control.
答案 1 :(得分:2)
我只想知道为什么我们要向SI添加AX。
相对于其他计算同一事物的方式?在不清楚Jester的评论后,您仍然想知道什么。如果这对您没有意义,也许您需要自己开始编写一些代码和/或查看您了解的C的编译器输出,以适应x86和asm,以便能够阅读其他人的代码。
稍后有JMP [SI]
会分派给该命令的处理程序代码,因此他们正在SI中计算地址。 (它正在索引一个单词表,因此加两次将单词索引转换为字节偏移。x86-16没有缩放索引寻址模式。)
公共ENTRY
之前的不同包装器存根将SI
设置为不同表的基础。显然,您从代码中删除了除CONSOLE_INT:
以外的所有代码(这就是为什么这里似乎有无用的jmp short ENTRY
会在此处汇编为jmp +0
的原因),但是您留下的那个有{{1} }。
如果不需要支持多个表,则可以只计算SI或DI中的字节偏移量,并使用MOV SI,OFFSET CONSOLE_TABLE
。但是在没有MOVZX的386之前的版本中,将字节零扩展到SI或DI是不方便的,因为没有上半部/下半部可单独访问的寄存器。
如果他们愿意销毁BX,则可以执行以下操作来保存指令。但是大概他们需要保留指向该结构的指针,以便以后访问其他成员。
jmp [CONSOLE_TABLE + DI]
如果他们想在添加到SI之前在{em> 之前将 MOV bl, [BX.CMD]
xor bh, bh
add bx, bx ; word index -> byte offset
...
jmp [bx+si] ; BX can be a base register, unlike AX
加倍,则可以在计算最终指针之前完成add ax,ax
,或者只是在将AX加倍后完成CMP AL,11
。