我正在尝试了解 MS-DOS v2.0 source code ,特别是 MSDATA.ASM 中的一些代码。该代码最初是使用 35 多年历史的 MASM 汇编器(未商用的版本)进行汇编的。我感兴趣的代码就在开头附近:
SUBTTL Initialized data and data used at DOS initialization
PAGE
; DATA AREA for MS-DOS
IFNDEF KANJI
KANJI EQU 0 ;FALSE
ENDIF
CONSTANTS SEGMENT BYTE PUBLIC 'CONST'
EXTRN international_table:BYTE
EXTRN Current_Country:WORD
ORG 0
CONSTRT EQU $ ; Start of constants segment
PUBLIC DevStrLen
DEVSTRLEN DB 3 ; Size of below
PUBLIC DevString
DEVSTRING DB "DEV" ; Dummy device directory
;
; Table of routines for assignable devices
;
; MSDOS allows assignment if the following standard devices:
; stdin (usually CON input)
; stdout (usually CON output)
; auxin (usually AUX input)
; auxout (usually AUX output)
; stdlpt (usually PRN output)
;
; SPECIAL NOTE:
; Status of a file is a strange idea. We choose to handle it in this manner:
; If we're not at end-of-file, then we always say that we have a character.
; Otherwise, we return ^Z as the character and set the ZERO flag. In this
; manner we can support program written under the old DOS (they use ^Z as EOF
; on devices) and programs written under the new DOS (they use the ZERO flag
; as EOF).
; Default FCBs for boot up
sftabl LABEL DWORD ; file table
DW -1
DW -1
DW sf_default_number ; Number of entries in table
DB sf_default_number DUP ( (SIZE sf_entry) DUP (0))
I_AM NoSetDir,BYTE ; true -> do not set directory
I_am DidCTRLC,BYTE ; true -> we did a ^C exit
I_am SpaceFlag,BYTE ; true -> embedded spaces are allowed
; in FCB
; the next two variables relate to the position of the logical stdout/stdin
; cursor. They are only meaningful when stdin/stdout are assigned to the
; console.
i_am CARPOS,BYTE ; cursor position in stdin
i_am STARTPOS,BYTE ; position of cursor at beginning
; of buffered input call
I_AM PFLAG,BYTE
I_AM VERFLG,BYTE ; Initialize with verify off
I_AM CONTPOS,WORD
PUBLIC CHARCO
CHARCO DB 00000011B ; Allows statchks every 4 chars...
I_AM DMAADD,DWORD ; User's disk transfer address
; (disp/seg)
ORG $-CONSTRT-4
DW 80H
DW ?
ENDMEM DW ?
我试图特别理解这段代码:
I_AM DMAADD,DWORD ; User's disk transfer address
; (disp/seg)
ORG $-CONSTRT-4
DW 80H
DW ?
ENDMEM DW ?
它似乎定义了一个 DWORD 公共(public)变量 DMAADD
,然后将值 80H
分配给变量 DMAADD
第一个字,然后 ?
到第二个单词。我心里有一些疑问,也许最重要的问题是 - 为什么要这样做,而不是仅仅将 80H
的值分配给变量 DMAADD
到下一行。为什么在这里应用这个策略,其目的是什么?为什么是ORG $-CONSTRT-4
?
I_AM
宏的定义和描述如下:
;
; define a data item to be public and of an appropriate size/type
;
I_AM MACRO name,size
PUBLIC name
IFIDN <size>,<WORD>
name DW ?
ELSE
IFIDN <size>,<DWORD>
name DD ?
ELSE
IFIDN <size>,<BYTE>
name DB ?
ELSE
name DB size DUP (?)
ENDIF
ENDIF
ENDIF
ENDM
最佳答案
开发人员似乎有意使用 I_AM
宏使符号(指向字节、字和双字)可供其他模块公开访问。问题是I_AM
宏不允许您指定数据,它会将其保留为未初始化为 ?
。为了解决这个问题,开发人员决定支持程序计数器以覆盖未初始化的数据,以便他们可以用 80h 的 WORD 值和第二个未初始化的 WORD ( ?
) 填充它。
您不能使用 org
的表达式那是负面的。您无法通过以下方式备份程序计数器:
org -4
您需要一个绝对值。您需要知道程序计数器距离段开头有多远。他们选择通过设置 CONSTRT
来做到这一点。在顶部:
CONSTANTS SEGMENT BYTE PUBLIC 'CONST'
EXTRN international_table:BYTE
EXTRN Current_Country:WORD
ORG 0
CONSTRT EQU $ ; Start of constants segment
在本例中CONSTRT
被赋予值 0(段的开始)。 $
是相对于段开头的当前程序计数器。
要确定当前程序计数器 4 个字节之前的绝对值,您可以采用当前程序计数器 $
并从段开头的程序计数器中减去它(CONSTRT
设置为)。一旦您知道距路段起点有多远,您就减去 4。
然后我们得到的是:
I_AM DMAADD,DWORD ; User's disk transfer address
; (disp/seg)
它定义了一个可公开访问的标签,该标签被定义为指向未初始化的 DWORD 值。这会将程序计数器备份 4 以替换未初始化的 DWORD:
ORG $-CONSTRT-4
然后发出 WORD 值 80h,后跟未初始化的 WORD 值:
DW 80H
DW ?
您可以替换 I_AM
宏,指针的备份和数据的替换为:
public DMAADD
DMAADD dd 80h
可能 DOS 开发人员总是通过 I_AM
导出指向 BYTE、WORD、DWORD 数据的标签。宏作为 Microsoft 的编码要求。这完全是推测。他们可能认为通用宏可以让他们改变导出此类数据的方法,而无需在无数地方更改代码。
关于assembly - 使用 $ 符号为上一行中存在的变量赋值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56623552/