I'm trying to understand the MS-DOS v2.0 source code, and in particular some of the code in MSDATA.ASM. This code was originally assembled with a 35+ year old MASM assembler (a version that wasn't commercially available). The code I'm interested in is near the beginning:
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'm trying to understand this code in particular:
I_AM DMAADD,DWORD ; User's disk transfer address
; (disp/seg)
ORG $-CONSTRT-4
DW 80H
DW ?
ENDMEM DW ?
It appears to define a DWORD public variable DMAADD then it assigns the variable DMAADD the values 80H to the first word and then ? to the second word. I have some doubt in my mind and perhaps the most important question is - why is it doing it this way, instead of just assigning value of 80H to the variable DMAADD to the next line. Why is this strategy being applied here, and what is its purpose? WHy the ORG $-CONSTRT-4?
The I_AM macro is defined and described this way:
;
; 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
It appears that the developers were intent on using the I_AM macro to make symbols (that point at BYTEs, WORDs, and DWORDs) publicly accessible by other modules. The problem is that the I_AM macro doesn't allow you to specify the data, it leaves it uninitialised as ?. To get around that the developers decided to back the program counter up to overwrite the uninitialised data so they could fill it in with a WORD value of 80h and a second WORD that is uninitialised (?).
You aren't allowed to use an expressions with an org that is negative. You couldn't back the program counter up with:
org -4
You need an absolute value. You need to know how far the program counter is from the beginning of the segment. They chose to do that by setting up CONSTRT at the top with:
CONSTANTS SEGMENT BYTE PUBLIC 'CONST'
EXTRN international_table:BYTE
EXTRN Current_Country:WORD
ORG 0
CONSTRT EQU $ ; Start of constants segment
In this case CONSTRT is given the value 0 (the start of the segment). $ is the current program counter relative to the beginning of the segment.
To determine the absolute value of the current program counter 4 bytes earlier you can take the current program counter $ and subtract it from the program counter at the beginning of the segment (which CONSTRT is set to). Once you know how far you are from the beginning of the segment you subtract 4.
What we have then is:
I_AM DMAADD,DWORD ; User's disk transfer address
; (disp/seg)
That defines a publicly accessible label that is defined as pointing at an uninitialised DWORD value. This backs up the program counter by 4 to replace the uninitialised DWORD:
ORG $-CONSTRT-4
This then emits the WORD value 80h followed by an uninitialised WORD value:
DW 80H
DW ?
You could have replaced the I_AM macro, the backing up of the pointer and replacing of the data with:
public DMAADD
DMAADD dd 80h
It may be that the DOS developers always exported labels that point at BYTE, WORD, DWORD data via the I_AM macro as a coding requirement at Microsoft. This is entirely speculative. They may have felt that a common macro would allow them to change the method of exporting such data without changing code in countless places.
来源:https://stackoverflow.com/questions/56623552/assigning-value-to-the-variable-present-in-previous-line-using-sign