This file can be included to 16bit DOS programs written in EuroAssembler.
It declares macroinstructions for interaction with
Disk Operation System - DOS.
DOS services run in 16bit real CPU mode.
dosapi HEAD ; Start of interface block includable to other programs. INCLUDEHEAD1 "cpuext.htm", "status16.htm"
%Par4 %SET BP+24 %Par3 %SET BP+22 %Par2 %SET BP+20 %Par1 %SET BP+18 %ReturnAX %SET BP+14 %ReturnCX %SET BP+12 %ReturnDX %SET BP+10 %ReturnBX %SET BP+08 %ReturnSP %SET BP+06 %ReturnBP %SET BP+04 %ReturnSI %SET BP+02 %ReturnDI %SET BP+00
DosAPI %MACRO INT=21h, \ AL=,AH=,CL=,CH=,DL=,DH=,BL=,BH=,AX=,CX=,DX=,BX=,BP=,SP=,SI=,DI=,DS=,ES=, \ al=,ah=,cl=,ch=,dl=,dh=,bl=,bh=,ax=,cx=,dx=,bx=,bp=,sp=,si=,di=,ds=,es= %intNr %SET 0x21 ; Interrupt 0x21 will be used if not specified otherwise. %kwlength %SETA %=# ; %kwlength is now the number of keyword operands in DosAPI invocation. k %FOR 1..%kwlength ; Loop through all keyword operands in DosAPI invocation. %kw %SET %=*{%k} ; %kw is now something like AH=5 or INT=21h. %kwsize %SETS %kw ; Number of characters in keyword operand %kw. %key %SET ; Split the operand by = to %key and %value. Start with empty %key. %i %SETA 1 ; Start with the 1st character. %REPEAT %IF "%kw[%i]" === "=" %EXITREPEAT ; Equal-sign found, the split is done. %ELSE %key %SET %key%kw[%i] ; Append the %i-th character and continue. %i %SETA %i + 1 %ENDIF %UNTIL %i > %kwsize %value %SET %kw[%i+1..%&] %IF "%key" == "INT" %intNr %SET %value ; Save the interrupt number for later to the variable %intNr. %ELSE %IF "%key"==="" || "%value"==="" %ERROR ID=5910,'Bad parameter "%key=%value", ignored.' %ELSE %IF "%key" !== "%value" MOV %key,%value ; Emit the instruction which loads the register %key. %ENDIF %ENDIF %ENDIF %ENDFOR k ; Process the next keyword operand. INT %intNr ; Finally emit the DOS service call. %ENDMACRO DosAPI
Macro GetArg retrieves ArgNumber-th parameter provided on command line.
Parameters on the command line may be separated with unquoted
white spaces or unquoted commas. Single aphostrophe cannot be used as quote.
The returned argument is not zero terminated. Quotes surrounding the argument are returned, too, but separating white spaces and commas are stripped off.
Runtime procedure GetArg@RT is also used in macro GetArgCount , its functionality is controlled by the ArgNumber parameter.
Dump of PSP:80h when the DOS program launched with parameters
/1 "2"
:
07 20 2F 31 20 22 32 22 0D / 1 " 2 " CR
Dump of PSP:80h when the DOS program launched without parameters:
00 0D CR
ArgNumber=-1 is a special case when this macro is used by GetArgCount and it returns number of arguments in AX instead of argument string in ES:SI,CX.ES= is paragraph address of PSP , as it was set when DOS program starts.
GetArg %MACRO ArgNumber PUSHW %ArgNumber CALL GetArgDos16@RT GetArgDos16@RT:: PROC1 PUSHAW MOV BP,SP SUB AX,AX ; AL will be the parsed character. SUB CX,CX ; CX is argument counter. MOV [%ReturnCX],CX MOV SI,0x80 ; Offset of PSP.CmdArgSize byte. CLD LODSB [ES:SI] ; ES is paragraph address of PSP. MOV DX,SI MOV BX,SI ADD DX,AX ; ES:DX now points to the CR which terminates command-line parameters. .10:MOV DI,SI CMP CX,[%Par1] ; %Par1 is the number of requested argument. JAE .50: .20:CMP SI,DX ; End of command-line reached? JNB .50: ; If no more arguments are available. LODSB [ES:SI] CMP AL,' ' JBE .20: ; Ignore the separator (space). DEC SI INC CX MOV BX,SI ; BX points to the brutto begining of CX-th argument. MOV DI,SI ; DI points to the brutto end of current argument. .30:CMP SI,DX JNB .10: LODSB [ES:SI] CMP AL,'"' JNE .40: .35:CMP SI,DX CMC JC .90: ; CF signalizes syntax error - unpaired quotes. LODSB [ES:SI] CMP AL,'"' JNE .35: ; Inside the quoted string. JMP .30: .40:CMP AL,',' JE .10: CMP AL,' ' JA .30: .45:CMP SI,DX JNB .10: LODSB [ES:SI] CMP AL,' ' JNA .45: CMP AL,',' JE .10: DEC SI JMP .10: ; Parse the next argument. .50:CMPW [%Par1],-1 ; Test if invoked from GetArgCount with ArgNr= -1. JNE .60: MOV [%ReturnCX],CX JMP .80: .60:CMP CX,[%Par1] STC JNE .90: MOV SI,BX ; Brutto CX-th argument found at SI..DI. Trim spaces and commas. .65:CMP SI,DI JNB .75: LODSB [ES:SI] CMP AL,' ' JBE .65: CMP AL,',' JE .65: DEC SI .70:DEC DI MOV AL,[ES:DI] CMP AL,' ' JBE .70: CMP AL,',' JE .70: INC DI ; Netto argument is now at SI..DI. .75 MOV [%ReturnSI],SI SUB DI,SI JC .80: ; Return CX=0 when the netto size is not positive. MOV [%ReturnCX],DI .80:CLC .90:POPAW RET 2 ENDPROC1 GetArgDos16@RT:: %ENDMACRO GetArg
GetArgCount %MACRO GetArg -1 %ENDMACRO GetArgCount
Macro GetEnv retrieves value of environment-variable with specified name
EnvName at run-time. The zero-terminated value is returned in ES:SI,CX.
Requested name should be in upper case and either zero-terminated,
or its EnvNameSize= explicitly specified.
Environment variables in DOS are stored in segment identified by current process' PSP at PSP.EnvSeg in the formatEnvName=EnvValue
where each definition is zero-terminated and one more zero (NULL character) terminates the whole array of ASCIIZ environment strings.
Remember that most DOS versions convert all environment names to uppercase.
In TINY COM programs are all segment registers permanently set to PSP. In DOS programs are DS and ES set to PSP when the executable MZ program starts, this value should be provided to the macro GetEnv in register ES.
GetEnv %MACRO EnvNamePtr, Size= -1
PUSHW %Size, %EnvNamePtr
CALL GetEnvDos16@RT::
GetEnvDos16@RT:: PROC1
PUSHAW
MOV BP,SP
MOV ES,[ES:0x002C] ; PSP.EnvSeg.
MOV SI,[%Par1] ; EnvNamePtr.
MOV DX,[%Par2] ; EnvNameSize.
MOV BX,SI
SUB DI,DI
SUB AX,AX ; AL=NULL.
CLD
JMPS .20:
.10: MOV CX,-1 ; Try the next string.
REPNE SCASB [ES:DI] ; Search for the terminating NULL.
CMP AL,[ES:DI] ; Second NULL terminates the whole set of environment strings.
MOV CX,AX ; Zero in case that EnvName was not found.
MOV [%ReturnSI],AX ; Zero in case that EnvName was not found.
STC
JZ .90: ; End of the set of strings, CX=SI=0, CF=1, nothing found.
.20: MOV SI,BX ; EnvNamePtr.
MOV CX,DX ; EnvName Size. DS:SI is EnvName, max CX bytes or terminated with NULL.
REPE CMPSB ; ES:DI is name=value
, perhaps the desired one.
JCXZ .30: ; If EnvNameSize was specified, omit the check on terminating NULL.
DEC DI
CMP [DS:SI-1],AL
.30: JNE .10:
CMPB [ES:DI],'='
JNE .10:
INC DI
MOV [%ReturnSI],DI ; EnvName was found, ES:DI is pointer to its value.
MOV CX,-1
REPNE SCASB [ES:DI] ; Find the size of ASCIIZ value ES:DI.
NOT CX
DEC CX
.90: MOV [%ReturnCX],CX
POPAW
RET 2*2
ENDP1 GetEnvDos16@RT::
%ENDMACRO GetEnv
Macro StdOutput writes one or more concatenated strings to the standard output or to other equipment identified by the DOS device Handle number.
Strings are either zero-terminated, or the keyword
Size=
must specify their size in bytes. The terminating NULL character is never written.
If keyword Eol=Yes
, macro writes CR+LF after all strings.
StdOutput %MACRO String1,String2,,,Size=-1, Handle=1, Eol=No OpNr %FOR 1..%#, STEP=1 PUSHW %Handle, %Size, %1 CALL StdOutputDos16@RT: %SHIFT 1 %ENDFOR OpNr %IF %Eol PUSHW %Handle, 2, -1 CALL StdOutputDos16@RT: %ENDIF StdOutputDos16@RT:: PROC1 ; StdCalled with %Par1=StringPtr,%Par2=Size,%Par3=Handle. PUSHAW MOV BP,SP PUSH ES,DS POP ES MOV DI,[%Par1] ; Pointer to String. MOV CX,[%Par2] ; String maximal size. MOV BX,[%Par3] ; Standard handle. JCXZ .90: CMP DI,-1 ; This sginalizes Eol request. JE .Eol: MOV DX,DI SUB AX,AX CLD REPNE SCASB JNE .50: DEC DI .50: SUB DI,DX DosAPI AH=0x40,BX=BX,CX=DI,DX=DX ; WRITE TO FILE OR DEVICE. .80: JC .90: CMP AX,CX ; Set CF in not all characters have been written. .90:POP ES POPAW RET 3*2 .Eol:PUSH DS PUSH SS POP DS PUSHW 0x0A0D ; Temporary store CR+LF on stack. DosAPI AH=0x40,BX=BX,CX=CX,DX=SP ; WRITE TO FILE OR DEVICE. INC SP,SP ; Discard CR+LF, preserve CF. POP DS JMPS .80: ENDPROC1 StdOutputDos16@RT:: %ENDMACRO StdOutput
Macro StdInput reads a line terminated with CR (ASCII 13) from standard input or from other equipment identified by the DOS device Handle number.
StdInput %MACRO Buffer, Size=, Handle=0 %IF "%Size" === "" PUSHW %Handle, SIZE# %Buffer, %Buffer %ELSE PUSHW %Handle, %Size, %Buffer %ENDIF CALL StdInputDos16@RT StdInputDos16@RT:: PROC1 ; StdCalled with %Par1=Buffer,%Par2=Size,%Par3=Handle. PUSHAW MOV BP,SP DosAPI AH=0x3F,BX=[%Par3],DX=[%Par1],CX=[%Par2] ; READ FROM FILE OR DEVICE. MOV [%ReturnCX],AX POPAW RET 3*2 ENDP1 StdInputDos16@RT:: %ENDMACRO StdInput
RETN
if registers CS, SS, SP have the same value which they had at the program entry.Errorlevel=
, this value may also be specified as an ordinal operand.
DosAPI AH=0x4C,AL=Errorlevel
.TerminateProgram %MACRO Errorlevel=0 %IF %# = 1 ; If ordinal provided. %IF "%1" !== "AL" MOV AL,%1 %ENDIF %ELSE ; Errorlevel is specified as keyword. %IF "%Errorlevel" !== "AL" MOV AL,%Errorlevel %ENDIF %ENDIF DosAPI AH=0x4C,AL=AL ; TERMINATE WITH RETURN CODE. %ENDMACRO TerminateProgram
Errorlevel=
, this value may also be specified as an ordinal operand.
DosAPI AH=0x31,AL=Errorlevel,DX=ResidentParagraphs
.TerminateStayResident %MACRO ResidentParagraphs, Errorlevel=0 %IF "%1" !== "DX" MOV DX,%1 %ENDIF %IF %# = 2 ; If two ordinals provided. MOV AL,%2 %ELSE MOV AL,%Errorlevel %ENDIF DosAPI AH=0x31,AL=AL,DX=DX ; TERMINATE AND STAY RESIDENT. %ENDMACRO TerminateStayResident
ENDHEAD dosapi ; End of interface block.